From fc334d9fbd4493e411568e66660fc131a3f69d88 Mon Sep 17 00:00:00 2001 From: Michael Hoffman Date: Wed, 25 Oct 2023 14:23:05 -0400 Subject: [PATCH 1/2] feat: add `agenda --tsv --details length` --- gcalcli/details.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/gcalcli/details.py b/gcalcli/details.py index 26a660c..fdcd193 100644 --- a/gcalcli/details.py +++ b/gcalcli/details.py @@ -124,6 +124,20 @@ def patch(cls, cal, event, fieldname, value): instant['timeZone'] = cal['timeZone'] +class Length(Handler): + """Handler for event duration.""" + + fieldnames = ['length'] + + @classmethod + def get(cls, event): + return [str(event['e'] - event['s'])] + + @classmethod + def patch(cls, cal, event, fieldname, value): + raise NotImplementedError + + class Url(Handler): """Handler for HTML and legacy Hangout links.""" @@ -259,6 +273,7 @@ def _get(cls, event): HANDLERS = OrderedDict([('id', ID), ('time', Time), + ('length', Length), ('url', Url), ('conference', Conference), ('title', Title), @@ -279,8 +294,7 @@ def _get(cls, event): in FIELD_HANDLERS.items() if handler in HANDLERS_READONLY) -_DETAILS_WITHOUT_HANDLERS = ['length', 'reminders', 'attendees', - 'attachments', 'end'] +_DETAILS_WITHOUT_HANDLERS = ['reminders', 'attendees', 'attachments', 'end'] DETAILS = list(HANDLERS.keys()) + _DETAILS_WITHOUT_HANDLERS DETAILS_DEFAULT = {'time', 'title'} From fbaf926c54a9d34ed6c1fa7f7a2855b60e52fc7d Mon Sep 17 00:00:00 2001 From: Michael Hoffman Date: Sun, 17 Nov 2024 14:46:31 -0500 Subject: [PATCH 2/2] feat: `agendaupdate`: add `length` detail --- gcalcli/actions.py | 19 +++++++++++++++++++ gcalcli/details.py | 19 ++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/gcalcli/actions.py b/gcalcli/actions.py index 8fbd69e..edec275 100644 --- a/gcalcli/actions.py +++ b/gcalcli/actions.py @@ -12,6 +12,21 @@ def _iter_field_handlers(row): yield fieldname, handler, value +def _check_writable_fields(row): + """Check no potentially conflicting fields for a writing action.""" + keys = row.keys() + + # XXX: instead of preventing use of end_date/end_time and length in the + # same input, use successively complex conflict resolution plans: + # + # 1. allow it as long as they don't conflict by row + # 2. conflict resolution by option + # 3. conflict resolution interactively + + if 'length' in keys and ('end_date' in keys or 'end_time' in keys): + raise NotImplementedError + + def patch(row, cal, interface): """Patch event with new data.""" event_id = row['id'] @@ -22,6 +37,8 @@ def patch(row, cal, interface): mod_event = {} cal_id = cal['id'] + _check_writable_fields(row) + for fieldname, handler, value in _iter_field_handlers(row): if fieldname in FIELDNAMES_READONLY: # Instead of changing mod_event, the Handler.patch() for @@ -56,6 +73,8 @@ def insert(row, cal, interface): event = {} cal_id = cal['id'] + _check_writable_fields(row) + for fieldname, handler, value in _iter_field_handlers(row): if fieldname in FIELDNAMES_READONLY: raise ReadonlyError("Cannot specify value on insert.") diff --git a/gcalcli/details.py b/gcalcli/details.py index fdcd193..a8be0e4 100644 --- a/gcalcli/details.py +++ b/gcalcli/details.py @@ -7,7 +7,7 @@ from dateutil.parser import isoparse, parse from .exceptions import ReadonlyCheckError, ReadonlyError -from .utils import is_all_day +from .utils import get_timedelta_from_str, is_all_day FMT_DATE = '%Y-%m-%d' FMT_TIME = '%H:%M' @@ -124,7 +124,7 @@ def patch(cls, cal, event, fieldname, value): instant['timeZone'] = cal['timeZone'] -class Length(Handler): +class Length(Time): """Handler for event duration.""" fieldnames = ['length'] @@ -135,7 +135,20 @@ def get(cls, event): @classmethod def patch(cls, cal, event, fieldname, value): - raise NotImplementedError + # start_date and start_time must be an earlier TSV field than length + start = event['start'] + end = event['end'] = {} + + if start['date']: + # XXX: handle all-day events + raise NotImplementedError + + start_datetime = isoparse(start['dateTime']) + end_datetime = start_datetime + get_timedelta_from_str(value) + + end['date'] = None # clear all-day date, for good measure + end['dateTime'] = end_datetime.isoformat() + end['timeZone'] = cal['timeZone'] class Url(Handler):