Project

General

Profile

Download (7.63 KB) Statistics
| Branch: | Tag: | Revision:

root / extra / modules / events.py @ e3bbea04

1
import time
2
import datetime
3
import urllib2
4
import vobject
5

    
6
from quixote import get_request, get_publisher, get_response
7
from quixote.html import htmltext, TemplateIO
8

    
9
from qommon.publisher import get_publisher_class
10
from qommon.storage import StorableObject
11
from qommon.cron import CronJob
12
from qommon import misc
13

    
14
class Event(StorableObject):
15
    _names = 'events'
16

    
17
    title = None
18
    description = None
19
    url = None
20
    date_start = None
21
    date_end = None
22
    location = None
23
    organizer = None
24
    more_infos = None
25
    keywords = None
26

    
27
    def in_month(self, year, month):
28
        if not self.date_end: # a single date
29
            return tuple(self.date_start[:2]) == (year, month)
30
        else:
31
            # an interval
32
            if tuple(self.date_start[:2]) > (year, month): # start later
33
                return False
34
            if tuple(self.date_end[:2]) < (year, month): # ended before
35
                return False
36
        return True
37

    
38
    def format_date(self):
39
        d = {
40
            'year_start': self.date_start[0],
41
            'month_start': misc.get_month_name(self.date_start[1]),
42
            'day_start': self.date_start[2]
43
            }
44
        if self.date_end and self.date_start[:3] != self.date_end[:3]:
45
            d.update({
46
                'year_end': self.date_end[0],
47
                'month_end': misc.get_month_name(self.date_end[1]),
48
                'day_end': self.date_end[2]
49
                })
50
            d2 = datetime.date(*self.date_start[:3]) + datetime.timedelta(days=1)
51
            if tuple(self.date_end[:3]) == (d2.year, d2.month, d2.day):
52
                # two consecutive days
53
                if self.date_start[1] == self.date_end[1]:
54
                    return _('On %(month_start)s %(day_start)s and %(day_end)s') % d
55
                else:
56
                    return _('On %(month_start)s %(day_start)s and %(month_end)s %(day_end)s') % d
57
            else:
58
                if self.date_start[0] == self.date_end[0]: # same year
59
                    if self.date_start[1] == self.date_end[1]: # same month
60
                        return _('From %(month_start)s %(day_start)s to %(day_end)s') % d
61
                    else:
62
                        return _('From %(month_start)s %(day_start)s '
63
                                 'to %(month_end)s %(day_end)s') % d
64
                else:
65
                    return _('From %(month_start)s %(day_start)s %(year_start)s '
66
                             'to %(month_end)s %(day_end)s %(year_end)s') % d
67
        else:
68
            return _('On %(month_start)s %(day_start)s') % d
69

    
70
    def as_vevent(self):
71
        vevent = vobject.newFromBehavior('vevent')
72
        vevent.add('uid').value = '%04d%02d%02d-%s@%s' % (self.date_start[:3] + (self.id,
73
            get_request().get_server().lower().split(':')[0].rstrip('.')))
74
        vevent.add('summary').value = unicode(self.title, 'iso-8859-15')
75
        vevent.add('dtstart').value = datetime.date(*self.date_start[:3])
76
        vevent.dtstart.value_param = 'DATE'
77
        if self.date_end:
78
            vevent.add('dtend').value = datetime.date(*self.date_end[:3])
79
            vevent.dtend.value_param = 'DATE'
80
        if self.description:
81
            vevent.add('description').value = unicode(self.description.strip(), 'iso-8859-15')
82
        if self.url:
83
            vevent.add('url').value = unicode(self.url, 'iso-8859-15')
84
        if self.location:
85
            vevent.add('location').value = unicode(self.location, 'iso-8859-15')
86
        if self.organizer:
87
            vevent.add('organizer').value = unicode(self.organizer, 'iso-8859-15')
88
        if self.keywords:
89
            vevent.add('categories').value = unicode(','.join(self.keywords), 'iso-8859-15')
90
        vevent.add('class').value = 'PUBLIC'
91
        return vevent
92

    
93
    def as_vcalendar(cls):
94
        cal = vobject.iCalendar()
