paul@216 | 1 | # -*- coding: iso-8859-1 -*- |
paul@216 | 2 | """ |
paul@216 | 3 | MoinMoin - Conversion of iCalendar resources to event pages |
paul@216 | 4 | |
paul@216 | 5 | @copyright: 2011 by Paul Boddie <paul@boddie.org.uk> |
paul@216 | 6 | |
paul@216 | 7 | @license: GNU GPL (v2 or later), see COPYING.txt for details. |
paul@216 | 8 | """ |
paul@216 | 9 | |
paul@216 | 10 | from collections import defaultdict |
paul@216 | 11 | from EventAggregatorSupport import Date |
paul@216 | 12 | import vCalendar |
paul@216 | 13 | import codecs |
paul@216 | 14 | import re |
paul@216 | 15 | |
paul@216 | 16 | date_regexp_str = ur'(?P<year>[0-9]{4})(?P<month>[0-9]{2})(?P<day>[0-9]{2})' |
paul@216 | 17 | time_regexp_str = ur'(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?:(?P<second>[0-6][0-9]))?' |
paul@216 | 18 | |
paul@216 | 19 | date_regexp = re.compile(date_regexp_str, re.UNICODE) |
paul@216 | 20 | time_regexp = re.compile(time_regexp_str, re.UNICODE) |
paul@216 | 21 | |
paul@216 | 22 | def convert(value, pattern): |
paul@216 | 23 | match = pattern.match(value) |
paul@216 | 24 | if match: |
paul@216 | 25 | return match.groups() |
paul@216 | 26 | else: |
paul@216 | 27 | return [value] |
paul@216 | 28 | |
paul@216 | 29 | def convert_date(value, end=0): |
paul@216 | 30 | fields = convert(value, date_regexp) |
paul@216 | 31 | if len(fields) == 1: |
paul@216 | 32 | return value |
paul@216 | 33 | else: |
paul@216 | 34 | date = Date(map(int, fields)) |
paul@216 | 35 | if end: |
paul@216 | 36 | date = date.day_update(-1) |
paul@216 | 37 | return str(date) |
paul@216 | 38 | |
paul@216 | 39 | def convert_time(value): |
paul@216 | 40 | fields = convert(value, time_regexp) |
paul@216 | 41 | if len(fields) == 1: |
paul@216 | 42 | return value |
paul@216 | 43 | else: |
paul@216 | 44 | return ":".join(fields) |
paul@216 | 45 | |
paul@216 | 46 | def convert_to_time_and_zone(value): |
paul@216 | 47 | for sign in ("-", "+", " "): |
paul@216 | 48 | parts = value.split(sign) |
paul@216 | 49 | if len(parts) > 1: |
paul@216 | 50 | return parts[0], (sign + parts[1]).lstrip() |
paul@216 | 51 | return value, "" |
paul@216 | 52 | |
paul@216 | 53 | def convert_datetime(value, end=0): |
paul@216 | 54 | parts = value.split("T") |
paul@216 | 55 | if len(parts) == 1: |
paul@216 | 56 | return convert_date(value, end) |
paul@216 | 57 | else: |
paul@216 | 58 | date, time = parts[:2] |
paul@216 | 59 | if value.endswith("Z"): |
paul@216 | 60 | time, zone = time[:-1], "UTC" |
paul@216 | 61 | else: |
paul@216 | 62 | time, zone = convert_to_time_and_zone(time) |
paul@216 | 63 | return "%s %s %s" % (convert_date(date), convert_time(time), zone) |
paul@216 | 64 | |
paul@216 | 65 | def parse_event(it): |
paul@216 | 66 | event = defaultdict(unicode) |
paul@216 | 67 | |
paul@216 | 68 | for name, parameters, value in it: |
paul@216 | 69 | if name == "END" and value == "VEVENT": |
paul@216 | 70 | break |
paul@216 | 71 | if name.startswith("DT"): |
paul@216 | 72 | value = convert_datetime(value, name == "DTEND") |
paul@216 | 73 | event[name] = value |
paul@216 | 74 | |
paul@216 | 75 | return event |
paul@216 | 76 | |
paul@216 | 77 | if __name__ == "__main__": |
paul@216 | 78 | import os, sys |
paul@216 | 79 | progname = os.path.split(sys.argv[0])[-1] |
paul@216 | 80 | |
paul@216 | 81 | try: |
paul@216 | 82 | it = vCalendar.iterparse(sys.argv[1]) |
paul@216 | 83 | except IndexError: |
paul@216 | 84 | print >>sys.stderr, "Usage: %s <iCalendar filename>" % progname |
paul@216 | 85 | sys.exit(1) |
paul@216 | 86 | |
paul@216 | 87 | events = [] |
paul@216 | 88 | |
paul@216 | 89 | for name, parameters, value in it: |
paul@216 | 90 | if name == "BEGIN" and value == "VEVENT": |
paul@216 | 91 | event = parse_event(it) |
paul@216 | 92 | event["DTEND"] = event["DTEND"] or event["DTSTART"] |
paul@216 | 93 | events.append(event) |
paul@216 | 94 | |
paul@216 | 95 | out = codecs.getwriter("utf-8")(sys.stdout) |
paul@216 | 96 | |
paul@216 | 97 | for event in events: |
paul@216 | 98 | out.write(u""" |
paul@216 | 99 | == %(SUMMARY)s == |
paul@216 | 100 | |
paul@216 | 101 | Start:: %(DTSTART)s |
paul@216 | 102 | End:: %(DTEND)s |
paul@216 | 103 | Summary:: %(SUMMARY)s |
paul@216 | 104 | Topics:: %(CATEGORIES)s |
paul@216 | 105 | Location:: %(LOCATION)s |
paul@216 | 106 | URL:: %(URL)s |
paul@216 | 107 | """ % event) |
paul@216 | 108 | |
paul@216 | 109 | out.write(""" |
paul@216 | 110 | ---- |
paul@216 | 111 | CategoryEvents |
paul@216 | 112 | """) |
paul@216 | 113 | |
paul@216 | 114 | # vim: tabstop=4 expandtab shiftwidth=4 |