Gestionale Gare di Scherma
Sistema completo per la gestione di gare di scherma (individuali e a squadre)
secondo le Disposizioni Attività Agonistica FIS 2026-27 .
Sostituisce un gestionale .NET legacy con uno stack moderno Python + Web.
gestionale_gara/
├── backend/ # FastAPI + PostgreSQL + SQLAlchemy
├── frontend-admin/ # HTML/CSS/JS + Bootstrap 5 (richiede login JWT)
├── frontend-results/ # HTML/CSS/JS + Bootstrap 5 (pubblico, no login)
└── cyrano-bridge/ # Modulo standalone: interfaccia pedane via Cyrano
Stack :
Backend: FastAPI, SQLAlchemy 2.0, Alembic, psycopg2, python-jose (JWT),
passlib (bcrypt), pandas
Frontend: HTML/CSS/JS vanilla + Bootstrap 5 CDN (no build step)
DB: PostgreSQL
Cyrano-bridge: Python standalone, protocollo seriale RS422 verso pedane Favero
Per avvio rapido e installazione vedi AVVIO.md .
Le pagine admin sono in frontend-admin/. Richiedono autenticazione (JWT).
Pagina
Descrizione
login.html
Login con username/password (default admin/admin123)
index.html
Dashboard: elenco tornei + creazione nuovo torneo
tournament-detail.html
Dettaglio torneo: gare, import XML legacy, gestione pool arbitri, gare nascoste. Modale "Nuova Gara" con campi serie/lega/zona/categorie_unite per gare a squadre
athletes.html
Anagrafica atleti: ricerca, edit, ranking per arma, società
atleti-allenamento.html
Associazione atleta → società di allenamento secondarie (vincoli arbitrali)
arbitri.html
Anagrafica arbitri: qualifiche per arma×genere, grado, pausa, società escluse, categorie escluse
registration.html
Iscrizioni di una gara: aggiungi atleti, ranking gara, marca presenti/ritirati
teams.html
Gestione squadre (gara tipo='squadre'): classica, mista_società, mista_genere (M+F), u14_categorie_unite. Import ranking.xls FIE legacy
tournament-arbitri.html
Pool arbitri del torneo (un arbitro disponibile per tutte le gare)
arbitri-gara.html
Assegnazione arbitri alle singole gare/gironi/assalti (auto-assign con load balancing)
formula.html
Configurazione formula gara: modalità, gironi, eliminazione, tableau, decalage, raggruppamenti con altre categorie
groups-view.html
Anteprima/composizione gironi, log serpentone, ricomposizione manuale
groups-scores.html
Inserimento punteggi gironi (matrice assalti), classifica gironi
de-bracket.html
Tabellone eliminazione diretta principale (R128..F), input punteggi assalti DE
de-playoffs.html
Tabelloni Play-Off / Play-Out (squadre Serie A2/B1/B2/C, Club League C)
pedane.html
Pianificazione pedane: orari appello gironi, pedana assalti DE
ranking-view.html
Classifica iniziale gara basata su ranking nazionale per arma
import.html
Import XML legacy .gst da gestionale .NET (atleti, iscrizioni, gironi, assalti)
import-pozzo.html
Import anagrafica atleti da file "pozzo" FIS XLSX/CSV
state-history.html
Storico checkpoint gara + rollback a stato precedente
Pagine pianificate ma non ancora UI dedicate (endpoint API disponibili)
Club League calendario Lega A/B (3 giornate × 5 turni) → endpoint POST /api/gare/{id}/club-league/calendario
Promozioni/Retrocessioni post-stagione → endpoint POST /api/gare/{id}/calcola-promozioni
Import classifica AMIS esterna per gare Master → endpoint POST /api/gare/{id}/import-amis-results
Backfill regioni (società/atleti) → endpoint POST /api/regioni/backfill-societa|-atleti
Frontend Results — Pagine pubbliche
Le pagine results sono in frontend-results/ e non richiedono login. Sono
le pagine consultate dal pubblico durante e dopo la gara.
Tutti gli endpoint sono prefissati con /api e protetti da JWT (eccetto
quelli pubblici di results.py). Documentazione interattiva: GET /docs.
Method
Path
Descrizione
POST
/api/auth/token
Login (form-encoded: username, password) → JWT
GET
/api/auth/me
Dati utente corrente
Method
Path
Descrizione
GET/POST
/api/tornei
Elenca/crea tornei
GET/PUT/DELETE
/api/tornei/{id}
CRUD singolo torneo
PATCH
/api/tornei/{id}/visibility
Toggle visibilità (nascosto)
GET
/api/tornei/{id}/gare
Elenco gare di un torneo
POST
/api/tornei/{id}/gare
Crea gara (accetta serie, lega, zona, fase, categorie_unite per squadre)
GET/PUT/DELETE
/api/gare/{id}
CRUD singola gara
PATCH
/api/gare/{id}/visibility
Toggle visibilità gara
Method
Path
Descrizione
GET
/api/categorie
Mappa categoria → label
GET/POST
/api/societa
Elenca/crea società (regione auto-popolata da codice se omessa)
PUT
/api/societa/{id}
Update società
GET/POST
/api/atleti
Elenca (con filtri q, cognome, societa_id, genere, cat_atleti) / crea atleta
GET/PUT
/api/atleti/{id}
Dettaglio / update atleta
Method
Path
Descrizione
GET
/api/gare/{id}/iscrizioni
Iscrizioni della gara
POST
/api/gare/{id}/iscrizioni
Aggiungi iscrizione (atleta o squadra)
PATCH
/api/iscrizioni/{id}
Update (presente, ritirato, escluso, ranking_gara)
DELETE
/api/iscrizioni/{id}
Cancella iscrizione
Method
Path
Descrizione
GET/POST
/api/gare/{gara_id}/squadre
Elenca/crea squadra
PUT/DELETE
/api/gare/{gara_id}/squadre/{id}
Update/elimina squadra
POST
/api/gare/{gara_id}/squadre/auto-classiche
Genera squadre classiche raggruppando per società
POST
/api/gare/{gara_id}/squadre/import-ranking-xls
Import ranking.xls FIE
Method
Path
Descrizione
GET
/api/gare/{id}/formula
Recupera configurazione formula
POST/PUT
/api/gare/{id}/formula
Crea/aggiorna formula
GET
/api/gare/{id}/formula/config-gironi
Configurazione gironi suggerita per N iscritti
Method
Path
Descrizione
GET
/api/gare/{id}/gironi
Elenco gironi del turno corrente
POST
/api/gare/{id}/gironi/genera
Genera gironi via serpentone FIE (parametri: turno, config_override)
GET
/api/gare/{id}/gironi/preview
Anteprima composizione senza scrivere su DB
PUT
/api/gare/{id}/gironi/composizione
Ricomposizione manuale dei gironi
Method
Path
Descrizione
GET
/api/gare/{id}/tabellone
Elenco slot tabellone (filtrabile per fase)
GET
/api/gare/{id}/tabellone/round/{round_size}
Slot di un round specifico
POST
/api/gare/{id}/tabellone/genera
Genera tabellone DE (parametri: n_eliminati, perc)
POST
/api/gare/{id}/tabellone/playoff-playout
Nuovo : genera Play-Off / Play-Out per Serie/Club League dopo R16
POST
/api/gare/{id}/tabellone/azzera
Cancella tabellone
PUT
/api/gare/{id}/tabellone/match/{round_size}/{idx}/pedana
Assegna pedana/ora a un match
Method
Path
Descrizione
GET
/api/assalti?gara_id=&fase=
Elenco assalti filtrabili
PUT
/api/assalti/{id}/side/{1|2}
Inserisci punteggio singolo lato (admin)
PUT
/api/assalti/{id}/full
Inserisci punteggio completo (arbitro)
GET
/api/assalti/{id}/log
Storico modifiche
Method
Path
Descrizione
GET
/api/gare/{id}/classifica-gironi
Classifica dopo gironi
GET
/api/gare/{id}/classifica-finale
Classifica finale (post DE)
Method
Path
Descrizione
GET/PATCH
/api/arbitri/atleti
Anagrafica arbitri (qualifiche, grado, pausa)
GET/POST/DELETE
/api/arbitri/atleti/{id}/societa-escluse
Vincoli società
GET/POST/DELETE
/api/arbitri/atleti/{id}/categorie-escluse
Vincoli categoria (es. "Master")
GET/POST/DELETE
/api/arbitri/tornei/{id}/pool
Pool arbitri del torneo
GET
/api/arbitri/gare/{id}/pool
Pool gara (proxy del torneo con flag eligibilità)
PUT
/api/arbitri/gironi/{id}/arbitro
Assegna arbitro a girone (con propagazione assalti)
PUT
/api/arbitri/assalti/{id}/arbitro
Assegna arbitro/assessori a un assalto
POST
/api/arbitri/gare/{id}/auto-assign
Auto-assegnazione (scope: gironi|assalti|all)
GET
/api/arbitri/gare/{id}/storico
Storico arbitraggi della gara
Method
Path
Descrizione
GET/POST
/api/gruppi-formula
Elenco/crea gruppo
GET
/api/gare/{id}/gruppo-formula
Gruppo di una gara
PUT/DELETE
/api/gruppi-formula/{id}
Update/scioglimento gruppo
Method
Path
Descrizione
POST
/api/gare/{id}/club-league/calendario
Genera calendario Lega A/B (16 squadre, 15 turni, 120 assalti)
POST
/api/gare/{id}/club-league/girone-unico-variabile
Genera girone unico per 2-16 squadre (caso Serie con <6 partecipanti)
Promozioni / AMIS / Regioni
Method
Path
Descrizione
POST
/api/gare/{id}/calcola-promozioni
Calcola promozioni/retrocessioni Serie/Lega (input: stagione es. "2026-27")
GET
/api/promozioni
Elenco promozioni (filtri: stagione, arma, genere, contesto)
POST
/api/gare/{id}/import-amis-results
Import classifica AMIS esterna per gare Master (modalità amis_external)
GET
/api/regioni
Mappa province → regione + zone Z1/Z2
POST
/api/regioni/backfill-societa
Popola Societa.regione dai codici esistenti
POST
/api/regioni/backfill-atleti
Propaga regione da società ad atleti
Method
Path
Descrizione
POST
/api/import-xml
Import XML legacy .gst (atleti, iscrizioni, gironi, assalti DE)
POST
/api/import-pozzo
Import anagrafica FIS da XLSX/CSV
GET
/api/export-legacy-xml/{torneo_id}
Export XML legacy compatibile col gestionale .NET
Method
Path
Descrizione
GET
/api/gare/{id}/checkpoints
Storico checkpoint
POST
/api/gare/{id}/rollback/{checkpoint_id}
Rollback allo stato indicato
Risultati pubblici (no auth)
Method
Path
Descrizione
GET
/api/public/tornei
Tornei pubblicati
GET
/api/public/gare/{id}
Dettaglio gara pubblica
GET
/api/public/gare/{id}/gironi
Composizione gironi
GET
/api/public/gare/{id}/tabellone
Tabellone DE
GET
/api/public/gare/{id}/classifica
Classifica finale
App arbitro (login dedicato)
Method
Path
Descrizione
POST
/api/arbitro-app/login
Login arbitro con num_fis + password
GET
/api/arbitro-app/my-assignments
Assegnazioni dell'arbitro corrente
PUT
/api/arbitro-app/assalti/{id}
Inserisci punteggio (entrambi i lati in una chiamata)
Method
Path
Descrizione
GET
/api/gare/{id}/pdf/{tipo}
Genera PDF (tipo: gironi, sommario_gironi, gironi_con_ris, tabellone, classifica_finale, cartellino_squadra, ...)
Modalità formula supportate
Configurabili via campo FormulaGara.modalita_formula (vedi
backend/app/models/formula.py ):
Valore
Articolo Disp.
Descrizione
standard
art. 5/7
Formula zonale/regionale: gironi (1-2 turni) con eliminazione 0-30% + DE integrale
campionato_italiano
art. 5
Campionati Italiani Juniores/Cadetti/Giovani/Assoluti: nessuna eliminazione post-gironi + ricomposizione "teste di serie" se top-N assenti
nazionale_giovani_cadetti
art. 5
Cadetti/Giovani fioretto M e spada: bypass top-16 in gironi + DE128 con preliminare (qualifica 64 al principale)
nazionale_assoluti
art. 5
Nazionale Assoluti: bypass top-8 (fioretto/sciabola) o top-16 (spada) + DE integrale senza preliminare
gpg_regionale
art. 30
GPG prove regionali: gironi 7/6 senza eliminazione
gpg_campionato
art. 30
GPG Campionato Italiano e U14: doppia tornata gironi 0-20% + DE fino a 256 atleti
gpg_maschietti_finale
art. 30
GPG Maschietti/Bambine finale: 3 turni (gironi senza elim → gironi con elim 50% → DE integrale)
girone_unico
art. 6
Club League Lega A/B (16 squadre): girone unico in 3 giornate × 5 turni × 8 incontri. Anche per Serie con <6 squadre
amis_external
art. 45
Gare Master: nessuna generazione gironi/DE, classifica importata da sistema AMIS esterno
Campi correlati su FormulaGara:
num_teste_serie_bypass (default 16) — quante teste di serie saltano i gironi (bypass)
num_qualificati_diretti (default 48) — quanti qualificati post-gironi entrano direttamente nel principale (modalità nazionale)
tableau_preliminare_enabled (default True) — se False salta il preliminare anche con modalità Cadetti/Giovani (variante fioretto F / sciabola)
perc_eliminazione_turno2 — % elim tra turno 1 e 2 (GPG Maschietti finale: default 50%)
stoccate_per_frazione_squadre (default 5) — soglia max per frazione della staffetta a squadre (5 → 45 totali, 4 → 36 U14 Maschietti/Giovanissimi)
tipo_decalage — societa | regione | nessuno
incontri_utili_fino_a — posizione massima utile (Serie C / Club League D: 6); marca automaticamente non_disputato=True sui bouts ininfluenti
finale_3_4_posto — abilita finalina 3°/4°
Configurabili via Squadra.tipo_squadra (vedi
backend/app/services/team_composer.py ):
Valore
Composizione
Vincoli
classica
3-4 atleti stessa società
suffisso -2, -3, … per società con più squadre
mista_societa
3-4 atleti ≥2 società diverse
societa_id=None
mista_genere
2 atleti, 1 M + 1 F
qualsiasi società
u14_categorie_unite
3-4 atleti (3+1 riserva)
stessa società, categorie tra quelle dichiarate in Gara.categorie_unite
Provincia → Regione + Zone Z1/Z2
Modulo backend/app/services/regioni.py :
PROVINCIA_REGIONE: 107 sigle attive + 4 storiche sarde (CI/OG/OT/VS)
REGIONI_PROVINCE: reverse map regione → set(sigle)
ZONE_INDIVIDUALI: Z1 (8 regioni Nord) e Z2 (12 regioni Centro-Sud), Disp. art. 31/38
regione_da_codice_societa(codice) — prime 2 lettere → regione
zona_individuale_da_regione(regione) → 'Z1' | 'Z2'
backfill_regione_societa(db) / backfill_regione_atleti(db)
Le società create via POST /api/societa ottengono regione automaticamente
dal codice se non esplicitata. Gli atleti propagano la regione dalla loro
società alla creazione.
Migrations Alembic in backend/alembic/versions/ (001..024). Idempotenti
(usano inspect() per check colonne/tabelle prima di create/drop).
Torneo (id, nome, data, luogo, variante_categoria fis|amis) → 1:N Gara, 1:N GruppoFormula
GruppoFormula (torneo_id, nome, gironi_uniti, de_unito) → 1:N Gara
Gara (torneo_id, arma, genere, categoria, tipo, stato_fase, gruppo_formula_id, ordine_in_gruppo, serie, lega, zona, fase, categorie_unite ) → 1:1 FormulaGara, 1:N Iscrizione/Girone/TabelloneED
FormulaGara (gara_id unico; dim_gironi_min/max, perc_eliminazione, perc_eliminazione_turno2 , num_minimo_qualificati, tableau_size, decalage_societa, tipo_decalage , finale_3_4_posto, modalita_formula, num_teste_serie_bypass, num_qualificati_diretti, tableau_preliminare_enabled , stoccate_per_frazione_squadre , incontri_utili_fino_a )
Iscrizione (gara_id, atleta_id XOR squadra_id, ranking_gara, presente, ritirato, escluso, classifica_finale)
Squadra (gara_id, nome, societa_id?, tipo_squadra, suffisso_numerico) → 1:N SquadraAtleta
SquadraAtleta (squadra_id, atleta_id, ordine, is_riserva)
Atleta (num_fis, cognome, nome, societa_id, data_nascita, genere, ranking_fioretto/spada/sciabola, cat_atleti, regione , is_arbitro + qualifiche_arbitro)
Societa (codice, denominazione, citta, sigla, data_scadenza, regione )
Girone (gara_id, turno, numero_girone, completato, pedana, ora_appello, arbitro_id)
GironeAtleta (girone_id, iscrizione_id, ordine_nel_girone, vinti, assalti, sd, sr, vm, classifica_girone)
Assalto (gara_id, fase: girone|de|spareggio|girone_unico , fase_tabellone, girone_id?, de_round?, de_slot?, ordine, iscrizione1/2_id, stoccate1/2 + _input, vittoria1/2, completato, pedana, ora_inizio, arbitro_id, assessore1/2_id, video_arbitro_id, non_disputato, numero_turno, numero_giornata )
TabelloneED (gara_id, fase_tabellone: principale|preliminare|playoff|playout|finale_5_8|finale_3_4 , round_size, slot_number, iscrizione_id, is_bye, assalto_id, vincitore_id, seed, pedana, ora_inizio)
PromozioneRetrocessione (stagione, torneo_id?, squadra_id, arma, genere, contesto: serie|club_league, serie_from/to, lega_from/to, zona, tipo: promozione|retrocessione, classifica_finale)
StatoGaraCheckpoint (gara_id, fase_al_momento, descrizione, snapshot_json) — per rollback
LogAzione (gara_id, tipo, testo) — log testuale generazioni/import
TorneoArbitro / ArbitroSocietaEsclusa / ArbitroCategoriaEsclusa
In grassetto i campi aggiunti per Disp. FIS 2026-27 (migrations 021-024).
Sotto backend/app/services/:
Modulo
Responsabilità
group_composer.py
Orchestrazione generazione gironi: serpentone FIE, gironi uniti, gestione turni multipli (incluso elim 50% turno 2 GPG Maschietti), verifica "teste di serie" Campionato Italiano
serpentone_fie.py
Algoritmo serpentone con decalage società o regione
de_builder.py
Generazione tabellone DE con seeding FIE; supporto modalità nazionale (bypass top-N) con/senza preliminare; ROUND_SIZES fino a 256; genera_playoff_playout per Serie/Club League; marca_incontri_ininfluenti
de_classifica.py
Calcolo classifica finale post-DE (popola Iscrizione.classifica_finale)
de_propagation.py
Propagazione vincitori DE ai round successivi
girone_unico.py
Girone unico Club League Lega A/B (calendario hardcoded 15 turni × 8 incontri) + girone unico variabile 2-16 (caso Serie con <6 squadre)
promozioni.py
Calcolo post-stagione promozioni/retrocessioni Serie A1..C e Club League A..D
amis_external.py
Import classifica AMIS esterna per gare Master
team_composer.py
Validazione composizione squadre (4 tipi), auto-generazione classiche, validazione categorie Master squadre
categorie.py
Mappa categoria → label (FIS + AMIS); categorie_amis_squadre_ammesse(arma, genere)
regioni.py
Mappa province → regione + zone Z1/Z2 + backfill
referee_assigner.py
Algoritmo eligibilità arbitri e auto-assegnazione con load balancing
ranking_calc.py
Calcolo classifica gironi (V/M, indicatore, stoccate date), eliminazione, pool DE per gironi uniti
ranking_xls.py
Parser ranking FIE legacy (sheets SPM/SPF/SCM/SCF/FM/FF)
bout_score_service.py
Applicazione punteggio assalto con propagazione DE; gestione fase 'girone_unico' e staffetta a frazioni
bout_score_parser.py
Parser input punteggio ("V5", "5/3", etc.)
bout_order.py
Ordine assalti FIE per gironi 3-13 (incluso varianti per 2 pedane)
state_manager.py
Checkpoint/rollback stato gara
action_logger.py
Log testuali generazioni gironi/DE/import
gruppo_formula.py
Helper raggruppamenti categorie (gironi/DE uniti)
display.py
Label uniforme partecipante (atleta o squadra)
planner.py
Pianificazione orari/pedane gironi
pdf_renderer.py / pdf_data.py
Generazione PDF (gironi, tabellone, classifica)
legacy_xml_export.py
Export XML compatibile col gestionale .NET
Stato implementazione FIS 2026-27
Tutte le 9 modalità formula (vedi tabella sopra)
Tabellone DE128 con preliminare (Cadetti/Giovani fioretto M e spada)
Tabellone DE senza preliminare (Cadetti/Giovani fioretto F/sciabola, Assoluti)
Tabellone fino a DE256 (GPG/U14 numerosi)
Play-Off / Play-Out per Serie A2/B1/B2/C e Club League C
Club League Lega A/B con girone unico 3 giornate × 5 turni
Girone unico variabile per Serie <6 squadre
4 tipi di squadra (classica, mista_societa, mista_genere, u14_categorie_unite)
Decalage per società o regione (configurabile)
Stoccate per frazione configurabili (45 vs 36 staffetta)
Eliminazione 50% turno 2 GPG Maschietti finale
Clamp 20% Cadetti in doppia tornata, 0% Juniores zonali/regionali
Validazione Master squadre (cat. A/B, eccezione fioretto F/sciabola F solo A)
Import classifica AMIS esterna per Master
Calcolo promozioni/retrocessioni Serie A1..C e Club League A..D
Marker "incontri non disputati" automatico per Play-Out se incontri_utili_fino_a < 9
107 province italiane + zone Z1/Z2
Auto-popolamento regione da codice società
Composizione zone Club League A/B/C/D : non specificata nelle Disp. Att.,
in attesa di Circolare Federale dedicata.
Fase Finale Lega A (8 squadre, 2 gironi × 4 + Play-Off senza 5°-8°):
building block _build_subtableau disponibile, funzione dedicata da
scrivere quando arriva la circolare con regole esatte (8 squadre = top 2
per zona o top 4 per zona?).
UI dedicate per Club League calendario, promozioni e import AMIS:
endpoint API funzionanti via Swagger UI (/docs).
Caso "17 squadre" in Serie A2/B1/B2: la prima per ranking entra
direttamente al DE16. Endpoint generico DE non lo gestisce ancora.
Gran Premio Paralimpico (carrozzina): richiede classificazione disabilità
su atleta + tabelle gironi dedicate + coefficienti Kt.
Gran Premio di Scherma Integrata.
Non Vedenti (Spada M/F) con DE 15 stoccate × 3 tempi.
Queste tre aree sono state escluse dal piano implementativo corrente per
decisione esplicita: richiedono infrastruttura nuova (classificazione
atleta, tipo gara dedicato, durata DE custom) da pianificare separatamente.
Migrations idempotenti : ogni migration usa _has_column() /
_has_constraint() per evitare conflitti con Base.metadata.create_all()
che gira al lifespan.
State autoflush=False : chiamare db.flush() esplicito dopo db.add()
prima di query dipendenti.
Test smoke : dopo modifiche pesanti, eseguire
cd backend && .venv/Scripts/python.exe -m alembic heads per verificare
la catena migrations.
GPL-2.0 (vedi AVVIO.md per il testo completo).
Disp. Att. FIS 2026-27: Disp.-Att.-Ag.-2026-27.pdf
Regolamento Organizzazione FIE: art. o.68 (serpentone gironi)
Regolamento Tecnico FIE: art. t.124 (passività), T 70 (materiali)