summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--EXAMPLES6
-rw-r--r--README.md40
-rwxr-xr-xcalendar-cli.py175
-rwxr-xr-xtests/script_test.sh12
4 files changed, 166 insertions, 67 deletions
diff --git a/EXAMPLES b/EXAMPLES
index 97b7c8b..fb474fa 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -51,7 +51,7 @@ fi
## Interactively mark tasks as completed:
-calendar-cli todo $selflags --hide-parents --limit 10 list --todo-template='# calendar-cli todo --todo-uid={uid} complete # {summary}' > $tempfile
+calendar-cli todo $selflags --hide-parents --limit 12 list --todo-template='# calendar-cli todo --todo-uid={uid} complete # {summary}' > $tempfile
## Exit if there aren't any tasks
if [ ! -s $tempfile ]
then
@@ -65,7 +65,7 @@ $EDITOR $tempfile
. $tempfile
## Set more realistic due-dates on overdue tasks
-calendar-cli todo --overdue list --todo-template='calendar-cli todo --todo-uid={uid} postpone --due "in 4d" # {summary}' > $tempfile
+calendar-cli todo --overdue list --todo-template='calendar-cli todo --todo-uid={uid} postpone --due "in 15d" # {summary}' > $tempfile
if [ -s $tempfile ]
then
echo -e "\nNext up: Look over overdue tasks and consider procrastinating some of them. Press enter"
@@ -75,7 +75,7 @@ then
fi
## Clean the list a bit by procrastinating tasks (this includes the overdue)
-calendar-cli todo --hide-future list --todo-template='calendar-cli todo --todo-uid={uid} postpone "in 4d" # {summary}' > $tempfile
+calendar-cli todo --limit 10 --hide-future list --todo-template='calendar-cli todo --todo-uid={uid} postpone "in 4d" # {summary}' > $tempfile
if [ -s $tempfile ]
then
echo -e "\nNext up: Consider procrastinating the start-date of some of the tasks on your list. Press enter"
diff --git a/README.md b/README.md
index b875b09..d7b590d 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,13 @@ calendar-cli
Simple command-line CalDav client, for adding and browsing calendar items, todo list items, etc. As of version 0.11 it's even becoming a full-fledged task management tool.
+Other tools
+-----------
+
+There is another project out there, "Command-line Interface for Google Calendar", previously located at pypi under the calendar-cli name. It has now been renamed to gcalendar-cli to avoid name conflict, and is available at https://pypi.python.org/pypi/gcalendar-cli/
+
There is a "competing" project at https://github.com/geier/khal - you may want to check it out - it's more mature but probably more complex. It's using a "vsyncdir" backend - if I've understood it correctly, that involves building a local copy of the calendar. The philosophy behind calendar-cli is slightly different, calendar-cli is supposed to be a simple cli-based caldav+ical client. No synchronization, no local storage.
-*NOTE*: As of 2015-10, someone has uploaded a "Command-line Interface for Google Calendar" to pypi with the same name - https://pypi.python.org/pypi/calendar-cli/ - this is a different project!
Support
-------
@@ -17,7 +21,7 @@ Rationale
GUIs and Web-UIs are nice for some purposes, but I really find the command line unbeatable when it comes to:
-* Minor stuff that are repeated often. Writing something like "todo add make a calendar-cli system" or "cal add 'tomorrow 15:40+2h' doctor appointment" is just very much faster than navigating into some web calendar interface and add an item there.
+* Minor stuff that are repeated often. Writing something like "todo add make a calendar-cli system" or "calendar add 'tomorrow 15:40+2h' doctor appointment" is just very much faster than navigating into some web calendar interface and add an item there.
* Things that are outside the scope of the UI. Here is one of many tasks I'd like to do: "go through the work calendar, find all new calendar events that are outside office hours, check up with the personal calendar if there are potential conflicts, add some information at the personal calendar if appropriate", and vice versa - it has to be handled very manually if doing it through any normal calendar application as far as I know, but if having some simple CLI or python library I could easily make some interactive script that would help me doing the operation above.
I've been looking a bit around, all I could find was cadaver and CalDAVClientLibrary. Both of those seems to be a bit shortcoming; they seem to miss the iCalendar parsing/generation, and there are things that simply cannot be done through those tools.
@@ -41,11 +45,14 @@ not be up-to-date and may contain features not implemented yet.
* --interactive: stop and query the user rather often
* --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)
+* --calendar-url: url to the calendar one wants to use.
+* --config-file: use a specific configuration file (default: $HOME/.config/calendar.conf)
* --config-section: use a specific section from the config file (i.e. to select a different caldav-server to connect to)
* --icalendar: Write or read icalendar to/from stdout/stdin
* --nocaldav: don't connect to a caldav server
+The caldav URL is supposed to be something like i.e. http://some.davical.server/caldav.php/ - it is only supposed to relay the server location, not the user or calendar. It's a common mistake to use something like http://some.davical.server/caldav.php/tobixen/work-calendar/ - it will also work, but it will ignore the calendar part of it, and use first calendar it can find - perhaps tobixen/family-calendar/. Use http://some.davical.server/caldav.php/ as the caldav URL, and tobixen/family-calendar as the calendar-url.
+
### Commands
* calendar - access/modify a calendar
@@ -80,15 +87,29 @@ Configuration file is by default located in $HOME/.config/calendar.conf and shou
The file may look like this:
```json
-{ "default":
- { "caldav_url": "http://foo.bar.example.com/caldav/",
+{ "default":
+ { "caldav_url": "http://foo.bar.example.com/caldav/",
+ "caldav_user": "luser",
+ "caldav_pass": "insecure"
+ }
+}
+```
+A configuration with multiple sections may look like this:
+
+```json
+{ "default":
+ { "caldav_url": "http://foo.bar.example.com/caldav/",
"caldav_user": "luser",
"caldav_pass": "insecure"
+ },
+ "caldav_url": "http://foo.baz.example.com/caldav/",
+ "caldav_user": "luser2",
+ "caldav_pass": "insecure2"
}
}
```
-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.
+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, or multiple calendars on the same CalDAV-server 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`
@@ -96,7 +117,7 @@ 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-url=http://calendar.bekkenstenveien53c.oslo.no/caldav.php/tobias/calendar/ 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)
@@ -123,6 +144,7 @@ Changelog
* 2015-03-30: version 0.8 - has a interactive configuration mode for those not feeling comfortable with hand-crafting the config in json syntax
* 2015-03-30: version 0.9 - now it's possible to set a duration when adding events to the calendar.
* 2015-05-02: version 0.10 - added support for todo subcommand
+* 2016-01-23: version 0.11 - more work on tasks, some bugfixes, pulled in new command line options from Florian Tham and Zachary Palmer
Roadmap
-------
@@ -132,7 +154,3 @@ This is being moved out to the issues section in github.
* Allow specification of event duration and/or event end time through options
* Fix easy-to-use symlinks (or alternatively wrapper scripts)
* Make some nosetests
-
-Donations
----------
-Donations are not expected, but as long as this is a one-man hobby project at least it's not problematic to receive donations. Send millibitcoins to 139xWFKwX9WejtRR1HP917qJGnRkZ6kn4M eventually. Donations may motivate me to work on specific feature requests or issues.
diff --git a/calendar-cli.py b/calendar-cli.py
index b15843e..effa704 100755
--- a/calendar-cli.py
+++ b/calendar-cli.py
@@ -1,8 +1,8 @@
-#!/usr/bin/python2
+#!/usr/bin/env python
"""
calendar-cli.py - high-level cli against caldav servers
-Copyright (C) 2013-2016 Tobias Brox and other contributors
+Copyright (C) 2013-2019 Tobias Brox and other contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,11 +33,19 @@ import os
import logging
import sys
import re
+import urllib3
+from getpass import getpass
+
+## ref https://github.com/tobixen/calendar-cli/issues/33, python3-compatibility
+try:
+ raw_input
+except NameError:
+ raw_input = input
__version__ = "0.11.0.dev0"
__author__ = "Tobias Brox"
__author_short__ = "tobixen"
-__copyright__ = "Copyright 2013-2016, Tobias Brox"
+__copyright__ = "Copyright 2013-2019, Tobias Brox"
#__credits__ = []
__license__ = "GPLv3+"
__license_url__ = "http://www.gnu.org/licenses/gpl-3.0-standalone.html"
@@ -47,11 +55,26 @@ __status__ = "Development"
__product__ = "calendar-cli"
__description__ = "high-level cli against caldav servers"
+def _date(ts):
+ """
+ helper function to get a date out of a Date or Datetime object.
+ """
+ if hasattr(ts, 'date'):
+ return ts.date()
+ return ts
+
def _force_datetime(t, args):
"""
- date objects cannot be compared with timestamp objects, neither in python2 nor python3. Silly.
- also, objects with time zone info cannot be compared with timestamps without time zone info.
- and both datetime.now() and datetime.utcnow() seems to be without those bits. Silly.
+ date objects cannot be compared with timestamp objects, neither in
+ python2 nor python3. Silly. also, objects with time zone info
+ cannot be compared with timestamps without time zone info. and
+ both datetime.now() and datetime.utcnow() seems to be without
+ those bits. Silly.
+
+ This method should only be used in comparitions, never when
+ populating fields in an icalendar object. Events with dates
+ rather than timestamps are to be considered as full-day events,
+ so the difference is significant.
"""
if type(t) == date:
t = datetime(t.year, t.month, t.day)
@@ -131,9 +154,25 @@ def _calendar_addics(caldav_conn, ics, uid, args):
raise ValueError("Nothing to do/invalid option combination for 'calendar add'-mode; either both --icalendar and --nocaldav should be set, or none of them")
return
- c = find_calendar(caldav_conn, args)
- c.add_event(ics)
-
+ try:
+ c = find_calendar(caldav_conn, args)
+ if re.search(r'^METHOD:[A-Z]+[\r\n]+',ics,flags=re.MULTILINE) and args.ignoremethod:
+ ics = re.sub(r'^METHOD:[A-Z]+[\r\n]+', '', ics, flags=re.MULTILINE)
+ print ("METHOD property found and ignored")
+ c.add_event(ics)
+ except caldav.lib.error.AuthorizationError:
+ print("Error logging in")
+ sys.exit(2)
+ """
+ Peter Havekes: This needs more checking. It works for me when connecting to O365
+
+ except caldav.lib.error.PutError as e:
+ if "200 OK" in str(e):
+ print("Duplicate")
+ else:
+ raise
+ """
+
def calendar_addics(caldav_conn, args):
"""
Takes an ics from external source and puts it into the calendar.
@@ -158,10 +197,10 @@ def calendar_addics(caldav_conn, args):
## 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:
@@ -176,14 +215,14 @@ def calendar_addics(caldav_conn, args):
def interactive_config(args, config, remaining_argv):
import readline
-
+
new_config = False
section = 'default'
backup = {}
modified = False
-
+
print("Welcome to the interactive calendar configuration mode")
- print("Warning - untested code ahead, raise issues at t-calendar-cli@tobixen.no")
+ print("Warning - untested code ahead, raise issues at t-calendar-cli@tobixen.no or the github repo")
if not config or not hasattr(config, 'keys'):
config = {}
print("No valid existing configuration found")
@@ -206,8 +245,14 @@ def interactive_config(args, config, remaining_argv):
config[section] = {}
for config_key in ('caldav_url', 'caldav_user', 'caldav_pass', 'caldav_proxy', 'ssl_verify_cert', 'language', 'timezone', 'inherits'):
- print("Config option %s - old value: %s" % (config_key, config[section].get(config_key, '(None)')))
- value = raw_input("Enter new value (or just enter to keep the old): ")
+
+ if config_key == 'caldav_pass':
+ print("Config option caldav_pass - old value: **HIDDEN**")
+ value = getpass(prompt="Enter new value (or just enter to keep the old): ")
+ else:
+ print("Config option %s - old value: %s" % (config_key, config[section].get(config_key, '(None)')))
+ value = raw_input("Enter new value (or just enter to keep the old): ")
+
if value:
config[section][config_key] = value
modified = True
@@ -240,7 +285,7 @@ def interactive_config(args, config, remaining_argv):
os.rename(args.config_file, "%s.%s.bak" % (args.config_file, int(time.time())))
with open(args.config_file, 'w') as outfile:
json.dump(config, outfile, indent=4)
-
+
if args.config_section == 'default' and section != 'default':
config['default'] = config[section]
@@ -252,7 +297,7 @@ def create_alarm(message, relative_timedelta):
alarm.add('DESCRIPTION', message)
alarm.add('TRIGGER', relative_timedelta, parameters={'VALUE':'DURATION'})
return alarm
-
+
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))
@@ -262,7 +307,7 @@ def calendar_add(caldav_conn, args):
## read timestamps from arguments
event_spec = args.event_time.split('+')
if len(event_spec)>3:
- raise ValueError('Invalid event time "%s" - can max contain 2 plus-signs' % event_time)
+ raise ValueError('Invalid event time "%s" - can max contain 2 plus-signs' % args.event_time)
elif len(event_spec)==3:
event_time = '%s+%s' % tuple(event_spec[0:2])
event_duration = event_spec[2]
@@ -278,13 +323,19 @@ def calendar_add(caldav_conn, args):
## TODO: error handling
event_duration_secs = int(event_duration[:-1]) * time_units[event_duration[-1:]]
dtstart = dateutil.parser.parse(event_spec[0])
- event.add('dtstart', dtstart)
- ## TODO: handle duration and end-time as options. default 3600s by now.
- event.add('dtend', dtstart + timedelta(0,event_duration_secs))
- ## TODO: what does the cryptic comment here really mean, and why was the dtstamp commented out? dtstamp is required according to the RFC.
- ## not really correct, and it breaks i.e. with google calendar
+ if args.whole_day:
+ if event_spec[1][-1:] != 'd':
+ raise ValueError('Duration of whole-day event must be multiple of 1d')
+ duration = int(event_spec[1][:-1])
+ dtstart = dateutil.parser.parse(event_spec[0])
+ dtend = dtstart + timedelta(days=duration)
+ event.add('dtstart', _date(dtstart.date()))
+ event.add('dtend', _date(dtend.date()))
+ else:
+ event.add('dtstart', dtstart)
+ ## TODO: handle duration and end-time as options. default 3600s by now.
+ event.add('dtend', dtstart + timedelta(0,event_duration_secs))
event.add('dtstamp', _now())
- ## maybe we should generate some uid?
uid = uuid.uuid1()
event.add('uid', str(uid))
for attr in vcal_txt_one + vcal_txt_many:
@@ -339,7 +390,7 @@ def journal_add(caldav_conn, args):
_calendar_addics(caldav_conn, cal.to_ical(), uid, args)
print("Added journal item with uid=%s" % uid)
## FULL STOP - should do some major refactoring before doing more work here!
-
+
def todo_add(caldav_conn, args):
## TODO: copied from calendar_add, should probably be consolidated
if args.icalendar or args.nocaldav:
@@ -372,7 +423,7 @@ def todo_add(caldav_conn, args):
rt.params['RELTYPE']=['CHILD']
rt.value = str(uid)
t.save()
-
+
for attr in vtodo_txt_one:
if attr == 'summary':
continue
@@ -420,7 +471,7 @@ def calendar_agenda(caldav_conn, args):
events = []
if args.icalendar:
for ical in events_:
- print(ical.data)
+ print(ical.data).encode('utf-8').strip()
else:
## flatten. A recurring event may be a list of events.
## jeez ... zimbra and DaviCal does completely different things here
@@ -441,16 +492,29 @@ def calendar_agenda(caldav_conn, args):
events.append({'dtstart': dtstart, 'instance': event})
events.sort(lambda a,b: cmp(a['dtstart'], b['dtstart']))
for event in events:
- event['dstart'] = event['dtstart'].strftime(args.timestamp_format)
event['summary'] = "(no description)"
+ event['dtstart'] = event['dtstart'].strftime(args.timestamp_format)
+ for timeattr in ('dtcreated', 'dtend'):
+ if hasattr(event['instance'], timeattr):
+ event[timeattr] = getattr(event['instance'], timeattr).value
+ if hasattr(event[timeattr], 'strftime'):
+ event[timeattr] = event[timeattr].strftime(args.timestamp_format)
+ else:
+ event[timeattr] = '-'
+ for textattr in vcal_txt_one:
+ if hasattr(event['instance'], textattr):
+ event[textattr] = getattr(event['instance'], textattr).value
+ else:
+ event[textattr] = '-'
for summary_attr in ('summary', 'location', 'description'):
if hasattr(event['instance'], summary_attr):
event['summary'] = getattr(event['instance'], summary_attr).value
break
event['uid'] = event['instance'].uid.value if hasattr(event['instance'], 'uid') else '<no uid>'
## TODO: this will probably break and is probably moot on python3?
- if isinstance(event['summary'], unicode):
- event['summary'] = event['summary'].encode('utf-8')
+ for attr in vcal_txt_one + ['summary']:
+ if isinstance(event[attr], unicode):
+ event[attr] = event[attr].encode('utf-8')
print(args.event_template.format(**event))
def todo_select(caldav_conn, args):
@@ -521,8 +585,8 @@ def todo_edit(caldav_conn, args):
## you may now access task.data to edit the raw ical, or
## task.instance.vtodo to edit a vobject instance
task.save()
-
-
+
+
def todo_postpone(caldav_conn, args):
if args.nocaldav:
raise ValueError("No caldav connection, aborting")
@@ -535,8 +599,8 @@ def todo_postpone(caldav_conn, args):
else:
new_ts = dateutil.parser.parse(args.until)
if not new_ts.time():
- new_ts = new_ts.date()
-
+ new_ts = _date(new_ts)
+
tasks = todo_select(caldav_conn, args)
for task in tasks:
if new_ts:
@@ -553,12 +617,12 @@ def todo_postpone(caldav_conn, args):
if type(task.instance.vtodo.dtstart.value) != type(task.instance.vtodo.due.value):
## RFC states they must be of the same type
if isinstance(task.instance.vtodo.dtstart.value, date):
- task.instance.vtodo.due.value = task.instance.vtodo.due.value.date()
+ task.instance.vtodo.due.value = _date(task.instance.vtodo.due.value)
else:
d = task.instance.vtodo.due.value
task.instance.vtodo.due.value = datetime(d.year, d.month, d.day)
## RFC also states that due cannot be before dtstart (and that makes sense)
- if task.instance.vtodo.dtstart.value > task.instance.vtodo.due.value:
+ if _force_datetime(task.instance.vtodo.dtstart.value, args) > _force_datetime(task.instance.vtodo.due.value, args):
task.instance.vtodo.due.value = task.instance.vtodo.dtstart.value
task.save()
@@ -638,7 +702,7 @@ def todo_complete(caldav_conn, args):
continue
task.complete()
-
+
def todo_delete(caldav_conn, args):
if args.nocaldav:
@@ -646,7 +710,7 @@ def todo_delete(caldav_conn, args):
tasks = todo_select(caldav_conn, args)
for task in tasks:
task.delete()
-
+
def config_section(config, section='default'):
if section in config and 'inherits' in config[section]:
ret = config_section(config, config[section]['inherits'])
@@ -655,13 +719,13 @@ def config_section(config, section='default'):
if section in config:
ret.update(config[section])
return ret
-
+
def main():
"""
the main function does (almost) nothing but parsing command line parameters
"""
## This boilerplate pattern is from
- ## http://stackoverflow.com/questions/3609852
+ ## 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
@@ -732,6 +796,7 @@ def main():
parser.add_argument("--ssl-verify-cert", help="verification of the SSL cert - 'yes' to use the OS-provided CA-bundle, 'no' to trust any cert and the path to a CA-bundle")
parser.add_argument("--debug-logging", help="turn on debug logging", action="store_true")
parser.add_argument("--calendar-url", help="URL for calendar to be used (may be absolute or relative to caldav URL, or just the name of the calendar)")
+ parser.add_argument("--ignoremethod", help="Ignores METHOD property if exists in the request. This violates RFC4791 but is sometimes appended by some calendar servers", action="store_true")
## TODO: check sys.argv[0] to find command
## TODO: set up logging
@@ -754,8 +819,8 @@ def main():
for attr in vtodo_txt_one + vtodo_txt_many:
todo_parser.add_argument('--no'+attr, help="for filtering tasks", action='store_true')
-
- #todo_parser.add_argument('--priority', ....)
+
+ #todo_parser.add_argument('--priority', ....)
#todo_parser.add_argument('--sort-by', ....)
#todo_parser.add_argument('--due-before', ....)
todo_subparsers = todo_parser.add_subparsers(title='tasks subcommand')
@@ -772,12 +837,12 @@ def main():
help="specifies a time at which a reminder should be presented for this task, " \
"relative to the start time of the task (as a timestamp delta)")
todo_add_parser.set_defaults(func=todo_add)
-
+
todo_list_parser = todo_subparsers.add_parser('list')
todo_list_parser.add_argument('--todo-template', help="Template for printing out the event", default="{dtstart}{dtstart_passed_mark} {due}{due_passed_mark} {summary}")
todo_list_parser.add_argument('--default-due', help="Default number of days from a task is submitted until it's considered due", type=int, default=14)
todo_list_parser.add_argument('--list-categories', help="Instead of listing the todo-items, list the unique categories used", action='store_true')
- todo_list_parser.add_argument('--timestamp-format', help="strftime-style format string for the output timestamps", default="%F (%a)")
+ todo_list_parser.add_argument('--timestamp-format', help="strftime-style format string for the output timestamps", default="%Y-%m-%d (%a)")
todo_list_parser.set_defaults(func=todo_list)
todo_edit_parser = todo_subparsers.add_parser('edit')
@@ -789,7 +854,7 @@ def main():
todo_edit_parser.set_defaults(func=todo_edit)
todo_postpone_parser = todo_subparsers.add_parser('postpone')
- todo_postpone_parser.add_argument('until', help="either a new date or +interval to add some interval to the existing time, or i.e. 'in 3d' to set the time to a new time relative to the current time. interval is a number postfixed with a one character unit (any of smhdwy). If the todo-item has a dstart, this field will be modified, else the due timestamp will be modified. If both timestamps exists and dstart will be moved beyond the due time, the due time will be set to dtime.")
+ todo_postpone_parser.add_argument('until', help="either a new date or +interval to add some interval to the existing time, or i.e. 'in 3d' to set the time to a new time relative to the current time. interval is a number postfixed with a one character unit (any of smhdwy). If the todo-item has a dtstart, this field will be modified, else the due timestamp will be modified. If both timestamps exists and dstart will be moved beyond the due time, the due time will be set to dtime.")
todo_postpone_parser.add_argument('--due', help="move the due, not the dtstart", action='store_true')
todo_postpone_parser.set_defaults(func=todo_postpone)
@@ -801,7 +866,7 @@ def main():
## journal
journal_parser = subparsers.add_parser('journal')
- journal_subparsers = journal_parser.add_subparsers(title='tasks subcommand')
+ journal_subparsers = journal_parser.add_subparsers(title='journal subcommand')
journal_add_parser = journal_subparsers.add_parser('add')
journal_add_parser.add_argument('summaryline', nargs='+')
journal_add_parser.set_defaults(func=journal_add)
@@ -812,6 +877,7 @@ 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('summary', nargs='+')
calendar_add_parser.set_defaults(func=calendar_add)
+ calendar_add_parser.add_argument('--whole-day', help='Whole-day event', action='store_true', default=False)
for attr in vcal_txt_one + vcal_txt_many:
calendar_add_parser.add_argument('--set-'+attr, help='Set '+attr)
@@ -825,8 +891,8 @@ def main():
calendar_agenda_parser.add_argument('--to-time', help="Fetch calendar until this timestamp")
calendar_agenda_parser.add_argument('--agenda-mins', help="Fetch calendar for so many minutes", type=int)
calendar_agenda_parser.add_argument('--agenda-days', help="Fetch calendar for so many days", type=int, default=7)
- calendar_agenda_parser.add_argument('--event-template', help="Template for printing out the event", default="{dstart} {summary}")
- calendar_agenda_parser.add_argument('--timestamp-format', help="strftime-style format string for the output timestamps", default="%F %H:%M (%a)")
+ calendar_agenda_parser.add_argument('--event-template', help="Template for printing out the event", default="{dtstart} {summary}")
+ calendar_agenda_parser.add_argument('--timestamp-format', help="strftime-style format string for the output timestamps", default="%Y-%m-%d %H:%M (%a)")
calendar_agenda_parser.set_defaults(func=calendar_agenda)
calendar_delete_parser = calendar_subparsers.add_parser('delete')
@@ -838,9 +904,20 @@ def main():
args = parser.parse_args(remaining_argv)
if not args.nocaldav:
+ if not args.calendar_url:
+ sys.stderr.write("""
+missing mandatory argument calendar_url
+Have you set up a config file? Read the doc or ...
+... use the --interactive-config option to create a config file
+""")
+ sys.exit(1)
caldav_conn = caldav_connect(args)
else:
+
caldav_conn = None
+
+ if args.ssl_verify_cert == 'no':
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
ret = args.func(caldav_conn, args)
diff --git a/tests/script_test.sh b/tests/script_test.sh
index 1636478..a32bc64 100755
--- a/tests/script_test.sh
+++ b/tests/script_test.sh
@@ -23,6 +23,10 @@ calendar_cli() {
[ -z "$output" ] || echo $output
}
+## CLEANUP from earlier failed test runs
+
+for uid in $($calendar_cli calendar agenda --from-time=2010-10-10 --agenda-days=1 --event-template='{uid}') ; do calendar_cli calendar delete --event-uid=$uid ; done
+calendar_cli todo --categories scripttest delete
## TESTING
@@ -36,7 +40,7 @@ echo "## Attempting to add a past event at 2010-10-09 20:00:00, 2 hours duration
calendar_cli calendar add '2010-10-09 20:00:00+2h' 'testing testing'
uid=$(echo $output | perl -ne '/uid=(.*)$/ && print $1')
echo "## Attempting to add a past event at 2010-10-10 20:00:00, CET (1 hour duration is default)"
-calendar_cli calendar add '2010-10-10 20:00:00+01:00' 'testing testing'
+calendar_cli calendar add '2010-10-10 20:00:00+01:00' 'testing testing' --set-description='this is a test calendar event' --set-location='Москва'
uid2=$(echo $output | perl -ne '/uid=(.*)$/ && print $1')
echo "## Attempting to add a past event at 2010-10-11 20:00:00, CET, 3h duration"
calendar_cli calendar add '2010-10-11 20:00:00+01:00+3h' 'testing testing'
@@ -44,11 +48,11 @@ uid3=$(echo $output | perl -ne '/uid=(.*)$/ && print $1')
echo "## OK: Added the event, uid is $uid"
echo "## Taking out the agenda for 2010-10-09 + four days"
-calendar_cli calendar agenda --from-time=2010-10-09 --agenda-days=4
-echo $output | { grep -q 'testing testing' && echo "## OK: found the event" ; } || echo "## FAIL: didn't find the event"
+calendar_cli calendar agenda --from-time=2010-10-09 --agenda-days=4 --event-template='{description} {location}'
+echo $output | { grep -q 'this is a test calendar event Москва' && echo "## OK: found the event" ; } || echo "## FAIL: didn't find the event"
echo "## Taking out the agenda for 2010-10-10, with uid"
-calendar_cli calendar agenda --from-time=2010-10-10 --agenda-days=1 --event-template='{dstart} {uid}'
+calendar_cli calendar agenda --from-time=2010-10-10 --agenda-days=1 --event-template='{dtstart} {uid}'
echo $output | { grep -q $uid2 && echo "## OK: found the UID" ; } || echo "## FAIL: didn't find the UID"
echo "## Deleting events with uid $uid $uid1 $uid2"