# Copyright(C) 2010-2013 Bezleputh
#
# This file is part of woob.
#
# woob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# woob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with woob. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime, time
from woob.tools.date import parse_date
from .address import PostalAddress, compat_field
from .base import BaseObject, Enum, EnumField, Field, FloatField, IntField, StringField
from .collection import CapCollection, Collection, CollectionNotFound
from .date import DateField
__all__ = ["BaseCalendarEvent", "CapCalendarEvent"]
class CATEGORIES(Enum):
CONCERT = "Concert"
CINE = "Cinema"
THEATRE = "Theatre"
TELE = "Television"
CONF = "Conference"
AUTRE = "Autre"
EXPO = "Exposition"
SPECTACLE = "Spectacle"
FEST = "Festival"
SPORT = "Sport"
# the following elements deal with ICalendar standards
# see https://fr.wikipedia.org/wiki/ICalendar#%C3%89v%C3%A9nements_(VEVENT)
class TRANSP(Enum):
OPAQUE = "OPAQUE"
TRANSPARENT = "TRANSPARENT"
class STATUS(Enum):
TENTATIVE = "TENTATIVE"
CONFIRMED = "CONFIRMED"
CANCELLED = "CANCELLED"
class TICKET(Enum):
AVAILABLE = "Available"
NOTAVAILABLE = "Not available"
CLOSED = "Closed"
[docs]class BaseCalendarEvent(BaseObject):
"""
Represents a calendar event
"""
start_date = DateField("Start date of the event")
end_date = DateField("End date of the event")
timezone = StringField("Timezone of the event in order to convert to utc time", default="Etc/UCT")
summary = StringField("Title of the event")
address = Field("Address where event will take place", PostalAddress)
category = EnumField("Category of the event", CATEGORIES)
description = StringField("Description of the event")
price = FloatField("Price of the event")
booked_entries = IntField("Entry number")
max_entries = IntField("Max entry number")
event_planner = StringField("Name of the event planner")
city = compat_field("address", "city")
location = compat_field("address", "street")
# the following elements deal with ICalendar standards
# see https://fr.wikipedia.org/wiki/ICalendar#%C3%89v%C3%A9nements_(VEVENT)
sequence = IntField("Number of updates, the first is number 1")
# (TENTATIVE, CONFIRMED, CANCELLED)
status = EnumField("Status of the event", STATUS)
# (OPAQUE, TRANSPARENT)
transp = EnumField("Describes if event is available", TRANSP)
# (AVAILABLE, NOTAVAILABLE, CLOSED)
ticket = EnumField("Describes if tickets are available", TICKET)
[docs] @classmethod
def id2url(cls, _id):
"""Overloaded in child classes provided by backends."""
raise NotImplementedError()
@property
def page_url(self):
"""
Get page URL of the announce.
"""
return self.id2url(self.id)
class Query(BaseObject):
"""
Query to find events
"""
start_date = DateField("Start date of the event")
end_date = DateField("End date of the event")
city = StringField("Name of the city in witch event will take place")
categories = Field("List of categories of the event", list, tuple, default=list(CATEGORIES))
ticket = Field("List of status of the tickets sale", list, tuple, default=list(TICKET))
summary = StringField("Title of the event")
[docs]class CapCalendarEvent(CapCollection):
"""
Capability of calendar event type sites
"""
ASSOCIATED_CATEGORIES = "ALL"
[docs] def has_matching_categories(self, query):
if self.ASSOCIATED_CATEGORIES == "ALL":
return True
for category in query.categories:
if category in self.ASSOCIATED_CATEGORIES:
return True
return False
[docs] def search_events(self, query):
"""
Search event
:param query: search query
:type query: :class:`Query`
:rtype: iter[:class:`BaseCalendarEvent`]
"""
raise NotImplementedError()
[docs] def list_events(self, date_from, date_to=None):
"""
list coming event.
:param date_from: date of beguinning of the events list
:type date_from: date
:param date_to: date of ending of the events list
:type date_to: date
:rtype: iter[:class:`BaseCalendarEvent`]
"""
raise NotImplementedError()
[docs] def get_event(self, _id):
"""
Get an event from an ID.
:param _id: id of the event
:type _id: str
:rtype: :class:`BaseCalendarEvent` or None is fot found.
"""
raise NotImplementedError()
[docs] def attends_event(self, event, is_attending=True):
"""
Attends or not to an event
:param event : the event
:type event : BaseCalendarEvent
:param is_attending : is attending to the event or not
:type is_attending : bool
"""
raise NotImplementedError()
[docs] def iter_resources(self, objs, split_path):
"""
Iter events by category
"""
if len(split_path) == 0 and self.ASSOCIATED_CATEGORIES != "ALL":
for category in self.ASSOCIATED_CATEGORIES:
collection = Collection([category], category)
yield collection
elif len(split_path) == 1 and split_path[0] in self.ASSOCIATED_CATEGORIES:
query = Query()
query.categories = split_path
query.start_date = datetime.combine(parse_date("today"), time.min)
query.end_date = parse_date("")
query.city = ""
yield from self.search_events(query)
[docs] def validate_collection(self, objs, collection):
"""
Validate Collection
"""
if collection.path_level == 0:
return
if collection.path_level == 1 and collection.split_path[0] in CATEGORIES.values:
return
raise CollectionNotFound(collection.split_path)