1# -*- coding: utf-8 -*- 2 3# Copyright(C) 2010-2013 Bezleputh 4# 5# This file is part of weboob. 6# 7# weboob is free software: you can redistribute it and/or modify 8# it under the terms of the GNU Lesser General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# weboob is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU Lesser General Public License for more details. 16# 17# You should have received a copy of the GNU Lesser General Public License 18# along with weboob. If not, see <http://www.gnu.org/licenses/>. 19 20from .base import ( 21 BaseObject, StringField, IntField, FloatField, Field, EnumField, 22 Enum, 23) 24from .collection import CapCollection, CollectionNotFound, Collection 25from .date import DateField 26from .address import compat_field, PostalAddress 27 28from datetime import time, datetime 29from weboob.tools.date import parse_date 30 31__all__ = ['BaseCalendarEvent', 'CapCalendarEvent'] 32 33 34class CATEGORIES(Enum): 35 CONCERT = u'Concert' 36 CINE = u'Cinema' 37 THEATRE = u'Theatre' 38 TELE = u'Television' 39 CONF = u'Conference' 40 AUTRE = u'Autre' 41 EXPO = u'Exposition' 42 SPECTACLE = u'Spectacle' 43 FEST = u'Festival' 44 SPORT = u'Sport' 45 46 47#the following elements deal with ICalendar stantdards 48#see http://fr.wikipedia.org/wiki/ICalendar#Ev.C3.A9nements_.28VEVENT.29 49class TRANSP(Enum): 50 OPAQUE = u'OPAQUE' 51 TRANSPARENT = u'TRANSPARENT' 52 53 54class STATUS(Enum): 55 TENTATIVE = u'TENTATIVE' 56 CONFIRMED = u'CONFIRMED' 57 CANCELLED = u'CANCELLED' 58 59 60class TICKET(Enum): 61 AVAILABLE = u'Available' 62 NOTAVAILABLE = u'Not available' 63 CLOSED = u'Closed' 64 65 66class BaseCalendarEvent(BaseObject): 67 """ 68 Represents a calendar event 69 """ 70 71 start_date = DateField('Start date of the event') 72 end_date = DateField('End date of the event') 73 timezone = StringField('Timezone of the event in order to convert to utc time', default='Etc/UCT') 74 summary = StringField('Title of the event') 75 address = Field('Address where event will take place', PostalAddress) 76 category = EnumField('Category of the event', CATEGORIES) 77 description = StringField('Description of the event') 78 price = FloatField('Price of the event') 79 booked_entries = IntField('Entry number') 80 max_entries = IntField('Max entry number') 81 event_planner = StringField('Name of the event planner') 82 83 city = compat_field('address', 'city') 84 location = compat_field('address', 'street') 85 86 #the following elements deal with ICalendar stantdards 87 #see http://fr.wikipedia.org/wiki/ICalendar#Ev.C3.A9nements_.28VEVENT.29 88 sequence = IntField('Number of updates, the first is number 1') 89 90 # (TENTATIVE, CONFIRMED, CANCELLED) 91 status = EnumField('Status of the event', STATUS) 92 # (OPAQUE, TRANSPARENT) 93 transp = EnumField('Describes if event is available', TRANSP) 94 # (AVAILABLE, NOTAVAILABLE, CLOSED) 95 ticket = EnumField('Describes if tickets are available', TICKET) 96 97 @classmethod 98 def id2url(cls, _id): 99 """Overloaded in child classes provided by backends.""" 100 raise NotImplementedError() 101 102 @property 103 def page_url(self): 104 """ 105 Get page URL of the announce. 106 """ 107 return self.id2url(self.id) 108 109 110class Query(BaseObject): 111 """ 112 Query to find events 113 """ 114 115 start_date = DateField('Start date of the event') 116 end_date = DateField('End date of the event') 117 city = StringField('Name of the city in witch event will take place') 118 categories = Field('List of categories of the event', list, tuple, default=list(CATEGORIES)) 119 ticket = Field('List of status of the tickets sale', list, tuple, default=list(TICKET)) 120 summary = StringField('Title of the event') 121 122 123class CapCalendarEvent(CapCollection): 124 """ 125 Capability of calendar event type sites 126 """ 127 128 ASSOCIATED_CATEGORIES = 'ALL' 129 130 def has_matching_categories(self, query): 131 if self.ASSOCIATED_CATEGORIES == 'ALL': 132 return True 133 134 for category in query.categories: 135 if category in self.ASSOCIATED_CATEGORIES: 136 return True 137 return False 138 139 def search_events(self, query): 140 """ 141 Search event 142 143 :param query: search query 144 :type query: :class:`Query` 145 :rtype: iter[:class:`BaseCalendarEvent`] 146 """ 147 raise NotImplementedError() 148 149 def list_events(self, date_from, date_to=None): 150 """ 151 list coming event. 152 153 :param date_from: date of beguinning of the events list 154 :type date_from: date 155 :param date_to: date of ending of the events list 156 :type date_to: date 157 :rtype: iter[:class:`BaseCalendarEvent`] 158 """ 159 raise NotImplementedError() 160 161 def get_event(self, _id): 162 """ 163 Get an event from an ID. 164 165 :param _id: id of the event 166 :type _id: str 167 :rtype: :class:`BaseCalendarEvent` or None is fot found. 168 """ 169 raise NotImplementedError() 170 171 def attends_event(self, event, is_attending=True): 172 """ 173 Attends or not to an event 174 :param event : the event 175 :type event : BaseCalendarEvent 176 :param is_attending : is attending to the event or not 177 :type is_attending : bool 178 """ 179 raise NotImplementedError() 180 181 def iter_resources(self, objs, split_path): 182 """ 183 Iter events by category 184 """ 185 if len(split_path) == 0 and self.ASSOCIATED_CATEGORIES != 'ALL': 186 for category in self.ASSOCIATED_CATEGORIES: 187 collection = Collection([category], category) 188 yield collection 189 190 elif len(split_path) == 1 and split_path[0] in self.ASSOCIATED_CATEGORIES: 191 query = Query() 192 query.categories = split_path 193 query.start_date = datetime.combine(parse_date('today'), time.min) 194 query.end_date = parse_date('') 195 query.city = u'' 196 for event in self.search_events(query): 197 yield event 198 199 def validate_collection(self, objs, collection): 200 """ 201 Validate Collection 202 """ 203 if collection.path_level == 0: 204 return 205 if collection.path_level == 1 and collection.split_path[0] in CATEGORIES.values: 206 return 207 raise CollectionNotFound(collection.split_path) 208