Put reminders on the plugin surface, with a service each to create, change, move, complete and reopen them (#331) #1080

Merged
tiagoagueda merged 1 commit from 331-reminder-services into main 2026-10-29 18:31:25 +00:00
Owner

A plugin keeping a CalDAV task list in step (postulo-dav#12) could not reach Reminder,
and Postulo itself made, ticked and unticked reminders by hand in a dozen places.

  • create_reminder, change_reminder, postpone_reminder (now with an actor),
    complete_reminder and reopen_reminder in applications.services, each taking an
    actor. Another person's application is a ValueError, the summary is cut to 250
    characters, a date means nine in the morning in the owner's zone (outside any request
    too), complete_reminder refuses a moment in the future, and reopening does not
    announce the reminder again.
  • add_reminder makes a new entry through create_reminder (and complete_reminder when
    it arrives done): the uid-keyed, source-guarded way in is layered over the one general
    act, not a second writer beside it. It takes an optional actor, which the calendar
    import now passes.
  • Every caller moved onto them: the create and edit forms, Done, Snooze, the API's add,
    change and complete, the interview, appointment and offer paths (the offer path no longer
    clears done_at by hand). The importer and the demo seed stay as they were; a test
    guards the rest of the tree.
  • Decision 1 (a): Reminder.actor names who last touched it when it was not the person,
    drawn beside it as "via ..." on the calendar and an application's page, on the API's
    ReminderOut, and in the archive (format 77).
  • Decision 3 (b): Not done in a done reminder's menu, and
    POST /api/v1/reminders/{id}/reopen.
  • Reminder and the five services are on api.__all__ and _ELSEWHERE; change_status
    stays off.

Closes #331

Notes for review

  • Five services: create_reminder, change_reminder, postpone_reminder, complete_reminder and reopen_reminder. Each takes an actor, and they are on the plugin surface with Reminder.
    • add_reminder (#887) is now a thin caller of create_reminder.
    • Every caller is moved onto them. A guard test fails on any reminder made or completed by hand outside the services, the importer and the demo seed.
  • The maintainer's decisions (the issue's recommendations):
    • a Reminder.actor column, shown as "via …";
    • change_status stays off the surface;
    • reopen from the page (Not done) and the API (POST /api/v1/reminders/{id}/reopen);
    • reopening doesn't announce again.
  • Behaviour changes worth a look:
    • Editing a done reminder's time is refused on the form ("It is already done."), where before it was saved silently.
    • A moved appointment no longer moves a done reminder.
    • The offer's answer-by reminder falls due at 09:00 in the owner's zone, not the active zone.
  • Archive format: 77, plus migration applications 0021_reminder_actor. #878 (PR #1061) also takes 77, so whichever lands second takes 78.

🤖 Generated with Claude Code

A plugin keeping a CalDAV task list in step (postulo-dav#12) could not reach `Reminder`, and Postulo itself made, ticked and unticked reminders by hand in a dozen places. - `create_reminder`, `change_reminder`, `postpone_reminder` (now with an actor), `complete_reminder` and `reopen_reminder` in `applications.services`, each taking an `actor`. Another person's application is a ValueError, the summary is cut to 250 characters, a date means nine in the morning in the owner's zone (outside any request too), `complete_reminder` refuses a moment in the future, and reopening does not announce the reminder again. - `add_reminder` makes a new entry through `create_reminder` (and `complete_reminder` when it arrives done): the uid-keyed, source-guarded way in is layered over the one general act, not a second writer beside it. It takes an optional `actor`, which the calendar import now passes. - Every caller moved onto them: the create and edit forms, Done, Snooze, the API's add, change and complete, the interview, appointment and offer paths (the offer path no longer clears `done_at` by hand). The importer and the demo seed stay as they were; a test guards the rest of the tree. - Decision 1 (a): `Reminder.actor` names who last touched it when it was not the person, drawn beside it as "via ..." on the calendar and an application's page, on the API's `ReminderOut`, and in the archive (format 77). - Decision 3 (b): *Not done* in a done reminder's menu, and `POST /api/v1/reminders/{id}/reopen`. - `Reminder` and the five services are on `api.__all__` and `_ELSEWHERE`; `change_status` stays off. Closes #331 ## Notes for review - **Five services:** `create_reminder`, `change_reminder`, `postpone_reminder`, `complete_reminder` and `reopen_reminder`. Each takes an `actor`, and they are on the plugin surface with `Reminder`. - `add_reminder` (#887) is now a thin caller of `create_reminder`. - Every caller is moved onto them. A guard test fails on any reminder made or completed by hand outside the services, the importer and the demo seed. - **The maintainer's decisions (the issue's recommendations):** - a `Reminder.actor` column, shown as "via …"; - `change_status` stays off the surface; - reopen from the page (*Not done*) and the API (`POST /api/v1/reminders/{id}/reopen`); - reopening doesn't announce again. - **Behaviour changes worth a look:** - Editing a done reminder's time is refused on the form ("It is already done."), where before it was saved silently. - A moved appointment no longer moves a done reminder. - The offer's answer-by reminder falls due at 09:00 in the owner's zone, not the active zone. - **Archive format:** 77, plus migration `applications 0021_reminder_actor`. #878 (PR #1061) also takes 77, so whichever lands second takes 78. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Put reminders on the plugin surface, with a service each to create, change, move, complete and reopen them (#331)
Some checks failed
CI / Unit tests and coverage (Python 3.14) (push) Failing after 17m48s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 3m11s
CI / Database tests on PostgreSQL (push) Successful in 4m9s
CI / Security audit (push) Successful in 1m11s
CI / Browser tests (Chromium) (push) Successful in 36m19s
Every Python / Unit tests (Python 3.12) (push) Failing after 12m46s
Every Python / Unit tests (Python 3.13) (push) Failing after 12m30s
CI / Unit tests and coverage (Python 3.14) (pull_request) Failing after 12m39s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 3m4s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m13s
CI / Security audit (pull_request) Successful in 1m9s
CI / Browser tests (Chromium) (pull_request) Successful in 32m25s
5fba2e3bea
A plugin keeping a CalDAV task list in step (postulo-dav#12) could not reach `Reminder`,
and Postulo itself made, ticked and unticked reminders by hand in a dozen places.

- `create_reminder`, `change_reminder`, `postpone_reminder` (now with an actor),
  `complete_reminder` and `reopen_reminder` in `applications.services`, each taking an
  `actor`. Another person's application is a ValueError, the summary is cut to 250
  characters, a date means nine in the morning in the owner's zone (outside any request
  too), `complete_reminder` refuses a moment in the future, and reopening does not
  announce the reminder again.
- `add_reminder` makes a new entry through `create_reminder` (and `complete_reminder` when
  it arrives done): the uid-keyed, source-guarded way in is layered over the one general
  act, not a second writer beside it. It takes an optional `actor`, which the calendar
  import now passes.
- Every caller moved onto them: the create and edit forms, Done, Snooze, the API's add,
  change and complete, the interview, appointment and offer paths (the offer path no longer
  clears `done_at` by hand). The importer and the demo seed stay as they were; a test
  guards the rest of the tree.
- Decision 1 (a): `Reminder.actor` names who last touched it when it was not the person,
  drawn beside it as "via ..." on the calendar and an application's page, on the API's
  `ReminderOut`, and in the archive (format 77).
- Decision 3 (b): *Not done* in a done reminder's menu, and
  `POST /api/v1/reminders/{id}/reopen`.
- `Reminder` and the five services are on `api.__all__` and `_ELSEWHERE`; `change_status`
  stays off.

Closes #331

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tiagoagueda force-pushed 331-reminder-services from 5fba2e3bea
Some checks failed
CI / Unit tests and coverage (Python 3.14) (push) Failing after 17m48s
CI / Checks: lint, migrations, catalogues, settings, built files (push) Successful in 3m11s
CI / Database tests on PostgreSQL (push) Successful in 4m9s
CI / Security audit (push) Successful in 1m11s
CI / Browser tests (Chromium) (push) Successful in 36m19s
Every Python / Unit tests (Python 3.12) (push) Failing after 12m46s
Every Python / Unit tests (Python 3.13) (push) Failing after 12m30s
CI / Unit tests and coverage (Python 3.14) (pull_request) Failing after 12m39s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 3m4s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m13s
CI / Security audit (pull_request) Successful in 1m9s
CI / Browser tests (Chromium) (pull_request) Successful in 32m25s
to 8b81d56184
Some checks failed
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
2026-10-29 10:01:10 +00:00
Compare
tiagoagueda force-pushed 331-reminder-services from 8b81d56184
Some checks failed
CI / Browser tests (Chromium) (pull_request) Has been cancelled
CI / Unit tests and coverage (Python 3.14) (pull_request) Has been cancelled
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Has been cancelled
CI / Database tests on PostgreSQL (pull_request) Has been cancelled
CI / Security audit (pull_request) Has been cancelled
to faa9ef68c6
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m53s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m50s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m47s
CI / Security audit (pull_request) Successful in 51s
CI / Browser tests (Chromium) (pull_request) Successful in 33m34s
2026-10-29 10:30:01 +00:00
Compare
tiagoagueda force-pushed 331-reminder-services from faa9ef68c6
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m53s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m50s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m47s
CI / Security audit (pull_request) Successful in 51s
CI / Browser tests (Chromium) (pull_request) Successful in 33m34s
to 5a66070a9c
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 13m20s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m44s
CI / Database tests on PostgreSQL (pull_request) Successful in 3m34s
CI / Security audit (pull_request) Successful in 54s
CI / Browser tests (Chromium) (pull_request) Successful in 34m1s
2026-10-29 18:28:11 +00:00
Compare
tiagoagueda deleted branch 331-reminder-services 2026-10-29 18:31:27 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo!1080
No description provided.