summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md11
-rwxr-xr-xcalendar-cli.py97
2 files changed, 80 insertions, 28 deletions
diff --git a/README.md b/README.md
index dce3a27..e4fd545 100644
--- a/README.md
+++ b/README.md
@@ -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)