summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md45
-rwxr-xr-xcalendar-cli.py116
2 files changed, 113 insertions, 48 deletions
diff --git a/README.md b/README.md
index a20fa9f..7c909be 100644
--- a/README.md
+++ b/README.md
@@ -20,19 +20,20 @@ Synopsis
cli.py should be symlinked to the various commands.
-A configuration file (ini-format?) can contain defaults for all the global options.
-
-The config file may have a section for each CalDAV server ... (todo, think more about this)
-
### Global options
-(only long options will be available in version 0.1; don't want to pollute the short option space yet)
+Only long options will be available up until version 0.10; I don't
+want to pollute the short option space before the CLI is reasonably
+well-defined.
+
+Always consult --help for up-to-date and complete listings of options.
+The list below will only contain the most important options and may
+not be up-to-date and may contain features not implemented yet.
* --interactive, -i: stop and query the user rather often
-* --caldav-url, --caldav-user, --caldav-pass: how to connect to the CalDAV server
-* --caldav-calendar: which calendar to access
-* --config: use a specific configuration file (default: $HOME/.calendar-cli.conf)
-* --config-section: use a specific section from the config file (i.e. to select a different
+* --caldav-url, --caldav-user, --caldav-pass: how to connect to the CalDAV server. Fits better into a configuration file.
+* --config-file: use a specific configuration file (default: $HOME/.calendar-cli.conf)
+* --config-section: use a specific section from the config file (i.e. to select a different caldav-server to connect to)
* --icalendar: instead of connecting to a CalDAV server, write an icalendar file to stdout
### Commands
@@ -46,7 +47,7 @@ The config file may have a section for each CalDAV server ... (todo, think more
Supported in v0.01:
-* dateutil.parser.parse()
+* anything recognized by dateutil.parser.parse()
All of those would eventually be supported in future versions if it's not too difficult to achieve:
@@ -59,11 +60,31 @@ All of those would eventually be supported in future versions if it's not too di
Alternatively, endtime or duration can be given through options.
+Configuration file
+------------------
+
+(I considered a configuration file in .ini-format, having a "default"-section with default values for any global options, and optionally other sections for different CalDAV-servers. Asking a bit around for recommendations on config file format as well as location, I was told that the .ini-format is not a standard, I'd be better off using a standard like yaml, json or xml. Personally I like json a bit better than yaml - after consulting with a friend I ended up with json. Location ... I think it's "cleaner" to keep it in ~/.config/, and I'd like any calendar application to be able to access the file, hence it got ~/.config/calendar.conf rather than ~/.calendar-cli.conf)
+
+The file may look like this:
+
+ { "default":
+ { "caldav_url": "http://foo.bar.example.com/ical/",
+ "caldav-user": "luser",
+ "caldav-pass": "insecure"
+ }
+ }
+
+Optionally, in addition (or even instead) of "default", other "sections" can be created and selected through the --config-section option. The rationale is to allow configuration for multiple CalDAV-servers to remain in the same configuration file. Later versions will eventually be capable of copying events, or putting events into several calendars.
+
+Remember to `chmod og-r ~/.config/calendar.conf` or `chmod 0600 ~/.config/calendar.conf`
+
### Examples
Add a calendar item "testevent" at 2013-10-01:
- ./calendar-cli.py --caldav-url=http://calendar.bekkenstenveien53c.oslo.no/caldav.php/ --caldav-user=tobias --caldav-pass=banana calendar --calendar-url=http://calendar.bekkenstenveien53c.oslo.no/caldav.php/tobias/calendar/ add 2013-10-01 testevent
+ ./calendar-cli.py calendar --calendar-url=http://calendar.bekkenstenveien53c.oslo.no/caldav.php/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
----------
@@ -79,8 +100,8 @@ Milestones
* CLI-interface for creating ical calendar events (working as of version 0.01)
* 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
-* Config file with CalDAV connection details
* Find default calendar-path
* Show agenda
* CLI-interface for creating ical todo events
diff --git a/calendar-cli.py b/calendar-cli.py
index 90727d7..f75bce1 100755
--- a/calendar-cli.py
+++ b/calendar-cli.py
@@ -11,8 +11,11 @@ from icalendar import Calendar,Event
from caldavclientlibrary.client.account import CalDAVAccount
from caldavclientlibrary.protocol.url import URL
import uuid
+import json
+import os
+import logging
-__version__ = "0.02"
+__version__ = "0.03"
__author__ = "Tobias Brox"
__author_short__ = "tobixen"
__copyright__ = "Copyright 2013, Tobias Brox"
@@ -33,7 +36,7 @@ def caldav_connect(args):
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(args):
+def calendar_add(caldav_conn, args):
if (not args.calendar_url):
## args.calendar_url must be given ... :/
niy()
@@ -61,37 +64,78 @@ def calendar_add(args):
elif args.caldav_url:
caldav_conn.session.writeData(URL(args.calendar_url+str(uuid.uuid1())+'.ical'), cal.to_ical(), 'text/calendar', method='PUT')
-parser = argparse.ArgumentParser()
-
-## Global options
-parser.add_argument("--icalendar", help="Do not connect to CalDAV server, but read/write icalendar format from stdin/stdout", action="store_true")
-parser.add_argument("--timezone", help="Timezone to use")
-parser.add_argument('--language', help="language used", default="EN")
-parser.add_argument("--caldav-url", help="Full URL to the caldav server", metavar="URL")
-parser.add_argument("--caldav-user", help="username to log into the caldav server", metavar="USER")
-parser.add_argument("--caldav-pass", help="password to log into the caldav server", metavar="PASS")
-parser.add_argument("--debug-logging", help="turn on debug logging", action="store_true")
-
-## TODO: check sys.argv[0] to find command
-subparsers = parser.add_subparsers(title='command')
-
-calendar_parser = subparsers.add_parser('calendar')
-calendar_parser.add_argument("--calendar-url", help="URL for calendar to be used")
-calendar_subparsers = calendar_parser.add_subparsers(title='subcommand')
-calendar_add_parser = calendar_subparsers.add_parser('add')
-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_agenda_parser = calendar_subparsers.add_parser('agenda')
-calendar_agenda_parser.set_defaults(func=niy)
-todo_parser = subparsers.add_parser('todo')
-todo_parser.set_defaults(func=niy)
-args = parser.parse_args()
-
-if not args.icalendar:
- caldav_conn = caldav_connect(args)
-
-ret = args.func(args)
-
-
+def main():
+ ## This boilerplate pattern is from
+ ## http://stackoverflow.com/questions/3609852
+ ## We want defaults for the command line options to be fetched from the config file
+
+ # Parse any conf_file specification
+ # We make this parser with add_help=False so that
+ # it doesn't parse -h and print help.
+ conf_parser = argparse.ArgumentParser(
+ description=__doc__, # printed with -h/--help
+ # Don't mess with format of description
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ # Turn off help, so we print all options in response to -h
+ add_help=False
+ )
+ conf_parser.add_argument("--config-file",
+ help="Specify config file", metavar="FILE", default=os.getenv('XDG_CONFIG_HOME', os.getenv('HOME', '~') + '/.config')+'/calendar.conf')
+ conf_parser.add_argument("--config-section",
+ help="Specify config section; allows several caldav servers to be configured in the same config file", default='default')
+ args, remaining_argv = conf_parser.parse_known_args()
+
+ config = {}
+ try:
+ with open(args.config_file) as config_file:
+ config = json.load(config_file)
+ except IOError:
+ ## File not found
+ logging.info("no config file found")
+ except ValueError:
+ logging.error("error in config file", exc_info=True)
+
+ defaults = config.get('default', {})
+
+ # Parse rest of arguments
+ # Don't suppress add_help here so it will handle -h
+ parser = argparse.ArgumentParser(
+ # Inherit options from config_parser
+ parents=[conf_parser]
+ )
+ parser.set_defaults(**defaults)
+
+ ## Global options
+ parser.add_argument("--icalendar", help="Do not connect to CalDAV server, but read/write icalendar format from stdin/stdout", action="store_true")
+ parser.add_argument("--timezone", help="Timezone to use")
+ parser.add_argument('--language', help="language used", default="EN")
+ parser.add_argument("--caldav-url", help="Full URL to the caldav server", metavar="URL")
+ parser.add_argument("--caldav-user", help="username to log into the caldav server", metavar="USER")
+ parser.add_argument("--caldav-pass", help="password to log into the caldav server", metavar="PASS")
+ parser.add_argument("--debug-logging", help="turn on debug logging", action="store_true")
+
+ ## TODO: check sys.argv[0] to find command
+ ## TODO: set up logging
+ subparsers = parser.add_subparsers(title='command')
+
+ calendar_parser = subparsers.add_parser('calendar')
+ calendar_parser.add_argument("--calendar-url", help="URL for calendar to be used")
+ calendar_subparsers = calendar_parser.add_subparsers(title='subcommand')
+ calendar_add_parser = calendar_subparsers.add_parser('add')
+ 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_agenda_parser = calendar_subparsers.add_parser('agenda')
+ calendar_agenda_parser.set_defaults(func=niy)
+ todo_parser = subparsers.add_parser('todo')
+ todo_parser.set_defaults(func=niy)
+ args = parser.parse_args(remaining_argv)
+
+ if not args.icalendar:
+ caldav_conn = caldav_connect(args)
+
+ ret = args.func(caldav_conn, args)
+
+if __name__ == '__main__':
+ main()