Skip to content

feat(votes): Scrutin + RepresentativeVote models (AN roll-call votes)

Scaffold Digital Fleet requested to merge feat/scrutins-votes-v2 into master

Scrutins votes — backend (models, endpoints, seat directory)

AN roll-call votes end-to-end: models, import pipeline, aggregate endpoints, seat directory. Pairs with risefor-front !79 and law-tracker !6 (merged).

Models (migrations 0269–0274)

  • Scrutin + RepresentativeVote — idempotent import, per-scrutin integrity checks (8,434 scrutins / 1.27M votes vs AN decompte totals)
  • Scrutin.law FK — plugin-gated model import + plugin-gated migration 0272 (0224/0255 pattern; no crash on non-law-tracker instances)
  • isAmendmentVote classifier v2 — apostrophe/typo-tolerant, calibrated on all 8,434 titles (7,219 amendment / 1,215 law-level, 0 mismatches)
  • Representative.placeHemicycle + migration 0274 — populated by import_elected_gouv from the AN mandature.placeHemicycle (577/577 deputies, DB == AN verified)

Endpoints

  • /scrutins/{id}/votes/ — nested container (votes NOT in the list serializer — timeout lesson)
  • /scrutins/{id}/party-tallies/ — per-group tallies + size/absent + seatDirectory (occupant identity + party urlid for every official seat) + per-group voter seats (numPlace = real AN seat)
  • /representatives/{id}/vote-history/ — server-side join, 200/page, newest first

Tooling

  • sync_rep_seats command — derives deputy seats from vote numPlace mode (≥70% confidence, dry-run default)
  • Absent counter = group directory chairs − group voter seats → always matches the amber chairs on the hemicycle (SOC 25=25, DEM 11=11, Horizons 15=15 verified)

Prod runbook (after merge)

python manage.py migrate risefor_lobbying   # 0269–0274 (0272 auto-skips without plugin)
env/bin/python law-tracker/.../update-data.py --type suivi-loi   # laws + scrutins + votes
env/bin/python law-tracker/.../update-data.py --type deputee     # deputies + seats

then restart backend + purge CF /static/*.

Edited by Scaffold Digital Fleet

Merge request reports