Project

General

Profile

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

root / extra / modules / events.py @ 2750dbae

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, htmlescape
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 after_today(self):
39
        today = time.localtime()[:3]
40
        if not self.date_end:
41
            return tuple(self.date_start[:3]) > today
42
        return tuple(self.date_end[:3]) > today
43

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

    
76
    def as_vevent(self):
77
        vevent = vobject.newFromBehavior('vevent')
78
        site_charset = get_publisher().site_charset
79
        vevent.add('uid').value = '%04d%02d%02d-%s@%s' % (self.date_start[:3] + (self.id,
80
            get_request().get_server().lower().split(':')[0].rstrip('.')))
81
        vevent.add('summary').value = unicode(self.title, site_charset)
82
        vevent.add('dtstart').value = datetime.date(*self.date_start[:3])
83
        vevent.dtstart.value_param = 'DATE'
84
        if self.date_end:
85
            vevent.add('dtend').value = datetime.date(*self.date_end[:3])
86
            vevent.dtend.value_param = 'DATE'
87
        if self.description:
88
            vevent.add('description').value = unicode(self.description.strip(), site_charset)
89
        if self.url:
90
            vevent.add('url').value = unicode(self.url, site_charset)
91
        if self.location:
92
            vevent.add('location').value = unicode(self.location, site_charset)
93
        if self.organizer:
94
            vevent.add('organizer').value = unicode(self.organizer, site_charset)
95
        if self.keywords:
96
            vevent.add('categories').value = [unicode(x, site_charset) for x in self.keywords]
97
        vevent.add('class').value = 'PUBLIC'
98
        return vevent
99

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

    
133
        if self.url:
134
            if get_response().iframe_mode:
135
                r += htmltext('<a class="external" href="%s" target="_top">%s</a>') % (
136
                        self.url, _('More information'))
137
            else:
138
                r += htmltext('<a class="external" href="%s">%s</a>') % (
139
                        self.url, _('More information'))
140
        r += htmltext('</dd>')
141
        return r.getvalue()
142

    
143
    def get_url(self):
144
        from quixote import get_request
145
        return '%s://%s%sagenda/events/%s/' % (
146
                get_request().get_scheme(), get_request().get_server(),
147
                get_publisher().get_root_url(), self.id)
148

    
149

    
150
    def get_atom_entry(self):
151
        from pyatom import pyatom
152
        entry = pyatom.Entry()
153
        entry.id = self.get_url()
154
        entry.title = self.title
155

    
156
        entry.content.attrs['type'] = 'html'
157
        entry.content.text = str('<p>' + htmlescape(
158
                    unicode(self.description, get_publisher().site_charset).encode('utf-8')) + '</p>')
159

    
160
        return entry
161

    
162

    
163
class RemoteCalendar(StorableObject):
164
    _names = 'remote_calendars'
165

    
166
    label = None
167
    url = None
168
    content = None
169
    events = None
170
    error = None  # (time, string, params)
171

    
172
    def download_and_parse(self, job=None):
173
        old_content = self.content
174

    
175
        try:
176
            self.content = urllib2.urlopen(self.url).read()
177
        except urllib2.HTTPError, e:
178
            self.error = (time.localtime(), N_('HTTP Error %s on download'), (e.code,))
179
            self.store()
180
            return
181
        except urllib2.URLError, e:
182
            self.error = (time.localtime(), N_('Error on download'), ())
183
            self.store()
184
            return
185

    
186
        if self.error:
187
            self.error = None
188
            self.store()
189

    
190
        if self.content == old_content:
191
            return
192

    
193
        self.events = []
194
        try:
195
            parsed_cal = vobject.readOne(self.content)
196
        except vobject.base.ParseError:
197
            self.error = (time.localtime(), N_('Failed to parse file'), ())
198
            self.store()
199
            return
200

    
201
        site_charset = get_publisher().site_charset
202
        for vevent in parsed_cal.vevent_list:
203
            ev = Event()
204
            ev.title = vevent.summary.value.encode(site_charset, 'replace')
205
            try:
206
                ev.url = vevent.url.value.encode(site_charset, 'replace')
207
            except AttributeError:
208
                pass
209
            ev.date_start = vevent.dtstart.value.timetuple()
210
            try:
211
                ev.date_end = vevent.dtend.value.timetuple()
212
            except AttributeError:
213
                pass
214
            try:
215
                ev.description = vevent.description.value.encode(site_charset, 'replace')
216
            except AttributeError:
217
                pass
218
            try:
219
                ev.keywords = [x.encode(site_charset) for x in vevent.categories.value]
220
            except AttributeError:
221
                pass
222
            self.events.append(ev)
223
        self.store()
224

    
225
        
226
    def get_error_message(self):
227
        if not self.error:
228
            return None
229
        return '(%s) %s' % (misc.localstrftime(self.error[0]),
230
                _(self.error[1]) % self.error[2])
231

    
232

    
233
def update_remote_calendars(publisher):
234
    for source in RemoteCalendar.select():
235
        source.download_and_parse()
236

    
237
def get_default_event_tags():
238
    return [_('All Public'), _('Adults'), _('Children'), _('Free')]
239

    
240
get_publisher_class().register_cronjob(CronJob(update_remote_calendars, minutes = [0]))
241

    
(10-10/26)