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