Project

General

Profile

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

root / extra / modules / events.py @ 9e6caf99

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

    
6
from quixote import get_request
7

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

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

    
16
    title = None
17
    description = None
18
    url = None
19
    date_start = None
20
    date_end = None
21

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

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

    
64
    def as_vevent(self):
65
        vevent = vobject.newFromBehavior('vevent')
66
        vevent.add('uid').value = '%04d%02d%02d-%s@%s' % (self.date_start[:3] + (self.id,
67
            get_request().get_server().lower().split(':')[0].rstrip('.')))
68
        vevent.add('summary').value = unicode(self.title, 'iso-8859-15')
69
        vevent.add('dtstart').value = datetime.date(*self.date_start[:3])
70
        vevent.dtstart.value_param = 'DATE'
71
        if self.date_end:
72
            vevent.add('dtend').value = datetime.date(*self.date_end[:3])
73
            vevent.dtend.value_param = 'DATE'
74
        if self.description:
75
            vevent.add('description').value = unicode(self.description.strip(), 'iso-8859-15')
76
        if self.url:
77
            vevent.add('url').value = unicode(self.url, 'iso-8859-15')
78
        vevent.add('class').value = 'PUBLIC'
79
        return vevent
80

    
81
    def as_vcalendar(cls):
82
        cal = vobject.iCalendar()
83
        cal.add('prodid').value = '-//Entr\'ouvert//NON SGML Au Quotidien'
84
        for x in cls.select():
85
            cal.add(x.as_vevent())
86
        return cal.serialize()
87
    as_vcalendar = classmethod(as_vcalendar)
88

    
89

    
90
class RemoteCalendar(StorableObject):
91
    _names = 'remote_calendars'
92

    
93
    label = None
94
    url = None
95
    content = None
96
    events = None
97
    error = None  # (time, string, params)
98

    
99
    def download_and_parse(self):
100
        old_content = self.content
101

    
102
        try:
103
            self.content = urllib2.urlopen(self.url).read()
104
        except urllib2.HTTPError, e:
105
            self.error = (time.localtime(), N_('HTTP Error %s on download'), (e.code,))
106
            self.store()
107
            return
108
        except urllib2.URLError, e:
109
            self.error = (time.localtime(), N_('Error on download'), ())
110
            self.store()
111
            return
112

    
113
        if self.error:
114
            self.error = None
115
            self.store()
116

    
117
        if self.content == old_content:
118
            return
119

    
120
        self.events = []
121
        try:
122
            parsed_cal = vobject.readOne(self.content)
123
        except vobject.base.ParseError:
124
            self.error = (time.localtime(), N_('Failed to parse file'), ())
125
            self.store()
126
            return
127

    
128
        for vevent in parsed_cal.vevent_list:
129
            ev = Event()
130
            ev.title = vevent.summary.value.encode('iso-8859-15')
131
            try:
132
                ev.url = vevent.url.value.encode('iso-8859-15')
133
            except AttributeError:
134
                pass
135
            ev.date_start = vevent.dtstart.value.timetuple()
136
            if vevent.dtend:
137
                try:
138
                    ev.date_end = vevent.dtend.value.timetuple()
139
                except AttributeError:
140
                    pass
141
            try:
142
                ev.description = vevent.description.value.encode('iso-8859-15')
143
            except AttributeError:
144
                pass
145
            self.events.append(ev)
146
        self.store()
147

    
148
        
149
    def get_error_message(self):
150
        if not self.error:
151
            return None
152
        return '(%s) %s' % (misc.localstrftime(self.error[0]),
153
                _(self.error[1]) % self.error[2])
154

    
155

    
156
def update_remote_calendars(publisher):
157
    for source in RemoteCalendar.select():
158
        source.download_and_parse()
159

    
160
get_publisher_class().register_cronjob(CronJob(update_remote_calendars, minutes = [0]))
161

    
(7-7/15)