Seconde moitie d ARCH-003.
Les memes vingt lignes vivaient dans trois routes -- matches.create_match,
matches.edit_match et team_matches.create_match : formater la date et
l heure, puis parcourir deux listes paralleles pour apparier un joueur avec
la ligne de participation qu une reaction Discord doit pouvoir retrouver.
Trois copies, donc trois occasions de diverger. Elles avaient deja diverge :
create_match lisait les heures des variables qu il venait d analyser, et
affichait 'TBD' des qu une des deux manquait ;
edit_match les relisait depuis la ligne enregistree et substituait
l heure de debut a une heure de fin absente.
Un match avec une heure de debut et pas de fin annoncait donc une heure
dans une route et 'TBD' dans l autre, pour la meme donnee.
app/services/scheduling.py retient la regle la plus soigneuse des deux.
zip_participants() isole l appariement par index, qui n est correct que
tant que les deux listes sont construites en phase -- desormais un seul
endroit a relire au lieu de trois, et une liste plus courte donne None,
converti en reference vers le match lui-meme.
matches.py 711 -> 687 lignes, team_matches.py 347 -> 337.
9 tests sur le service seul, sans base ni requete.
Co-Authored-By: Claude Opus 5 <[email protected]>
ARCH-004 et la moitie NotificationService d ARCH-003.
app/routes/users.py faisait 1 699 lignes et couvrait six sujets qui ne
partageaient rien d autre qu un prefixe d URL. Il devient un paquet :
blueprint.py l objet Blueprint, seul
_shared.py helpers de formulaire, gamertags, validation de PDF
accounts.py 348 l. liste, creation, edition, suppression, fiche
availability.py 233 l. disponibilites joueur et creneaux coach
contracts.py 206 l. depot, signature, telechargement
notes.py 376 l. notes d equipe et notes nominatives
one_on_one.py 259 l. demandes de seance individuelle
profile.py 132 l. profil de la personne connectee
Aucun fichier ne depasse 400 lignes -- le critere d acceptation de l audit.
**Un seul blueprint, pas six.** Les endpoints restent `users.*`. Les
renommer aurait touche 137 appels `url_for` dans les gabarits, pour un
benefice nul : l objectif est un fichier qu on peut lire, pas une carte
d URL a reapprendre. Les 30 endpoints sont identiques avant et apres,
verifie sur url_map.
send_discord_notification part dans app/services/notifications.py. Elle
tirait `requests`, `logging` et le bot Discord dans un module dont le sujet
est le traitement HTTP, et se trouvait coincee entre deux definitions de
route. Son `except Exception` est conserve et documente : une notification
qui n arrive pas ne doit pas annuler la transaction qu elle annoncait.
tests/test_route_map.py, nouveau : il parcourt gabarits et code, releve
tout endpoint nomme litteralement dans un url_for, et verifie qu il existe
dans la carte. C est le mode de defaillance de ce genre de decoupage --
pas une erreur a l import, mais un BuildError chez la premiere personne qui
ouvre la page concernee. Un test de garde verifie aussi que le scan trouve
quelque chose, sinon le reste serait vide de sens.
Piege rencontre, et corrige : test_role_change patchait
`app.routes.users.update_user_gamertags`. Apres le decoupage ce nom est un
reexport, pas celui qu accounts.py resout -- le patch aurait pu laisser la
route appeler la vraie fonction et le test passer sans rien verifier. Ici
monkeypatch a echoue bruyamment, mais la cible est desormais explicite et
le test enregistre que la doublure a bien ete appelee.
347 tests passent (263 + 84, dont 82 parametres par la carte des routes).
Co-Authored-By: Claude Opus 5 <[email protected]>