From a08e1f28358747576aaf91633141ea6006edeef1 Mon Sep 17 00:00:00 2001 From: Aurelian Shuttleworth Date: Mon, 26 Jan 2026 14:16:01 +0100 Subject: [PATCH 1/2] feat: add support for event transparency (ghost blocks) - Add Transparency handler to details.py - Add --transparency flag to argparsers.py - Update CLI wiring to pass transparency arg - Add regression test --- gcalcli/argparsers.py | 6 ++++++ gcalcli/cli.py | 3 ++- gcalcli/details.py | 7 +++++++ gcalcli/gcal.py | 4 +++- tests/test_gcalcli.py | 20 ++++++++++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index 023a338e..20be7b2d 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -609,6 +609,12 @@ def get_argument_parser(): 'from lavender, sage, grape, flamingo, banana, tangerine, ' 'peacock, graphite, blueberry, basil, tomato.', ) + add.add_argument( + '--transparency', + default='opaque', + choices=['opaque', 'transparent'], + help='Event transparency (free/busy status). Default is opaque (busy).', + ) add.add_argument('--title', default=None, type=str, help='Event title') add.add_argument( '--who', diff --git a/gcalcli/cli.py b/gcalcli/cli.py index 5f7ad1da..1a6d72bc 100755 --- a/gcalcli/cli.py +++ b/gcalcli/cli.py @@ -304,7 +304,8 @@ def main(): gcal.AddEvent(parsed_args.title, parsed_args.where, estart, eend, parsed_args.description, parsed_args.who, - parsed_args.reminders, parsed_args.event_color) + parsed_args.reminders, parsed_args.event_color, + parsed_args.transparency) elif parsed_args.command == 'search': gcal.TextQuery( diff --git a/gcalcli/details.py b/gcalcli/details.py index acc4bac9..8604b2ef 100644 --- a/gcalcli/details.py +++ b/gcalcli/details.py @@ -356,6 +356,12 @@ class ID(SimpleSingleFieldHandler): fieldnames = ['id'] +class Transparency(SimpleSingleFieldHandler): + """Handler for event transparency (free/busy).""" + + fieldnames = ['transparency'] + + class Action(SingleFieldHandler): """Handler specifying event processing during an update.""" @@ -377,6 +383,7 @@ def _get(cls, event): ('calendar', Calendar), ('email', Email), ('attendees', Attendees), + ('transparency', Transparency), ('action', Action)]) HANDLERS_READONLY = {Url, Calendar} diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index c8fb52f0..f1f0ce47 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -1464,12 +1464,14 @@ def QuickAddEvent(self, event_text, reminders=None): return new_event - def AddEvent(self, title, where, start, end, descr, who, reminders, color): + def AddEvent(self, title, where, start, end, descr, who, reminders, color, + transparency='opaque'): calendar = self._prompt_for_calendar(self.cals) event = {} event['summary'] = title + event['transparency'] = transparency if self.options['allday']: event['start'] = {'date': start} diff --git a/tests/test_gcalcli.py b/tests/test_gcalcli.py index c6c4bf9a..c74cde2f 100644 --- a/tests/test_gcalcli.py +++ b/tests/test_gcalcli.py @@ -393,3 +393,23 @@ def test_next_cut(PatchedGCalI): event_title = "樹貞 fun fun fun" assert gcal._next_cut(event_title) == (8, 6) + +def test_add_event_transparency(PatchedGCalI): + cal_names = parse_cal_names(['jcrowgey@uw.edu'], printer=None) + gcal = PatchedGCalI( + cal_names=cal_names, allday=False, default_reminders=True) + assert gcal.AddEvent(title='transparent event', + where='anywhere', + start='now', + end='tomorrow', + descr='testing', + who='anyone', + reminders=None, + color='banana', + transparency='transparent') + + gcal.api_tracker.verify_all_mutating_calls([ + CallMatcher('insert', + body_has_fields={'summary', 'start', 'end', 'transparency'}, + body_fields={'transparency': 'transparent'}) + ]) From 4fb7955e94dd4bd7985001e9a87bc50c248e1054 Mon Sep 17 00:00:00 2001 From: Aurelian Shuttleworth Date: Mon, 26 Jan 2026 14:21:16 +0100 Subject: [PATCH 2/2] refactor: rename --transparency to --availability (free/busy) - Rename CLI flag to --availability - Update choices to free/busy - Map free->transparent and busy->opaque in gcal.py - Rename Transparency handler to Availability and update display logic - Update tests --- flake.lock | 61 +++++++++++++++++++++++++++++++++++++++++++ gcalcli/argparsers.py | 8 +++--- gcalcli/cli.py | 2 +- gcalcli/details.py | 11 +++++--- gcalcli/gcal.py | 4 +-- tests/test_gcalcli.py | 4 +-- 6 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 flake.lock diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..ff6a61ab --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1769330179, + "narHash": "sha256-yxgb4AmkVHY5OOBrC79Vv6EVd4QZEotqv+6jcvA212M=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "48698d12cc10555a4f3e3222d9c669b884a49dfe", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index 20be7b2d..2707842e 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -610,10 +610,10 @@ def get_argument_parser(): 'peacock, graphite, blueberry, basil, tomato.', ) add.add_argument( - '--transparency', - default='opaque', - choices=['opaque', 'transparent'], - help='Event transparency (free/busy status). Default is opaque (busy).', + '--availability', + default='busy', + choices=['free', 'busy'], + help='Event availability (free/busy status). Default is busy.', ) add.add_argument('--title', default=None, type=str, help='Event title') add.add_argument( diff --git a/gcalcli/cli.py b/gcalcli/cli.py index 1a6d72bc..6a566bfe 100755 --- a/gcalcli/cli.py +++ b/gcalcli/cli.py @@ -305,7 +305,7 @@ def main(): gcal.AddEvent(parsed_args.title, parsed_args.where, estart, eend, parsed_args.description, parsed_args.who, parsed_args.reminders, parsed_args.event_color, - parsed_args.transparency) + parsed_args.availability) elif parsed_args.command == 'search': gcal.TextQuery( diff --git a/gcalcli/details.py b/gcalcli/details.py index 8604b2ef..e549f106 100644 --- a/gcalcli/details.py +++ b/gcalcli/details.py @@ -356,11 +356,16 @@ class ID(SimpleSingleFieldHandler): fieldnames = ['id'] -class Transparency(SimpleSingleFieldHandler): - """Handler for event transparency (free/busy).""" +class Availability(SimpleSingleFieldHandler): + """Handler for event availability (free/busy).""" fieldnames = ['transparency'] + @classmethod + def _get(cls, event): + val = event.get(cls.fieldnames[0], 'opaque') + return 'free' if val == 'transparent' else 'busy' + class Action(SingleFieldHandler): """Handler specifying event processing during an update.""" @@ -383,7 +388,7 @@ def _get(cls, event): ('calendar', Calendar), ('email', Email), ('attendees', Attendees), - ('transparency', Transparency), + ('availability', Availability), ('action', Action)]) HANDLERS_READONLY = {Url, Calendar} diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index f1f0ce47..1d1ebe49 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -1465,13 +1465,13 @@ def QuickAddEvent(self, event_text, reminders=None): return new_event def AddEvent(self, title, where, start, end, descr, who, reminders, color, - transparency='opaque'): + availability='busy'): calendar = self._prompt_for_calendar(self.cals) event = {} event['summary'] = title - event['transparency'] = transparency + event['transparency'] = 'transparent' if availability == 'free' else 'opaque' if self.options['allday']: event['start'] = {'date': start} diff --git a/tests/test_gcalcli.py b/tests/test_gcalcli.py index c74cde2f..35a3617f 100644 --- a/tests/test_gcalcli.py +++ b/tests/test_gcalcli.py @@ -394,7 +394,7 @@ def test_next_cut(PatchedGCalI): event_title = "樹貞 fun fun fun" assert gcal._next_cut(event_title) == (8, 6) -def test_add_event_transparency(PatchedGCalI): +def test_add_event_availability(PatchedGCalI): cal_names = parse_cal_names(['jcrowgey@uw.edu'], printer=None) gcal = PatchedGCalI( cal_names=cal_names, allday=False, default_reminders=True) @@ -406,7 +406,7 @@ def test_add_event_transparency(PatchedGCalI): who='anyone', reminders=None, color='banana', - transparency='transparent') + availability='free') gcal.api_tracker.verify_all_mutating_calls([ CallMatcher('insert',