Attachment 'monthcal2ics.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - Get an iCalendar (ics) feed from a MonthCalendar
4
5 @copyright: 2010 Florian Birée <florian@biree.name>
6 @license: GNU GPL, see COPYING for details.
7 @version: 0.2
8
9 Version 0.2:
10 * add the support of MoinMoin 1.8, add the mime type in result headers
11 Version 0.1:
12 * first version, for MoinMoin 1.9
13 """
14
15 import calendar
16 import datetime
17 import re
18
19 from MoinMoin.Page import Page
20
21 # Some constants :
22 # Range of time (relative from today) where events are searched:
23 START_RANGE = datetime.timedelta(days=31) # before today
24 END_RANGE = datetime.timedelta(days=6*31) # after today
25 # Default duration of an event:
26 DEFAULT_DURATION = datetime.timedelta(hours=2)
27
28 class VCalendar(list):
29 """Far uncomplete implementation of vCalendar"""
30
31 def __unicode__(self):
32 """Return the calendar in the iCalendar format"""
33 return (
34 u"BEGIN:VCALENDAR\nVERSION:2.0\n" +
35 u"PRODID:-//MoinMoin MonthCalendar2ics\n" +
36 u"".join([unicode(event) for event in self]) +
37 u"END:VCALENDAR\n"
38 )
39
40 def __str__(self):
41 """Return the calendar in the iCalendar format (utf-8 encoded)"""
42 return unicode(self).encode('utf-8')
43
44 class VEvent(object):
45 """Far uncomplete implementation of vEvent"""
46
47 def __init__(self, summary, dtstart, dtend=None, description=None):
48 """Create a new event
49
50 summary is a short description of the event
51 dtstart is a datetime.date for a full-day event, or a datetime.datetime
52 dtend is a datetime.datetime
53 description is the long description of the event
54 """
55 self.summary = summary
56 self.dtstart = dtstart
57 self.dtend = dtend
58 self.description = description
59
60 def __unicode__(self):
61 """Return the event in the iCalendar format"""
62 def idate(date):
63 """Return the date or datetime in the iCalendar format"""
64 return unicode(date.isoformat().replace(':', u'').replace('-', u''))
65 def istr(raw_str):
66 """Return the string in with iCalendar escaped new lines"""
67 return unicode(raw_str.replace('\n', '\\n'))
68
69 ev_str = u"BEGIN:VEVENT\n"
70 ev_str += u"DTSTART:%s\n" % idate(self.dtstart)
71 if self.dtend:
72 ev_str += u"DTEND:%s\n" % idate(self.dtend)
73 ev_str += u"SUMMARY:%s\n" % istr(self.summary)
74 if self.description:
75 ev_str += u"DESCRIPTION:%s\n" % istr(self.description)
76 ev_str += u"END:VEVENT\n"
77 return ev_str
78
79 def __str__(self):
80 """Return the event in the iCalendar format (utf-8 encoded)"""
81 return unicode(self).encode('utf-8')
82
83 def extract_events_from_page(content, year, month, day):
84 """Parse a MoinMoin page and extract events. Return a VCalendar object."""
85 def tatoi(time_tupple):
86 """Convert a string time tupple to an int tupple"""
87 if time_tupple[0]:
88 h = int(time_tupple[0])
89 else:
90 h = 0
91 if time_tupple[1]:
92 m = int(time_tupple[1])
93 else:
94 m = 0
95 return (h, m)
96
97 level1headers = re.compile(r'^\s*=\s(.*)\s=$', re.MULTILINE)
98 times = re.compile(r'(\d{1,2})\s*[hH:](?:\s*(\d{2}))?')
99
100 splitted_page = level1headers.split(content)
101 cal = VCalendar()
102 for i in range(len(splitted_page) / 2):
103 summary = splitted_page[i*2+1]
104 description = splitted_page[(i+1) * 2].strip()
105 found_times = times.findall(summary)
106 if len(found_times) == 0: # no start time, full day event
107 start_date = datetime.date(year, month, day)
108 end_date = None
109 elif len(found_times) == 1: # one start time, 1h event
110 h, m = tatoi(found_times[0])
111 start_date = datetime.datetime(year, month, day, h, m)
112 duration = DEFAULT_DURATION
113 end_date = start_date + duration
114 else: # at least two times (start and end of the event)
115 h, m = tatoi(found_times[0])
116 start_date = datetime.datetime(year, month, day, h, m)
117 h, m = tatoi(found_times[1])
118 end_date = datetime.datetime(year, month, day, h, m)
119 if not start_date < end_date:
120 duration = DEFAULT_DURATION
121 end_date = start_date + duration
122 cal.append(VEvent(summary, start_date, end_date, description))
123 return cal
124
125 def execute(pagename, request):
126 """Execute the generation of the ics feed.
127
128 Pagename must be the page where the month calendar is.
129 """
130 start_date = datetime.date.today() - START_RANGE
131 end_date = datetime.date.today() + END_RANGE
132 year_month_list = []
133 current_date = start_date.replace(day=1)
134 while current_date < end_date:
135 year_month_list.append((current_date.year, current_date.month))
136 firstday, monthlen = calendar.monthrange(current_date.year,
137 current_date.month)
138 current_date += datetime.timedelta(days=monthlen)
139
140 # Search for events and build the calendar
141 cal = VCalendar()
142 for year, month in year_month_list:
143 firstday, monthlen = calendar.monthrange(year, month)
144 for day in range(1, monthlen + 1):
145 link = "%s/%4d-%02d-%02d" % (pagename, year, month, day)
146 daypage = Page(request, link)
147 if daypage.exists() and request.user.may.read(link):
148 #print "found :", link
149 daycontent = daypage.get_raw_body()
150 cal += extract_events_from_page(daycontent, year,month, day)
151 # Send the iCalendar file
152 request.content_type = 'text/calendar'
153 if hasattr(request, 'headers') and hasattr(request.headers, 'add'):#moin 1.9
154 request.headers.add('Content-Type: text/calendar')
155 request.headers.add('Content-Disposition', 'inline; filename="%s.ics"' %
156 pagename)
157 else: # moin 1.8
158 request.emit_http_headers([
159 'Content-Type: text/calendar',
160 'Content-Disposition: inline; filename="%s.ics"' % pagename,
161 ])
162
163 request.write(str(cal))
164 #request.close()
165
166
167
168
169
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.