Metadata-Version: 2.4
Name: aiuti-analytics
Version: 0.1.0
Summary: Libreria modulare per l'analisi degli aiuti di stato italiani (OpenData 2014-2025): data-access DuckDB, motore di analisi componibile e agnostico, classificatori pluggable, pipeline, export.
Project-URL: Homepage, https://github.com/Vinello28/aiuti-lab
Project-URL: Repository, https://github.com/Vinello28/aiuti-lab
Project-URL: Issues, https://github.com/Vinello28/aiuti-lab/issues
Author-email: Gabriele Vianello <vianello.tech@gmail.com>
License: MIT
License-File: LICENSE
Keywords: data-analysis,duckdb,etl,italy,opendata,pandas,state-aid
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.12
Requires-Dist: duckdb>=1.0
Requires-Dist: openpyxl>=3.1
Requires-Dist: pandas>=2.2
Requires-Dist: pyarrow>=15
Requires-Dist: pydantic>=2.5
Requires-Dist: pyyaml>=6
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: llm
Requires-Dist: openai>=1.30; extra == 'llm'
Provides-Extra: nlp
Requires-Dist: spacy>=3.7; extra == 'nlp'
Requires-Dist: transformers>=4.40; extra == 'nlp'
Provides-Extra: report
Requires-Dist: matplotlib>=3.8; extra == 'report'
Requires-Dist: seaborn>=0.13; extra == 'report'
Requires-Dist: wordcloud>=1.9; extra == 'report'
Provides-Extra: ui
Requires-Dist: matplotlib>=3.8; extra == 'ui'
Requires-Dist: seaborn>=0.13; extra == 'ui'
Requires-Dist: streamlit>=1.30; extra == 'ui'
Requires-Dist: wordcloud>=1.9; extra == 'ui'
Description-Content-Type: text/markdown

# aiuti-lab

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.

## Setup

```bash
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.

## CLI

```bash
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 2016
```

## Inferenza su endpoint BERT (host:porta)

Per 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):

```yaml
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 classe
```

Costruisce `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).

## Dati in altri formati (Excel, JSON, Parquet) e altri schemi

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.

```yaml
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: importo
```

La 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).

## Web UI (Streamlit)

```bash
pip install -e ".[ui]"
streamlit run webapp/app.py
```

Cinque 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`.

## Deploy sicuro sul server (Docker) — multi-utente con login + 2FA

> **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.

```bash
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 -d
```

Sui **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):**
1. `https://users.aiuti.local` → login LLDAP come `admin` (password `LLDAP_ADMIN_PASSWORD`).
   Crea i gruppi `aiuti-users` e `aiuti-admins`, metti te stesso in `aiuti-admins`.
2. Crea gli account degli altri (username, email, password) e assegnali a `aiuti-users`.
3. 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`.

## Architettura

- `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_parallel` come UNICO punto con `Pool`/`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 per `current_user()`/`is_admin()`. L'app dipende
  da queste funzioni, non dagli header (Dependency Inversion): passare a OIDC cambierebbe solo qui.

## Test

```bash
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)
```
