Skip to content

Make "cancel campaign" work for the campaign's coordinators - #679

Open
lgelauff wants to merge 1 commit into
masterfrom
fix/cancel-campaign-endpoint
Open

lgelauff wants to merge 1 commit into
masterfrom
fix/cancel-campaign-endpoint

Conversation

@lgelauff

Copy link
Copy Markdown
Collaborator

Fixes #675.

  • POST /admin/campaign/<id>/cancel called CoordinatorDAO.cancel_campaign(), which didn't exist, so it was always a 500. The logic now lives on CoordinatorDAO.cancel_campaign(), and OrganizerDAO.cancel_campaign() delegates to it: one implementation. The endpoint returns {id, status, cancelled_rounds}.
  • A campaign with a finalized round can't be cancelled (400). It has results, so it is closed instead: finalized with a report, or archived. Rounds that are already cancelled are left as they are, so their close date and log stay. The cancel code has never checked for finalized rounds (since 2017), and no commit or issue says that was intended.
  • If the rounds to cancel have completed votes, the cancel is refused (400) with a warning, for example "the rounds of campaign 12 have 120 completed votes, which will no longer count if it is cancelled; send discard_completed=true to cancel anyway". Sending discard_completed: true cancels anyway. This is the same flag remove_juror uses for discarding a juror's completed votes. Votes are not deleted: they keep their status and values, and their round is cancelled.
  • tools/admin.py cancel now passes the campaign id, which is what OrganizerDAO.cancel_campaign() expects, mentions the votes in its yes/no prompt, and prints the number of rounds it actually cancelled.

Permissions are unchanged: CoordinatorDAO.from_campaign allows the campaign's coordinators and maintainers; others get 403. Cancelling a single finalized round through /admin/round/<id>/cancel is still allowed, as before; that is left for a separate decision.

Tests

  • New test_coordinator_cancels_campaign: a coordinator cancels a campaign with one active round and one round cancelled earlier; after one vote, a cancel without discard_completed is refused with the warning and the round stays active; with it, the active round is cancelled with no open tasks left, the earlier round keeps its close date, and cancelled_rounds is 1; a juror who isn't a coordinator gets 403. It fails on master with the 500.
  • test_home_client now also checks that a finalized campaign can't be cancelled, also not after reopening it, and that its rounds stay finalized.
  • Each new check fails when its part of the fix is taken out (no finalized-round check; cancelled rounds not skipped; no completed-votes warning).
  • Full suite: 172 passed, 3 xfailed.
  • Not tested: tools/admin.py itself (it needs a database).

Written by Claude (Claude Code) on behalf of @lgelauff.

🤖 Generated with Claude Code

POST /admin/campaign/<id>/cancel called CoordinatorDAO.cancel_campaign(),
which only existed on OrganizerDAO, so every call was a 500. Move the logic
to CoordinatorDAO.cancel_campaign() and let OrganizerDAO delegate to it, so
there is one implementation. The endpoint now returns the campaign id, its
status and how many rounds were cancelled.

A campaign with a finalized round can no longer be cancelled (400): it has
results, so it is closed instead (finalized with a report, or archived).
Rounds that are already cancelled are left as they are.

If the rounds to cancel have completed votes, which would no longer count,
the cancel is refused with a warning unless the request sets
discard_completed (the same flag remove_juror uses).

tools/admin.py passed the campaign object where OrganizerDAO expects an id;
pass the id, mention the votes in its confirmation prompt, and print the
number of rounds actually cancelled.

Adds a regression test (a 500 before the fix; a non-coordinator gets 403;
completed votes need discard_completed; an earlier cancelled round is
untouched) and extends test_home_client
(cancel refused once finalized, and still refused after reopening).

Fixes #675.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Cancel campaign" endpoint always returns a 500

1 participant