95
        cal.add('prodid').value = '-//Entr\'ouvert//NON SGML Au Quotidien'
96
        for x in cls.select():
97
            cal.add(x.as_vevent())
98
        return cal.serialize()
99
    as_vcalendar = classmethod(as_vcalendar)
100
    
101
    def as_html_dt_dd(self):
102
        root_url = get_publisher().get_root_url()
103
        r = TemplateIO(html=True)
104
        r += htmltext('<dt>')
105
        r += self.format_date()
106
        r += htmltext('</dt>')
107
        r += htmltext('<p><dd><strong>%s</strong>') % self.title
108
        if self.description:
109
            r += ' - ' + self.description
110
        r += htmltext('</p>')
111
        if (self.location or self.organizer or self.more_infos or self.keywords):
112
            r += htmltext('<ul>')
113
            if self.location:
114
                r += htmltext('<li>%s: %s</li>') % (_('Location'), self.location)
115
            if self.organizer:
116
                r += htmltext('<li>%s: %s</li>') % (_('Organizer'), self.organizer)
117
            if self.more_infos:
118
                r += htmltext('<li>%s</li>') % self.more_infos
119
            if self.keywords:
120
                r += htmltext('<li>')
121
                for k in self.keywords:
122
                    r += htmltext('<a class="tag" href="%sagenda/tag/%s">%s</a> ') % (root_url, k, k)
123
                r += htmltext('</li>')
124
            r += htmltext('</ul>')
125

    
126
        if self.url:
127
            if get_response().iframe_mode:
128
                r += htmltext('<a class="external" href="%s" target="_top">%s</a>') % (
129
                        self.url, _('More information'))
130
            else:
131
                r += htmltext('<a class="external" href="%s">%s</a>') % (
132
                        self.url, _('More information'))
133
        r += htmltext('</dd>')
134
        return r.getvalue()
135

    
136

    
137

    
138

    
139

    
140
class RemoteCalendar(StorableObject):
141
    _names = 'remote_calendars'
142

    
143
    label = None
144
    url = None
145
    content = None
146
    events = None
147
    error = None  # (time, string, params)
148

    
149
    def download_and_parse(self):
150
        old_content = self.content
151

    
152
        try:
153
            self.content = urllib2.urlopen(self.url).read()
154
        except urllib2.HTTPError, e:
155
            self.error = (time.localtime(), N_('HTTP Error %s on download'), (e.code,))
156
            self.store()
157
            return
158
        except urllib2.URLError, e:
159
            self.error = (time.localtime(), N_('Error on download'), ())
160
            self.store()
161
            return
162

    
163
        if self.error:
164
            self.error = None
165
            self.store()
166

    
167
        if self.content == old_content:
168
            return
169

    
170
        self.events = []
171
        try:
172
            parsed_cal = vobject.readOne(self.content)
173
        except vobject.base.ParseError:
174
            self.error = (time.localtime(), N_('Failed to parse file'), ())
175
            self.store()
176
            return
177

    
178
        for vevent in parsed_cal.vevent_list:
179
            ev = Event()
180
            ev.title = vevent.summary.value.encode('iso-8859-15')
181
            try:
182
                ev.url = vevent.url.value.encode('iso-8859-15')
183
            except AttributeError:
184
                pass
185
            ev.date_start = vevent.dtstart.value.timetuple()
186
            try:
187
                ev.date_end = vevent.dtend.value.timetuple()
188
            except AttributeError:
189
                pass
190
            try:
191
                ev.description = vevent.description.value.encode('iso-8859-15')
192
            except AttributeError:
193
                pass
194
            self.events.append(ev)
195
        self.store()
196

    
197
        
198
    def get_error_message(self):
199
        if not self.error:
200
            return None
201
        return '(%s) %s' % (misc.localstrftime(self.error[0]),
202
                _(self.error[1]) % self.error[2])
203

    
204

    
205
def update_remote_calendars(publisher):
206
    for source in RemoteCalendar.select():
207
        source.download_and_parse()
208

    
209
def get_default_event_tags():
210
    return [_('All Public'), _('Adults'), _('Children'), _('Free')]
211

    
212
get_publisher_class().register_cronjob(CronJob(update_remote_calendars, minutes = [0]))
213

    
(7-7/15)