Problem
At the end of a campaign there's no way to produce a single unified ranking of the finalists representing the whole jury's combined opinion. Rating/yes-no rounds only give a threshold cut (a set of survivors, not an order), and the one path that does produce an order — Schulze, via the ranking round — is buried and CSV-only, with no on-screen ranked view anywhere in the app.
We need an explicit "combine everything into one final ranking" step that closes the campaign.
Proposed feature
Aggregate the votes of a coordinator-selected finalized round into one unified ranking of the most-recent round's finalist images, with an optional maximum length. The result is a new terminal round that finalizes the campaign and is exported with provenance. A re-runnable preview lets the coordinator compare options before an atomic commit.
Aggregation method, by the source round's type:
- Ranking round → Schulze (default) and Borda, shown side by side. Schulze reuses the existing in-tree implementation (verified correct against the canonical example).
- Rating / yes-no round → average (default) or sum of scores — these are already cardinal scores, so Borda/Schulze would throw away the intensity jurors expressed.
A note on what these methods optimize for: all of the proposed mechanisms are greedy — they reward the highest number of points (or pairwise wins) and do not attempt to guarantee justified or proportional representation. (Even the Schulze variant in use is the non-proportional one.) Greedy aggregation is probably the most popular choice for "who are the best photos," but it is not the only legitimate one — methods that guarantee justified/proportional representation are a real alternative. The mechanism dispatch is deliberately pluggable so other aggregation philosophies can be added later as first-class options, not just bolted on.
Rules:
- Completed votes only; disqualified entries excluded; missing votes ignored.
- Finalists with no votes in the chosen source round are excluded and logged (shown as a count).
- True ties are shown as ties. Any strict ordering needed (e.g. at the max-length cut) uses a deterministic key — never randomness. A tie straddling the cut keeps the whole tied group.
Important caveats
- Disqualifying an image after aggregation can reorder the survivors (Schulze and Borda both fail "independence of irrelevant alternatives"), so a later DQ requires re-running the aggregation. This must be documented in the results UI. (Average/sum don't have this issue.)
- The existing Schulze path uses a random tie-breaker (
rdb.py:1954), so its results aren't reproducible. This should be fixed to a deterministic tie-break and pinned with a regression test — likely a small prerequisite PR.
Notes
- Implemented as a
vote_method='aggregate' terminal round; mechanism dispatch is pluggable (median, etc. could follow).
- Detailed spec (endpoints, storage, acceptance criteria, edge cases) available on request.
Open questions: Schulze tie-break fix as a prerequisite PR? · Expose "sum" for rating sources, or average-only? · Borda raw-sum vs per-juror-normalized?
Problem
At the end of a campaign there's no way to produce a single unified ranking of the finalists representing the whole jury's combined opinion. Rating/yes-no rounds only give a threshold cut (a set of survivors, not an order), and the one path that does produce an order — Schulze, via the ranking round — is buried and CSV-only, with no on-screen ranked view anywhere in the app.
We need an explicit "combine everything into one final ranking" step that closes the campaign.
Proposed feature
Aggregate the votes of a coordinator-selected finalized round into one unified ranking of the most-recent round's finalist images, with an optional maximum length. The result is a new terminal round that finalizes the campaign and is exported with provenance. A re-runnable preview lets the coordinator compare options before an atomic commit.
Aggregation method, by the source round's type:
A note on what these methods optimize for: all of the proposed mechanisms are greedy — they reward the highest number of points (or pairwise wins) and do not attempt to guarantee justified or proportional representation. (Even the Schulze variant in use is the non-proportional one.) Greedy aggregation is probably the most popular choice for "who are the best photos," but it is not the only legitimate one — methods that guarantee justified/proportional representation are a real alternative. The mechanism dispatch is deliberately pluggable so other aggregation philosophies can be added later as first-class options, not just bolted on.
Rules:
Important caveats
rdb.py:1954), so its results aren't reproducible. This should be fixed to a deterministic tie-break and pinned with a regression test — likely a small prerequisite PR.Notes
vote_method='aggregate'terminal round; mechanism dispatch is pluggable (median, etc. could follow).Open questions: Schulze tie-break fix as a prerequisite PR? · Expose "sum" for rating sources, or average-only? · Borda raw-sum vs per-juror-normalized?