summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Brox <tobias@redpill-linpro.com>2021-12-30 03:52:25 +0100
committerTobias Brox <tobias@redpill-linpro.com>2021-12-30 03:52:25 +0100
commit46f66948473dc0b0ef98525e03bdf73d43992b1d (patch)
tree74e3b2302579d0e62b39b9ff53f4785ff989660d
parent64892380606729b022daeadd5d7b82b4795964a3 (diff)
downloadcalendar-cli-46f66948473dc0b0ef98525e03bdf73d43992b1d.zip
working on the new kal interface
-rw-r--r--NEXT_LEVEL.md50
-rw-r--r--README.md9
-rw-r--r--cal.py107
-rwxr-xr-xcalendar-cli.py2
-rw-r--r--setup.py7
-rw-r--r--tests/_setup_alias18
-rwxr-xr-xtests/test_calendar-cli.sh3
-rwxr-xr-xtests/tests.sh21
8 files changed, 176 insertions, 41 deletions
diff --git a/NEXT_LEVEL.md b/NEXT_LEVEL.md
index bc193c2..2ed6c62 100644
--- a/NEXT_LEVEL.md
+++ b/NEXT_LEVEL.md
@@ -66,22 +66,28 @@ A calendar event could be "striked-out" if it has a child VJOURNAL entry. A tas
### General thoughts
-* calendar-cli should be a simple command line wrapper over some python library. Logic that may be useful for python programmers should either be split out into a new calendar library ... or perhaps pushed to the caldav-library.
-* Old calendar-cli stays, for backward compatibility
-* Consider new tools, less reliance on "tool subcommand subcommand subcommand ..." - at the other hand, I'm fairly happy with the todo interface for listing/editing/completing. First options to filter/sort/limit the todo item(s), then a subcommand for what to do on those items.
-* Consider using the click framework
+* calendar-cli should be a simple command line wrapper over existing python libraries. It should not contain a lot of logic besides parsing command line options and arguments. Logic that may be useful for python programmers should be pushed to other libraries, like the caldav-library, or be split into a new library.
+* Old calendar-cli stays for quite some time, for backward compatibility
+* We'll split out a new command for the new interface, probably it will be `kal`. I considered a lot of different name options:
+ * cal ... but `cal(1)` - is a well-established command, so it's out of the question (unless we overload the command by making a wrapper calling `cal` if no subcommand is given ... that could be an idea).
+ * `calcli`, `cal-cli` and different variants of it. The dash already hurt me with calendar-cli, don't want to repeat that. And "cli" is a bit redundant, when typing a command on the command line it's reasonable to assume it's a cli.
+ * `cal-add`, or even `cal-add-todo` ... but modern cli frameworks are more often than not built over different variants of `command subcommand subsubcommand`.
+ * `kal` ... looks a bit like a silly misspelling or an attempt to localize it into some non-english language (which then will look silly when combined with english subcommands and english options). At the other hand, I do see that it's not entirely uncommon to use `klass` when putting a class into a variable in python, `kal` is short to type, easy to remember, and there aren't too many other projects utilizing the `kal`-name as far as I can see.
+* We'll use click, which is supposed to support subcommands in an elegant way.
+* I'm fairly happy with the todo interface for listing/editing/completing. First options to filter/sort/limit the todo item(s), then a subcommand for what to do on those items.
* Should consider that we at some point may want to support tools that doesn't support caldav. Should als oconsider that we may want to support tools that doesn't support icalendar. For instance, issue tracking through gitlab or github.
* Perhaps a new calendar-tui for increased interactivity?
+* Some or all of the commands should be possible to iterate over several calendars. This is almost impossible with `calendar-cli` due to the way the configuration is made (perhaps we should require that connection parameters are given in a config file?)
### Details
Add an event, task or journal entry:
```
-cal-add-event --config-section=private_calendar --set-location="Aker Brygge marina" "new years party" 2032-12-31T20:00+5h
-cal-add-todo "Buy food for the new years party" --set-due=2032-12-30T20:00 --set-duration=1h
+kal add event --config-section=private_calendar --set-location="Aker Brygge marina" "new years party" 2032-12-31T20:00+5h
+kal add todo "Buy food for the new years party" --set-due=2032-12-30T20:00 --set-duration=1h
-cal-add-journal "Captain's log" 2020-12-04 'Started from Świnoujście a bit after 03AM. Due to miscommunication, bad planning and language problems my crew member Bartek managed to throw the whole moring rope to the sea (clearly the captains fault - I didnt explain the task "release one end of the rope, let it go into the sea and pull it in" well enough, and he did ask "are you really sure about that?" before throwing both ends of the rope to the sea). Tail wind, between 8-16 knots relative windspeed, changed a bit between broad reach and butterfly. While going butterfly, due to a rather big wave we had an accidental jib, bad enough that the preventer rope broke off the cleat it was attached to (but luckily no damanges to the jib). There seems to be a minor salt water leakage by the rudder. Passed Falsterbo around 21, moored up in the guest harbour in Skanör around 22. Very quiet as it was way outside the season. Didnt find any obvious choice on the payment automat for harbor fee - eventually I payed SEK 100 for "tvättstuga". I got no access to the laundry room, so I decided that 100 SEK was an OK price for staying overnight with electricity off-season in Skanör.'
+kal add journal "Captain's log" 2020-12-04 'Started from Świnoujście a bit after 03AM. Due to miscommunication, bad planning and language problems my crew member Bartek managed to throw the whole mooring rope to the sea (clearly the captains fault - I didnt explain the task "release one end of the rope, let it go into the sea and then pull it in" well enough, and he did ask "are you really sure about that?" before throwing both ends of the rope to the sea). Tail wind, between 8-16 knots relative windspeed, changed a bit between broad reach and butterfly. While going butterfly, due to a rather big wave we had an accidental jib, bad enough that the preventer rope broke off the cleat it was attached to (but luckily no damanges to the rig). There seems to be a minor salt water leakage by the rudder. Passed Falsterbo around 21, moored up in the guest harbour in Skanör around 22. Very quiet as it was way outside the season. Didnt find any obvious choice on the payment automat for harbor fee - eventually I payed SEK 100 for "tvättstuga". I got no access to the laundry room, so I decided that 100 SEK was an OK price for staying overnight with electricity off-season in Skanör.'
```
While the specification for journal, todo and event are fairly similar, the non-optional parameters will be different due to slightly different typical use-case scenarios:
@@ -93,30 +99,30 @@ While the specification for journal, todo and event are fairly similar, the non-
The todo-item added above has both due timestamp and duration set, which is against the RFC. It will be converted to dtstart and due before it's pushed to the calendar.
-To get things out from the calendar, one can use the cal-agenda command.
+To get things out from the calendar, one can use the kal agenda command.
```
-cal-agenda --config-section=private --config-section=work --agenda-days=30 --event-template="{dtstart} {summary} UID={uid}" --todo-template="{due} {summary} UID={uid}"
+kal agenda --config-section=private --config-section=work --agenda-days=30 --event-template="{dtstart} {summary} UID={uid}" --todo-template="{due} {summary} UID={uid}"
```
-cal-agenda should first print out all events starting or ending within the next 30 days, then all tasks with dtstart or due within the next 30 days. (in my revised "task management" above, dtstart is defined as due minus estimated work time). The tasks should be "smart sorted", according to the algorithm given in the "Task management" section above ("based on the ratio between duration and available time until due date"). It should accept several --config-section, take out all it can find from those config sections and sort the things afterwards. Exceptions due to unreachable caldav servers or calendars not supporting tasks etc should be caught and presented nicely.
+kal agenda should first print out all events starting or ending within the next 30 days, then all tasks with dtstart or due within the next 30 days. (in my revised "task management" above, dtstart is defined as due minus estimated work time). The tasks should be "smart sorted", according to the algorithm given in the "Task management" section above ("based on the ratio between duration and available time until due date"). It should accept several --config-section, take out all it can find from those config sections and sort the things afterwards. Exceptions due to unreachable caldav servers or calendars not supporting tasks etc should be caught and presented nicely.
-The cal-agenda is to be considered a convenience-command, it is slightly redundant. The output of the command should be considered to be for direct human consumption, no further processing of the output should be done. The cal-select command is the ultimate tool for a lot of things:
+The kal agenda is to be considered a convenience-command, it is slightly redundant. The output of the command should be considered to be for direct human consumption, no further processing of the output should be done. The kal select command is the ultimate tool for a lot of things:
```
-cal-select --timespan=2021-12-01+2w list
-cal-select --todo --nocategories --list
-cal-select --todo --nocategories -1 edit --add-category=keyboard
-cal-select --todo --due-before=2021-12-01 --categories=keyboard --smart-sort list
-cal-select --todo --due-before=2021-12-01 --categories=keyboard --smart-sort -1 complete
-cal-select --uid=e71a6180-45a2-11ec-9605-fa163e7cfdd5 delete
-cal-select --due-before=2021-12-24T15:00 --categories=housework calculate-panic-time --work-factor=0.125
-cal-select --journal --dtstart-after=2021-10-01 --dtstart-before=2021-11-01 sum_hours
+kal select --timespan=2021-12-01+2w list
+kal select --todo --nocategories --list
+kal select --todo --nocategories -1 edit --add-category=keyboard
+kal select --todo --due-before=2021-12-01 --categories=keyboard --smart-sort list
+kal select --todo --due-before=2021-12-01 --categories=keyboard --smart-sort -1 complete
+kal select --uid=e71a6180-45a2-11ec-9605-fa163e7cfdd5 delete
+kal select --due-before=2021-12-24T15:00 --categories=housework calculate-panic-time --work-factor=0.125
+kal select --journal --dtstart-after=2021-10-01 --dtstart-before=2021-11-01 sum_hours
```
-cal-select should select objects based on some criterias and then perform some action (`list`, `edit`, `postpone`, `complete`, `delete`, `calculate-panic-time`, 'sum_hours' and some more - see further below) on the objects.
+kal select should select objects based on some criterias and then perform some action (`list`, `edit`, `postpone`, `complete`, `delete`, `calculate-panic-time`, 'sum_hours' and some more - see further below) on the objects.
-The technical differences between tasks, events and journal entries are small - cal-select should basically work on all three of them unless `--todo`, `--event` or `--journal` is epxlicitly given. If the action given does not match one or more of the objects selected (say, "completing" a journal does not make sense), the script should raise an exception before doing any modifications of the calendar. `--offset` and `--limit` may be used to specify a handful of objects. "-1" is shortform for "--limit 1", or typically "do this action with the top item at the list"
+The technical differences between tasks, events and journal entries are small - kal select should basically work on all three of them unless `--todo`, `--event` or `--journal` is epxlicitly given. If the action given does not match one or more of the objects selected (say, "completing" a journal does not make sense), the script should raise an exception before doing any modifications of the calendar. `--offset` and `--limit` may be used to specify a handful of objects. "-1" is shortform for "--limit 1", or typically "do this action with the top item at the list"
`--smart-sort` will give the above mentioned sort algorithm for tasks, and regular sorting by dtstart for events and journals.
@@ -132,7 +138,7 @@ sum_hours will sum the duration of all objects. Journal entries cannot have dur
The `pin` subcommand will "pin" one or more todo-items to some specific time on the calendar. Duration will be copied. The tasks will be serialized. If there are conflicting events in the same calendar, the tasks will be put after the conflicting events. No checks will be done to ensure that the tasks ends up within ordinary working hours, outside the night hours or before the due date. Or perhaps some sanity checks should be done ... it will be a lot of cleanup-work to be done if one accidentally forgets "-1" and adds some hundreds of items to the calendar ...
-```cal-select --todo --categories=housework --smart-sort --limit=3 pin '2021-12-02 16:00'
+```kal select --todo --categories=housework --smart-sort --limit=3 pin '2021-12-02 16:00'
### Time tracking
diff --git a/README.md b/README.md
index 6d37344..085a719 100644
--- a/README.md
+++ b/README.md
@@ -10,12 +10,19 @@ There is another project out there, "Command-line Interface for Google Calendar"
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, just client-side operations.
+New vs old interface
+--------------------
+
+calendar-cli.py is the old interface, it will hang around and be supported for some time to come. cal.py is the new interface, but until version 1.0 is ready, there will still be functionality in calendar-cli that isn't mirrored to cal.py.
+
+I wanted a short and easy command name, since `cal(1)` is already a popular Unix command, I'm considering to install it into /usr/bin with the name `kal`, but I'm a bit uncertain (seems either like a bad typo or an attempt on localizing the command into my native language?)
+
Usage examples
--------------
The commands and options will be described further down, however examples often beats documentation.
-First, check the tests folder - the file tests.sh shows some basic usage examples. If you have radicale installed (`sudo pip install radicale`), you can try executing test_calendar-cli.sh in that folder, it basically sets up a temporary radicale server and executes the tests.sh towards that server. If test_calendar-cli.sh breaks then _please_ raise an issue on the github or try to reach out through other channels.
+First, check the tests folder - the file tests.sh shows some basic usage examples. If you have radicale installed (`sudo pip install radicale`), you can try executing test_calendar-cli.sh in the test folder, it basically sets up a temporary radicale server and executes the tests.sh towards that server. If test_calendar-cli.sh breaks then _please_ raise an issue on the github or try to reach out through other channels.
In the examples folder there is a script I was using on a regular basis for task management for a while.
diff --git a/cal.py b/cal.py
new file mode 100644
index 0000000..77e7c94
--- /dev/null
+++ b/cal.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+
+"""https://github.com/tobixen/calendar-cli/ - high-level cli against caldav servers.
+
+Copyright (C) 2013-2021 Tobias Brox and other contributors.
+
+See https://www.gnu.org/licenses/gpl-3.0.en.html for license information.
+
+This is a new cli to be fully released in version 1.0, until then
+quite much functionality will only be available through the legacy
+calendar-cli
+"""
+
+from calendar_cli import __version__
+
+import click
+import os
+import caldav
+
+## should make some subclasses of click.ParamType:
+
+## class DateOrDateTime - perhaps a subclass of click.DateTime, returns date
+## if no time is given (can probably just be subclassed directly from
+## click.DateTime?
+
+## class DurationOrDateTime - perhaps a subclass of the above, should attempt
+## to use pytimeparse if the given info is not a datetime.
+
+## See https://click.palletsprojects.com/en/8.0.x/api/#click.ParamType and
+## /usr/lib/*/site-packages/click/types.py on how to do this.
+
+@click.group()
+## TODO
+#@click.option('-c', '--config-file', type=click.File("rb"), default=f"{os.environ['HOME']}/.config/calendar.conf")
+#@click.option('--config-section', default="default")
+@click.option('--caldav-url', help="Full URL to the caldav server", metavar='URL')
+@click.option('--caldav-username', '--caldav-user', help="Full URL to the caldav server", metavar='URL')
+@click.option('--caldav-password', '--caldav-pass', help="Full URL to the caldav server", metavar='URL')
+@click.option('--calendar-url', help="Calendar id, path or URL", metavar='cal', multiple=True)
+@click.option('--calendar-name', help="Calendar name", metavar='cal', multiple=True)
+@click.pass_context
+def cli(ctx, **kwargs):
+ """
+ CalDAV Command Line Interface
+ """
+
+ ## The cli function will prepare a context object, a dict containing the
+ ## caldav_client, principal and calendar
+
+ ctx.ensure_object(dict)
+ ## TODO: add all relevant connection parameters for the DAVClient as options
+ ## TODO: logic to read the config file and edit kwargs from config file
+ ## TODO: delayed communication with caldav server (i.e. if --help is given to subcommand)
+ ## TODO: catch errors, present nice error messages
+ conn_params = {}
+ for k in kwargs:
+ if k.startswith('caldav_'):
+ conn_params[k[7:]] = kwargs[k]
+ client = caldav.DAVClient(**conn_params)
+ principal = client.principal()
+ calendars = []
+ for calendar_url in kwargs['calendar_url']:
+ calendars.append(principal.calendar(cal_id=calendar_url))
+ for calendar_name in kwargs['calendar_name']:
+ calendars.append(principal.calendar(name=calendar_name))
+ if not calendars:
+ calendars = principal.calendars()
+ ctx.obj['client'] = client
+ ctx.obj['principal'] = principal
+ ctx.obj['calendars'] = calendars
+
+@cli.group()
+@click.option('-l', '--add-ical-line', multiple=True)
+@click.pass_context
+def add(ctx, add_ical_line):
+ click.echo("working on something here")
+
+@cli.command()
+@click.pass_context
+def test(ctx):
+ """
+ Will test that we can connect to the caldav server and find the calendars.
+ """
+ print("Seems like everything is OK")
+
+
+@add.command()
+def ical():
+ click.echo("soon you should be able to add ical data to your calendar")
+ raise NotImplementedError("foo")
+
+@add.command()
+def todo():
+ click.echo("soon you should be able to add tasks to your calendar")
+ raise NotImplementedError("foo")
+
+@add.command()
+def event():
+ click.echo("soon you should be able to add events to your calendar")
+ raise NotImplementedError("foo")
+
+def journal():
+ click.echo("soon you should be able to add journal entries to your calendar")
+ raise NotImplementedError("foo")
+
+if __name__ == '__main__':
+ cli()
diff --git a/calendar-cli.py b/calendar-cli.py
index 5bd0990..9c41657 100755
--- a/calendar-cli.py
+++ b/calendar-cli.py
@@ -3,6 +3,8 @@
"""
https://github.com/tobixen/calendar-cli/ - high-level cli against caldav servers.
+This is the legacy calendar-cli. A new interface is being built in cli.py
+
Copyright (C) 2013-2021 Tobias Brox and other contributors.
See https://www.gnu.org/licenses/gpl-3.0.en.html for license information.
diff --git a/setup.py b/setup.py
index 0d097c1..dbd6bd6 100644
--- a/setup.py
+++ b/setup.py
@@ -29,12 +29,19 @@ setup(
"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries",
],
scripts=['calendar-cli.py', 'calendar-cli'],
+ py_modules=['cal'],
install_requires=[
'icalendar',
'caldav>=0.8.1',
'pytz', ## pytz is supposed to be obsoleted, but see https://github.com/collective/icalendar/issues/333
'tzlocal',
+ 'Click',
'six'
],
+ entry_points={
+ 'console_scripts': [
+ 'kal = cal:cli',
+ ],
+ },
**metadata
)
diff --git a/tests/_setup_alias b/tests/_setup_alias
new file mode 100644
index 0000000..45a267c
--- /dev/null
+++ b/tests/_setup_alias
@@ -0,0 +1,18 @@
+error() {
+ echo "$1"
+ echo "sleeping 3"
+ sleep 3
+ exit 255
+}
+
+[ -z "$calendar_cli" ] && [ -x ./calendar-cli.py ] && calendar_cli=./calendar-cli.py
+[ -z "$calendar_cli" ] && [ -x ../calendar-cli.py ] && calendar_cli=../calendar-cli.py
+[ -z "$calendar_cli" ] && error "couldn't find ./calendar_cli.py nor ../calendar_cli.py"
+
+calendar_cli() {
+ echo " $calendar_cli $@"
+ output=$($calendar_cli "$@")
+ [ -z "$output" ] || echo $output
+}
+
+## TODO: new kal command aka cal.py \ No newline at end of file
diff --git a/tests/test_calendar-cli.sh b/tests/test_calendar-cli.sh
index f986f12..6d22973 100755
--- a/tests/test_calendar-cli.sh
+++ b/tests/test_calendar-cli.sh
@@ -12,6 +12,7 @@ if [ -n "$radicale_pid" ]
then
echo "## Radicale now running on pid $radicale_pid"
calendar_cli="../calendar-cli --caldav-url=http://localhost:5232/ --caldav-user=testuser --calendar-url=/testuser/calendar-cli-test-calendar"
+ kal="../cal.py --caldav-url=http://localhost:5232/ --caldav-user=testuser --calendar-url=/testuser/calendar-cli-test-calendar"
echo "## Creating a calendar"
$calendar_cli calendar create calendar-cli-test-calendar
@@ -30,8 +31,6 @@ fi
echo "########################################################################"
echo "## XANDIKOS"
echo "########################################################################"
-echo "Not supported at the moment (xandikos dev decided to return a 500 telling that expand doesn't work when I prodded them that it didn't work. should maybe try to create a workaround)"
-exit 0
xandikos_bin=$(which xandikos 2> /dev/null)
if [ -n "$xandikos_bin" ]
then
diff --git a/tests/tests.sh b/tests/tests.sh
index 5b6b1af..f519447 100755
--- a/tests/tests.sh
+++ b/tests/tests.sh
@@ -6,22 +6,11 @@ set -e
## SETUP
########################################################################
-error() {
- echo "$1"
- echo "sleeping 3"
- sleep 3
- exit 255
-}
-
-[ -z "$calendar_cli" ] && [ -x ./calendar-cli.py ] && calendar_cli=./calendar-cli.py
-[ -z "$calendar_cli" ] && [ -x ../calendar-cli.py ] && calendar_cli=../calendar-cli.py
-[ -z "$calendar_cli" ] && error "couldn't find ./calendar_cli.py nor ../calendar_cli.py"
-
-calendar_cli() {
- echo " $calendar_cli $@"
- output=$($calendar_cli "$@")
- [ -z "$output" ] || echo $output
-}
+for path in . .. ./tests ../tests
+do
+ setup="$path/_setup_alias"
+ [ -x $setup ] && source $setup
+done
echo "## CLEANUP from earlier failed test runs"