diff options
-rw-r--r-- | README.md | 11 | ||||
-rwxr-xr-x | calendar-cli.py | 97 |
2 files changed, 80 insertions, 28 deletions
@@ -86,10 +86,11 @@ Remember to `chmod og-r ~/.config/calendar.conf` or `chmod 0600 ~/.config/calend Add a calendar item "testevent" at 2013-10-01: - ./calendar-cli.py calendar --calendar-url=http://calendar.bekkenstenveien53c.oslo.no/caldav.php/tobias/calendar/ add 2013-10-01 testevent + ./calendar-cli.py calendar --calendar-url=tobias/calendar/ add 2013-10-01 testevent (assumes that `caldav-url`, `calldav-pass` and `caldav-user` has been added into configuration file. Those may also be added as command line options) + Objectives ---------- @@ -105,9 +106,9 @@ Milestones * CalDAV login (working as of version 0.02) * Push calendar item into CalDAV server (working as of version 0.02, but both an URL for the caldav server and an URL for the actual calendar has to be given) * Config file with CalDAV connection details (working as of version 0.03) -* Replace calendar-URL with calendar-path -* Find default calendar-path -* Show agenda +* Find default calendar-path (working as of version 0.04) +* Export ical file (working as of version 0.05) +* Show agenda (... hit a snag in the underlying libary. considering to replace the library.) * CLI-interface for creating ical todo events * Push todo item into CalDAV server @@ -119,3 +120,5 @@ Status 2013-09-28: version 0.02 - possible to add a calendar item to the caldav server 2013-10-02: version 0.03 - support for configuration file 2013-10-05: version 0.04 - no need to specify URL for the default calendar +2013-10-10: Attempts on implementing "agenda" stalled a bit due to problems with the library used. Considering to switch library. +2013-11-30: version 0.05 - added the calendar "addics" command for adding an ics file diff --git a/calendar-cli.py b/calendar-cli.py index 154b1e7..4d74a59 100755 --- a/calendar-cli.py +++ b/calendar-cli.py @@ -14,8 +14,9 @@ import uuid import json import os import logging +import sys -__version__ = "0.04" +__version__ = "0.05" __author__ = "Tobias Brox" __author_short__ = "tobixen" __copyright__ = "Copyright 2013, Tobias Brox" @@ -35,29 +36,14 @@ def caldav_connect(args): ssl = (splits.scheme == "https") return CalDAVAccount(splits.netloc, ssl=ssl, user=args.caldav_user, pswd=args.caldav_pass, root=(splits.path or '/'), principal=None, logging=args.debug_logging) - -def calendar_add(caldav_conn, args): - cal = Calendar() - cal.add('prodid', '-//{author_short}//{product}//{language}'.format(author_short=__author_short__, product=__product__, language=args.language)) - cal.add('version', '2.0') - if args.timezone: - tz = pytz.timezone(args.timezone) - event = Event() - ## read timestamps from arguments - dtstart = dateutil.parser.parse(args.event_time) - event.add('dtstart', dtstart) - ## TODO: handle duration and end-time as options. default 3600s by now. - event.add('dtend', dtstart + timedelta(0,3600)) - ## not really correct, and it breaks i.e. with google calendar - #event.add('dtstamp', datetime.now()) - ## maybe we should generate some uid? - uid = uuid.uuid1() - event.add('uid', str(uid)) - event.add('summary', ' '.join(args.description)) - cal.add_component(event) - +def _calendar_addics(caldav_conn, ics, uid, args): + """" + Internal" method for adding a calendar object item to the caldav + server through a PUT. ASSUMES the ics conforms to rfc4791.txt + section 4.1 Handles --calendar-url and --icalendar from the args + """ if args.icalendar: - print(cal.to_ical()) + print(ics) return if args.calendar_url: @@ -76,8 +62,68 @@ def calendar_add(caldav_conn, args): if not calendar.endswith('/'): calendar += '/' url = URL(calendar + str(uid) + '.ics') - caldav_conn.session.writeData(url, cal.to_ical(), 'text/calendar', method='PUT') + caldav_conn.session.writeData(url, ics, 'text/calendar', method='PUT') + +def calendar_addics(caldav_conn, args): + """ + Takes an ics from external source and puts it into the calendar. + From the CalDAV RFC: + + Calendar components in a calendar collection that have different UID + property values MUST be stored in separate calendar object resources. + + This means the inbound .ics has to be split up into one .ics for + each event as long as the uid is different. + """ + if args.file == '-': + input_ical = sys.stdin.read() + else: + with open(args.file, 'r') as f: + input_ical = f.read() + + c = Calendar.from_ical(input_ical) + + ## unfortunately we need to mess around with the object internals, + ## since the icalendar library doesn't offer methods out of the + ## hat for doing such kind of things + entries = c.subcomponents + + ## Timezones should be duplicated into each ics, ref the RFC + timezones = [x for x in entries if x.name == 'VTIMEZONE'] + + ## Make a mapping from UID to the other components + uids = {} + for x in entries: + if x.name == 'VTIMEZONE' or not 'UID' in x: + continue + uid = x['UID'].to_ical() + uids[uid] = uids.get(uid, []) + [x] + + for uid in uids: + c.subcomponents = timezones + uids[uid] + _calendar_addics(caldav_conn, c.to_ical(), uid, args) + +def calendar_add(caldav_conn, args): + cal = Calendar() + cal.add('prodid', '-//{author_short}//{product}//{language}'.format(author_short=__author_short__, product=__product__, language=args.language)) + cal.add('version', '2.0') + if args.timezone: + tz = pytz.timezone(args.timezone) + event = Event() + ## read timestamps from arguments + dtstart = dateutil.parser.parse(args.event_time) + event.add('dtstart', dtstart) + ## TODO: handle duration and end-time as options. default 3600s by now. + event.add('dtend', dtstart + timedelta(0,3600)) + ## not really correct, and it breaks i.e. with google calendar + #event.add('dtstamp', datetime.now()) + ## maybe we should generate some uid? + uid = uuid.uuid1() + event.add('uid', str(uid)) + event.add('summary', ' '.join(args.description)) + cal.add_component(event) + _calendar_addics(caldav_conn, cal.to_ical(), uid, args) def main(): ## This boilerplate pattern is from @@ -141,6 +187,9 @@ def main(): calendar_add_parser.add_argument('event_time', help="Timestamp and duration of the event. See the documentation for event_time specifications") calendar_add_parser.add_argument('description', nargs='+') calendar_add_parser.set_defaults(func=calendar_add) + calendar_addics_parser = calendar_subparsers.add_parser('addics') + calendar_addics_parser.add_argument('--file', help="ICS file to upload", default='-') + calendar_addics_parser.set_defaults(func=calendar_addics) calendar_agenda_parser = calendar_subparsers.add_parser('agenda') calendar_agenda_parser.set_defaults(func=niy) |