Applicazione modulare per l'analisi degli aiuti di stato italiani (OpenData 2014–2025).
Consolida in una libreria testata il know-how disperso nei notebook/script del repo
open-data-analytics: data-access, classificatori pluggable, pipeline, export.
Esterna al repo originale: legge i dati esistenti in sola lettura (data_root in
aiuti.yml punta a ../open-data-analytics/data); non modifica né duplica il corpus.
cd aiuti-lab
python -m venv .venv --system-site-packages # riusa i pacchetti base gia' presenti (pandas, pyarrow)
source .venv/bin/activate
pip install -e ".[dev]" # core + duckdb + pytest
pytest
# Metodi opzionali (solo se servono spacy/transformer/LLM):
pip install -e ".[nlp,llm]" # spacy + transformers + openai
python -m spacy download it_core_news_lg # modello per il metodo 'spacy'Senza gli extra, il metodo regex (gazetteer, P≈0.97) e' pienamente funzionante; gli altri
metodi risultano non disponibili (aiuti methods li marca con ·) finche' non installi l'extra.
aiuti info # config risolta + file annuali trovati
aiuti count # record totali e per anno (DuckDB)
aiuti methods # metodi registrati e disponibilità
aiuti evaluate --methods regex # confronto metodi sul goldset (regex: micro_P≈0.97)
aiuti pipelines # pipeline predefinite e loro stage
aiuti run ai_tech --years 2016 --formats parquet,csv # produce out/ai_tech/2016.{parquet,csv}
aiuti run ai_gate # gate AI sottrattivo sull'intero corpus
aiuti run traceability # record di tracciabilità + profilazione
aiuti report year_share # trend AI per anno (via DuckDB) -> out/report/
aiuti report multiclass # distribuzione ambiti applicativi
aiuti report regions # importi AI per regione
aiuti endpoints # endpoint di inferenza configurati
aiuti ping bert_binary # health check di un endpoint
aiuti classify --endpoint bert_binary --out-col CLASSIF_NEW --years 2016Per lanciare classificazioni nuove — non previste dalla pipeline legacy — punta l'app a un
modello BERT servito in un container su host:porta (localhost o rete locale del dipartimento).
Due protocolli, scelti con protocol:.
NVIDIA Triton (protocollo KServe v2):
endpoints:
bert_binary:
host: 192.168.1.50 # container Triton su LAN
port: 8000 # porta HTTP di Triton
protocol: triton
model_name: bert_binary # nome nel model repository
input_name: INPUT # tensore di input (da config.pbtxt)
batch_size: 512 # l'endpoint accetta batch di 512 testi
labels: [NON_AI, AI] # argmax dei logit -> nome classeCostruisce POST /v2/models/{model_name}/infer con inputs BYTES, health su
/v2/health/ready. Parsing: output numerici (logit/probabilità) → argmax per riga mappato via
labels; output BYTES → etichette dirette o formato classificazione score:index:label.
simple (FastAPI/TorchServe custom): POST {texts:[...]} → {predictions:[{label,score}]},
con chiavi input_key/output_key/label_key/score_key configurabili; gestisce anche il
formato HF all-scores.
Nessuna dipendenza esterna (urllib stdlib). Usalo da CLI (aiuti ping, aiuti classify --endpoint …)
o dalla scheda 🔌 Inferenza BERT della web UI (test connessione, prova su testo singolo,
classificazione dell'intero corpus con download).
L'app non è legata al CSV. Il formato e i nomi delle colonne si impostano in aiuti.yml
(o dalla scheda Configurazione della web UI — vedi sotto), senza toccare il codice.
annual_glob: "dati_*.xlsx" # il pattern determina i file; l'estensione dà il formato
source_format: auto # auto | csv | json | parquet | xlsx (auto = da estensione)
# Se il tuo dataset usa nomi di colonna diversi, mappali sui nomi canonici dell'app.
# Sinistra = nome usato internamente; destra = nome nel TUO file.
column_map:
DESCRIZIONE_PROGETTO: descrizione
CLASSIFICAZIONE: esito
IMPORTO_NOMINALE_TOTALE: importoLa traduzione avviene in lettura (un solo punto, aiuti_lab.sources): oracoli, pipeline e report
continuano a vedere i nomi canonici. Excel funziona out-of-the-box (openpyxl è incluso).
pip install -e ".[ui]"
streamlit run webapp/app.pyCinque schede — Configurazione (imposta cartella dati, formato, mappatura colonne ed
endpoint e salva su aiuti.yml, tutto dai menu, senza editare file a mano; solo admin),
Corpus (conteggi + report con grafici), Pipeline (produzione dataset con download
parquet/CSV/dta), Inferenza BERT (classificazione via endpoint), Evaluate (confronto
metodi sul goldset). È un layer sottile: chiama solo aiuti_lab.*, hostabile in dipartimento.
Per lo sviluppo locale senza lo stack di autenticazione: AIUTI_AUTH_MODE=dev streamlit run webapp/app.py.
Chi fa questi passi. L'installazione e la configurazione (segreti, DNS, CA, utenti LDAP, 2FA) sono compiti dell'amministratore tecnico del dipartimento, una volta sola. Gli utenti finali (professori) non toccano nulla di tutto ciò: accedono via browser al portale sicuro e usano la web UI già pronta.
Un solo comando avvia app + autenticazione. Architettura Gatekeeper: è pubblicato solo il reverse proxy (Caddy), che verifica login e secondo fattore con Authelia prima di far passare qualunque richiesta a Streamlit. Identità/ruoli stanno in LLDAP (con UI web di amministrazione) e PostgreSQL; le sessioni in Redis. L'app non è mai esposta senza login.
cp .env.example .env
# genera i segreti (una volta) e incollali in .env:
openssl rand -hex 32 # per AUTHELIA_SESSION_SECRET, AUTHELIA_STORAGE_ENCRYPTION_KEY,
# AUTHELIA_JWT_SECRET, LLDAP_JWT_SECRET
openssl rand -base64 24 # per le password: POSTGRES/AUTHELIA_DB/LLDAP_DB/REDIS/LLDAP_ADMIN
# imposta anche DATA_DIR (cartella dati) e DOMAIN (default aiuti.local)
docker compose up -dSui client aggiungi al DNS interno (o a /etc/hosts): aiuti.local auth.aiuti.local users.aiuti.local → IP del server. WebAuthn richiede HTTPS: Caddy usa una CA interna
(importa la sua root CA nei browser per togliere l'avviso, oppure accetta il certificato).
Primo avvio (admin):
https://users.aiuti.local→ login LLDAP comeadmin(passwordLLDAP_ADMIN_PASSWORD). Crea i gruppiaiuti-userseaiuti-admins, metti te stesso inaiuti-admins.- Crea gli account degli altri (username, email, password) e assegnali a
aiuti-users. - Ogni utente, al primo accesso su
https://aiuti.local, fa login e registra il 2FA (passkey/WebAuthn o app TOTP). Il link di registrazione (notifier su file) si legge con:docker compose exec authelia cat /config/notification.txt(in produzione: configurare SMTP).
Ruoli (RBAC): chi è in aiuti-admins vede la scheda Configurazione (scrittura globale);
gli altri usano analisi, pipeline, inferenza. Più utenti lavorano in contemporanea con sessioni
isolate. La cartella dati resta in sola lettura; gli output persistono in ./out.
I certificati della CA interna sono persistiti (
caddy_data); i segreti stanno solo in.env(git-ignored). Per la massima sicurezza si possono usare i Docker secrets al posto di.env.
aiuti_lab.config— Settings (Pydantic): unica sorgente di path/worker/chunk.aiuti_lab.corpus— Data Access Layer (Repository): DuckDB per gli scan, pandas a chunk per il lavoro riga-per-riga,map_parallelcome UNICO punto conPool/fork.aiuti_lab.text/aiuti_lab.schema— fix pandas 3.0 Arrow/RE2 e nomi colonna centralizzati.aiuti_lab.taxonomies— oracoli unici di label (ai_tech, traceability v2, ai_gate, tipo_ai, profiling); gli invarianti di correttezza sono coperti da test.aiuti_lab.methods— classificatori pluggable (Strategy + Registry + Factory): regex, spacy, transformer, llm, ensemble (primario + fallback).aiuti_lab.pipeline— stage componibili (Chain) + runner cache-first + export parquet/CSV/STATA.aiuti_lab.evaluate/aiuti_lab.report— metriche sul goldset e analisi/figure parametriche.webapp/auth.py— adapter d'identità (Gatekeeper): legge l'identità già verificata dal reverse proxy (Authelia) e fa da unico punto percurrent_user()/is_admin(). L'app dipende da queste funzioni, non dagli header (Dependency Inversion): passare a OIDC cambierebbe solo qui.
pytest # 74 test: oracoli, DAL, formati (csv/json/parquet/xlsx) + mappatura colonne,
# metodi, pipeline end-to-end, export .dta, inferenza (simple/Triton),
# autenticazione (RBAC/dev-fallback/deny) + UI smoke (admin-gating)