Implement #550. Includes BREAKING CHANGES. Perhaps next release should increase major version number. * feat!: add header row for `gcalcli agenda --tsv` BREAKING CHANGE: having a header row will break any software that assumes that the output of `gcalcli agenda --tsv` doesn't have one. Also slightly refactored the output printing to use print() like in most parts of the code. * feat!: gcal agenda --tsv prints empty time fields for all-day events BREAKING CHANGE: this changes the previous behavior which was to depict all-day events as starting and ending at midnight. * feat: add `--detail id` for `gcalcli agenda --tsv` * feat: add agendaupdate for title, id, location, description argparsers.py, cli.py: add `agendaupdate` subcommand gcal.py: add `AgendaUpdate()` details.py: - `Title`, `SimpleSingleFieldHandler`: add `patch()` - add `FIELD_HANDLERS` * feat: add agendaupdate for time Round trips don't work for all day events. The current output format is to present them as events starting and ending at midnight of the same day. But this keeps us from having events starting and ending at midnight of the same day. In the next commit I plan to change the output for all day events so the time fields are blank. Also: - change signature of `Handler.patch()` to include `cal`, which is necessary to extract the timezone - change default of `Handler.fieldnames` from `None` to `[]` to silence a mypy warning. * feat: add detail for `action` for `gcalcli agenda`. Column is ignored by `gcalcli agendaupdate`. * feat: add agendaupdate action dispatch and `patch` action Refactor most of existing agendaupdate into new module `gcalcli.actions` in `patch()`. * feat: add agendaupdate for calendar This treats calendar as a readonly field. Actually moving calendar would be a bit more involved, and would necessitate: - changing agendaupdate from its current one-calendar-only restriction - using the events().move() method to do the move. It's not done by events().patch() That can be a future feature. * feat: add agendaupdate `ignore` action * feat: add agendaupdate `delete` action * feat: add agendaupdate `insert` action * feat: `agendaupdate` patch action uses insert if no id supplied * feat: error when unsupported agendaupdate action used Also limits actions to a defined set. Before one could have specified action to be name of some other object in `gcalcli.actions` with unpredictable results. * feat: add agendaupdate for url Both these fields are read-only. If html_link is in the input, always fail. If hangout_link is in the input, check against the current value. If they don't match, fail. * feat: add agendaupdate for conference Partially implements #522. * fix: change SimpleSingleColumnHandler.header to a simple list Can't use a property on a classmethod without making a custom metaclass. Without that, the property is only enacted upon class instantiation, which we aren't doing here. A custom metaclass is too magic, and thus far we have avoided instantiating what would be singleton objects. Also changed `SimpleSingleColumnHandler._get()` to use `cls.header[0]` instead of `cls._key` which is removed. * fix: gcalcli agenda to work with refactor 72710b4 * refactor: move _tsv() implementation to gcalcli.details Refactor `gcalcli.gcal.GoogleCalendarInterface._tsv()`, replacing the long list of if statements with a modular design using a series of `gcalcli.details.Handler` classes. Each `Handler` represents the set of event properties that are captured by each item of `gcalcli.argparsers.DETAILS`. So far only the `header` class attribute and the `get()` methods are implemented. This is what is necessary for TSV output. * refactor: derive `argparsers.DETAILS` from `details.HANDLERS` Set `gcalcli.argparsers.DETAILS` to a superset of `gcalcli.details.HANDLERS.keys()` to avoid duplication. * refactor: change `header` to `fieldnames` and `column` to `field` * refactor: change `Handler.patch()` to include `fieldname` argument This will enable `Handler`s with multiple fields to work for patching. Necessary for `Time.patch()` and `Conference.patch()` Also introduce `SingleFieldHandler.patch(cls,` ...`)` which dispatches to `cls._patch()`. * refactor: move gcal.GoogleCalendarInterface._isallday() to utils.is_all_day() This enables its use by details.py. * refactor: `details.Url` uses `details.URL_PROPS` to identify properties to get The new `details.URL_PROPS` constant `OrderedDict` will also work for use in `Url.patch()`, which should be implemented in the next commit. This will allow automated handling of two property/fieldname pairs for this `Handler`. * refactor: add `exceptions.ReadonlyError`, `exceptions.ReadonlyCheckError` * refactor: add `gcal.GoogleCalendarInterface.get_events()` change previous uses of `get_cal_service().events()` to `get_events()` * refactor: add `gcal.GoogleCalendarInterface.delete()` * refactor: add gcalcli.actions._iter_field_handlers() * style: add extra line of whitespace * ci: eliminate flake8-docstrings method docstring checks for details.py These are mostly overriding pure virtual classes and adding a docstring to each one would be superfluous and make the code harder to read.