1
2
3
4
5
6
7
8
9
10 "Deals with iCalendar calendar import stuff"
11
12
13 from __future__ import with_statement
14 import datetime
15 import time
16
17
18
19
20 import bpcalendar
21 import bptime
22 import common_calendar
23 import guihelper
24 import vcal_calendar as vcal
25 import vcard
26
27 module_debug=False
28
29
34
35
42 _funcs={
43 'W': lambda x: x*604800,
44 'H': lambda x: x*3600,
45 'M': lambda x: x*60,
46 'S': lambda x: x,
47 'D': lambda x: x*86400,
48 'T': lambda x: 0,
49 'P': lambda x: 0,
50 }
52 _i=0
53 for _ch in data.get('value', ''):
54 if _ch=='+':
55 self._neg=False
56 elif _ch=='-':
57 self._neg=True
58 elif _ch.isdigit():
59 _i=_i*10+int(_ch)
60 else:
61 self._duration+=self._funcs.get(_ch, lambda _: 0)(_i)
62 _i=0
64 if self._neg:
65 return -self._duration
66 return self._duration
67
68
69 parentclass=vcal.VCalendarImportData
71
74
76
77
78
79 try:
80 _params=v.get('params', {})
81 if _params.get('RELATED', None)=='END':
82 return False
83 if _params.get('VALUE', 'DURATION')!='DURATION':
84 return False
85 _d=Duration(v)
86 if _d.get()>0:
87 return False
88 dd['alarm_value']=abs(_d.get()/60)
89 return True
90 except:
91 if __debug__:
92 raise
93 return False
94
96
97 if v.get('value', None)!='VALARM':
98 return False
99 _trigger=v.get('params', {}).get('TRIGGER', None)
100 if _trigger:
101 return self._conv_alarm(_trigger, dd)
102 return False
103
108
110 if v.get('params', {}).get('VALUE', None)=='DATE':
111
112 dd['allday']=True
113 return bptime.BPTime(v['value']).get()
114
115
117 _value={}
118 for _item in data.get('value', '').split(';'):
119 _l=_item.split('=')
120 if len(_l)>1:
121 _value[_l[0]]=_l[1].split(',')
122 else:
123 _value[_l[0]]=[]
124 return _value
125
126 _sorted_weekdays=['FR', 'MO', 'TH', 'TU', 'WE']
127 _dow_bitmap={
128 'SU': 1,
129 'MO': 2,
130 'TU': 4,
131 'WE': 8,
132 'TH': 0x10,
133 'FR': 0x20,
134 'SA': 0x40
135 }
136
138
139 dd['repeat_type']='daily'
140
141
142 _days=value.get('BYDAY', [])
143 _days.sort()
144 if _days==self._sorted_weekdays:
145 _interval=0
146 else:
147 try:
148 _interval=int(value.get('INTERVAL', [1])[0])
149 except ValueError:
150 _interval=1
151 dd['repeat_interval']=_interval
152 return True
153
155
156 dd['repeat_type']='weekly'
157 try:
158 _interval=int(value.get('INTERVAL', [1])[0])
159 except ValueError:
160 _interval=1
161 dd['repeat_interval']=_interval
162 _dow=0
163 for _day in value.get('BYDAY', []):
164 _dow|=self._dow_bitmap.get(_day, 0)
165 dd['repeat_dow']=_dow
166 return True
167
169 dd['repeat_type']='monthly'
170 try:
171 _interval2=int(value.get('INTERVAL', [1])[0])
172 except ValueError:
173 _interval2=1
174 dd['repeat_interval2']=_interval2
175
176 _nth=0
177 _dow=0
178 _daystr=value.get('BYDAY', [None])[0]
179 if _daystr:
180
181 _dow=self._dow_bitmap.get(_daystr[-2:], 0)
182 _nth=1
183 try:
184 if len(_daystr)>2:
185 _nth=int(_daystr[:-2])
186 elif value.get('BYSETPOS', [None])[0]:
187 _nth=int(value['BYSETPOS'][0])
188 except ValueError:
189 pass
190 if _nth==-1:
191 _nth=5
192 if _nth<1 or _nth>5:
193 _nth=1
194 dd['repeat_dow']=_dow
195 dd['repeat_interval']=_nth
196 return True
197
199 dd['repeat_type']='yearly'
200 return True
201
202 _funcs={
203 'DAILY': _build_daily,
204 'WEEKLY': _build_weekly,
205 'MONTHLY': _build_monthly,
206 'YEARLY': _build_yearly,
207 }
209 _params=v.get('params', {})
210 _value=self._build_value_dict(v)
211 _rep=self._funcs.get(
212 _value.get('FREQ', [None])[0], lambda *_: False)(self, _value, dd)
213 if _rep:
214 if _value.get('COUNT', [None])[0]:
215 dd['repeat_num']=int(_value['COUNT'][0])
216 elif _value.get('UNTIL', [None])[0]:
217 dd['repeat_end']=bptime.BPTime(_value['UNTIL'][0]).get()
218 dd['repeat_wkst']=_value.get('WKST', ['MO'])[0]
219 return _rep
220
222 r=[]
223 try:
224 _val=v if isinstance(v, (list, tuple)) else [v]
225 for _item in _val:
226 for n in _item['value'].split(','):
227 r.append(bptime.BPTime(n).get())
228 return r
229 except:
230 if __debug__:
231 raise
232 return []
233
235 _dt=bptime.BPTime(v['value']).get(default=(0,0,0, None, None))
236 if _dt[-1] is None:
237
238 dd['allday']=True
239 _dt=_dt[:3]+(0,0)
240 return _dt
241
244
245 _calendar_keys=[
246 ('CATEGORIES', 'categories', parentclass._conv_cat),
247 ('DESCRIPTION', 'notes', parentclass._conv_str),
248 ('DTSTART', 'start', _conv_start_date),
249 ('DTEND', 'end', _conv_end_date),
250 ('DURATION', 'end', _conv_duration),
251 ('LOCATION', 'location', parentclass._conv_str),
252 ('PRIORITY', 'priority', parentclass._conv_priority),
253 ('SUMMARY', 'description', parentclass._conv_str),
254 ('RRULE', 'repeat', _conv_repeat),
255 ('EXDATE', 'exceptions', _conv_exceptions),
256 ('BEGIN-END', 'alarm', _conv_valarm),
257 ]
258
259
264
265
266 ExportDialogParent=common_calendar.ExportCalendarDialog
267 out_line=vcard.out_line
268
270 _default_file_name="calendar.ics"
271 _wildcards="ICS files|*.ics"
272
275
277 f.write(out_line('BEGIN', None, 'VCALENDAR', None))
278 f.write(out_line('PRODID', None, '-//BitPim//EN', None))
279 f.write(out_line('VERSION', None, '2.0', None))
280 f.write(out_line('METHOD', None, 'PUBLISH', None))
283
285
286 f.write(out_line('BEGIN', None, 'VTIMEZONE', None))
287 _tzid=time.tzname[0].split(' ')[0]
288 f.write(out_line('TZID', None, _tzid, None))
289 _offset_standard=-((time.timezone/3600)*100+time.timezone%3600)
290 _offset_daylight=_offset_standard+100
291
292 f.write(out_line('BEGIN', None, 'STANDARD', None))
293 f.write(out_line('DTSTART', None, '20051030T020000', None))
294 f.write(out_line('RRULE', None,
295 'FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11', None))
296 f.write(out_line('TZOFFSETFROM', None,
297 '%05d'%_offset_daylight, None))
298 f.write(out_line('TZOFFSETTO', None,
299 '%05d'%_offset_standard, None))
300 f.write(out_line('END', None, 'STANDARD', None))
301
302 f.write(out_line('BEGIN', None, 'DAYLIGHT', None))
303 f.write(out_line('DTSTART', None, '20060402T020000', None))
304 f.write(out_line('RRULE', None,
305 'FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3', None))
306 f.write(out_line('TZOFFSETFROM', None,
307 '%05d'%_offset_standard, None))
308 f.write(out_line('TZOFFSETTO', None,
309 '%05d'%_offset_daylight, None))
310 f.write(out_line('END', None, 'DAYLIGHT', None))
311
312 f.write(out_line('END', None, 'VTIMEZONE', None))
313 return _tzid
314
315
317 _cats=[x['category'] for x in v]
318 if _cats:
319 return out_line(keyword, None, ','.join(_cats), None)
321 if v:
322 return out_line(keyword, None, v, None)
324 if v<1:
325 return
326 return out_line(keyword, None, '%d'%min(v, 9), None)
328 if v<0:
329
330 return
331 _res=out_line('BEGIN', None, 'VALARM', None)
332 _res+=out_line('TRIGGER', None,
333 '-P%dDT%dH%dM'%(v/1440, (v%1440)/60, v%60), None)
334 _res+=out_line('ACTION', None, 'AUDIO', None)
335 _res+=out_line('END', None, 'VALARM', None)
336 return _res
338
339
340 _start=bptime.BPTime(event.start)
341 _end=bptime.BPTime(event.end)
342 if event.allday:
343
344 _params=('VALUE=DATE',)
345 _res=out_line('DTSTART', _params,
346 _start.iso_str(no_time=True), None)
347 _end+=bptime.timedelta(days=1)
348 _res+=out_line('DTEND', _params,
349 _end.iso_str(no_time=True), None)
350 else:
351 _params=('TZID=%s'%tzid,)
352 _res=out_line('DTSTART', _params, _start.iso_str(no_seconds=False), None)
353 _res+=out_line('DTEND', _params, _end.iso_str(no_seconds=False), None)
354 return _res
356
357 _start=bptime.BPTime(event.start)
358 _end=bptime.BPTime(event.end)
359 if event.allday:
360
361 _params=('VALUE=DATE',)
362 _res=out_line('DTSTART', _params,
363 _start.iso_str(no_time=True), None)
364 _end+=bptime.timedelta(days=1)
365 _res+=out_line('DTEND', _params,
366 _end.iso_str(no_time=True), None)
367 else:
368
369 _new_end=_start+(_end-_start).seconds
370 _params=('TZID=%s'%tzid,)
371 _res=out_line('DTSTART', _params, _start.iso_str(no_seconds=False), None)
372 _res+=out_line('DTEND', _params, _new_end.iso_str(no_seconds=False), None)
373 return _res
375 _value=['FREQ=DAILY']
376 if not event.open_ended():
377 _value.append('UNTIL=%04d%02d%02dT000000Z'%event.end[:3])
378 if rpt.interval:
379
380 _value.append('INTERVAL=%d'%rpt.interval)
381 else:
382
383 _value.append('BYDAY=MO,TU,WE,TH,FR')
384 return out_line('RRULE', None, ';'.join(_value), None)
385 _dow_list=(
386 (1, 'SU'), (2, 'MO'), (4, 'TU'), (8, 'WE'), (16, 'TH'),
387 (32, 'FR'), (64, 'SA'))
388 _dow_wkst={
389 1: 'MO', 2: 'TU', 3: 'WE', 4: 'TH', 5: 'FR', 6: 'SA', 7: 'SU' }
424 _value=['FREQ=YEARLY',
425 'INTERVAL=1',
426 'BYMONTH=%d'%event.start[1],
427 'BYMONTHDAY=%d'%event.start[2],
428 ]
429 if not event.open_ended():
430 _value.append('UNTIL=%04d%02d%02dT000000Z'%event.end[:3])
431 return out_line('RRULE', None, ';'.join(_value), None)
438 _repeat=event.repeat
439 _type=_repeat.repeat_type
440 if _type:
441 _res=getattr(self, '_write_repeat_'+_type, lambda *_: None)(event, _repeat)
442 if _res and _repeat.suppressed:
443 _res+=self._write_repeat_exceptions(event, _repeat)
444 return _res
453
454 _field_list=(
455 ('SUMMARY', 'description', _write_string),
456 ('DESCRIPTION', 'notes', _write_string),
457 ('DTSTART', 'start', _write_times),
458 ('LOCATION', 'location', _write_string),
459 ('PRIORITY', 'priority', _write_priority),
460 ('CATEGORIES', 'categories', _write_categories),
461 ('TRIGGER', 'alarm', _write_alarm),
462 )
463
465
466 f.write(out_line('COMMENT', None, '//----------', None))
467 f.write(out_line('BEGIN', None, 'VEVENT', None))
468 for _entry in self._field_list:
469 _v=getattr(event, _entry[1], None)
470 if _v is not None:
471 _line=_entry[2](self, _entry[0], _v, event, tzid)
472 if _line:
473 f.write(_line)
474 f.write(out_line('DTSTAMP', None,
475 '%04d%02d%02dT%02d%02d%02dZ'%time.gmtime()[:6],
476 None))
477 f.write(out_line('UID', None, event.id, None))
478 f.write(out_line('END', None, 'VEVENT', None))
480 filename=self.filenamectrl.GetValue()
481 try:
482 f=file(filename, 'wt')
483 except:
484 f=None
485 if f is None:
486 guihelper.MessageDialog(self, 'Failed to open file ['+filename+']',
487 'Export Error')
488 return
489 all_items=self._selection.GetSelection()==0
490 dt=self._start_date.GetValue()
491 range_start=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay())
492 dt=self._end_date.GetValue()
493 range_end=(dt.GetYear(), dt.GetMonth()+1, dt.GetDay())
494 cal_dict=self.GetParent().GetCalendarData()
495 self._write_header(f)
496 _tzid=self._write_timezone(f)
497
498 for k,e in cal_dict.items():
499 if not all_items and \
500 (e.end < range_start or e.start>range_end):
501 continue
502 self._write_event(f, e, _tzid)
503 self._write_end(f)
504 f.close()
505