--- name: project-myicr-flussi-knowledge description: "Base di conoscenza logistica dei flussi dati MyICR_Suite/PowerBricks (pipeline, provenienza, trasformazioni) — costruita con Mauro, letta dal subagente myicr-flussi-expert" metadata: type: project --- Conoscenza sulla logistica dei flussi dati per l'ecosistema MyICR_Suite/PowerBricks (Mediaset), costruita insieme a Mauro a partire dal 2026-07-17. Letta a inizio task dal subagente `myicr-flussi-expert` (`.claude/agents/myicr-flussi-expert.md`). **Why:** complementare a [[project_powerbricks_knowledge]] (che documenta la semantica di business dei dati as-is). Questo documento tiene traccia di dove nascono i dati (OnAir e altre fonti), che pipeline li trasformano (Frank, DataHub_v2, ParquetToAccess, PipelinePython), e le anomalie diagnosticate risalendo al codice a monte — non la semantica dei campi, quella resta nell'altra KB. **How to apply:** il subagente `myicr-flussi-expert` scrive qui direttamente, in parallelo al lavoro. Ogni voce porta un tag di stato: `[CONFERMATO]` (validato con Mauro/Adrian) o `[IPOTESI]` (dedotto da codice/dati, non ancora validato). Sezioni vuote finché non emerge conoscenza reale. ## 🔥 Domande aperte (tenere calde — aggiornare a ogni sessione) 1. **Consumer di `db_Osservatorio.accdb`/`tabOsservatorio`** — trovato nel codice (`integrazione_update_day()`), consumer reale non identificato. 2. **`gemma.parquet` ↔ Acquisti (W:\)** — collegamento mai verificato a voce con Mauro (solo ipotizzato), a differenza di Osservatorio/MonitorOsservatorioPy che è stato chiuso per intero. 3. **`ICR_MONDO`** — prefissi noti (`CON`, `MED`, `SKY`, `FOX`, `PREMIUM`-obsoleto), ma il significato esatto di `CON`=Concorrenza / `MED`=Mediaset non confermato a voce. 4. **`sync_DEV_LOCAL.bat`** — ponte ipotizzato tra `I:\` (output pipeline) e il path Dropbox `MyICR_Suite/local_db/parquet` che leggiamo — file gitignored, non verificabile da Nave, non confermato al 100%. 5. **Fix "canale secondario" DVB-T** (v. Passo 18) — non una domanda ma un lavoro concreto da fare quando/se Mauro deciderà: esporre in MyICR_Suite/PowerBricks che un prodotto è arrivato tramite il workaround DVB-T, oggi invisibile. *(v. anche `project_powerbricks_knowledge.md` per le domande aperte lato semantica dati: scala `veg` A-H criterio di attribuzione, `situazione`/`causale` in diritti mai approfonditi, perché coesistono tre codifiche rete, contenuto business di linker.db/ott.db/mediatrack.db mai interrogato)* ## Provenienza e sistemi sorgente `[CONFERMATO]` (2026-07-17, ereditato da [[project_powerbricks_knowledge]]) — quasi tutti i parquet in `PYTHON/MyICR_Suite/local_db/parquet/` arrivano da **OnAir** (sistema aziendale Mediaset), eccetto `gemma.parquet`, `imdb_full.parquet`, `reti_cluster.parquet`. ## Pipeline note (schema, non ancora indagate nel dettaglio) `[IPOTESI]` (da `PYTHON/CLAUDE.md`, non ancora verificato con lettura diretta del codice): - **Frank** — orchestratore job ETL, coordina ImdbUpdate, GemmaReport, DataHub_v2, ecc. - **PIPELINE_PYTHON** (`I:\SOFTWARE\SCHEDULATORE\PIPELINE_PYTHON`, **non raggiungibile da Nave**) — genera il mirror SQLite dei parquet L2 per query Access ("sqlite_mirror", nota 2026-04-27 in CLAUDE.md). - **DataHub_v2**, **ParquetToAccess** (`I:\SOFTWARE\SCHEDULATORE\UPDATE_PYTHON\...`, **non raggiungibili da Nave**) — ruolo esatto ancora da chiarire. ## Catena completa fino ai parquet — in costruzione (2026-07-17) `[CONFERMATO]` (Mauro, 2026-07-17) — **punto di partenza della catena**: tutto parte dalla directory `I:\SOFTWARE\SCHEDULATORE`. Contenuto già osservato via Frank-relay (list_dir): `FTP`, `INPUT`, `PIPELINE_PYTHON`, `UPDATE_PYTHON`, `UPDATE_PYTHON_WEEKLY`, `vecchia procedura VBA`. Mauro segnala che la catena da qui fino ai parquet finali è **lunga e complessa** — costruzione passo-passo in corso, non riassumere/semplificare finché non è raccontata per intero. **Passo 1 — primo problema identificato (Mauro, 2026-07-17)**: `[CONFERMATO]` — il codice che gestisce la pipeline si trova **solo** in `I:\SOFTWARE\SCHEDULATORE\PIPELINE_PYTHON` — segnalato esplicitamente da Mauro come "il primo problema" (natura del problema da chiarire: fragilità/single point of failure? mancanza di versionamento git? non ancora specificato, in attesa del seguito del racconto). **Implementato (2026-07-17, sessione ufficio)**: `[CONFERMATO]` — copia versionata creata in `Dropbox/adrian/PYTHON/PIPELINE_PYTHON/`. Cosa è stato fatto: - Robocopy da `I:\SOFTWARE\SCHEDULATORE\PIPELINE_PYTHON` → `PYTHON/PIPELINE_PYTHON/` escludendo `.git`, `.claude`, `LOG/`, `__pycache__/`, `logs/`, `parquet/`, `parquet_layer1/`, `*.pkl`, `*.accdb`, `*.db` - `.gitignore` aggiornato (aggiunto `.claude/`) - `CLAUDE.md` minimale creato (v1, eredita Costituzione, documenta architettura e gotcha principali, punta a CLAUDE.md originale su I:\ per dettaglio completo) - `deploy.bat` creato con robocopy inverso (PYTHON/ → I:\), con pausa di conferma, da usare solo manualmente - Mappa progetti in `PYTHON/CLAUDE.md` aggiornata - `I:\SOFTWARE\SCHEDULATORE\PIPELINE_PYTHON` non toccata in alcun modo **Passo 2 — origine dei file di input (Mauro, 2026-07-17)**: `[CONFERMATO]` — i file grezzi vengono postati **dalla Direzione Sistemi Mediaset, in automatico**, dentro `I:\SOFTWARE\SCHEDULATORE\INPUT`. Cadenza mista: alcuni file arrivano con frequenza **settimanale**, altri **giornaliera**. Collegamento con quanto già noto: la **Direzione Sistemi** è la stessa citata in precedenza come tramite per `osservatorio.parquet` — probabilmente lo stesso meccanismo generale di deposito file (Direzione Sistemi → `INPUT/` → pipeline), non solo per il caso Osservatorio. **Correzione importante — dove gira davvero il codice (Mauro, 2026-07-17)**: `[CONFERMATO]` — il Python che fa l'ingestione **non gira sulla macchina di Mauro (A)**, gira su **un'altra macchina della Direzione Sistemi**. Loro lanciano: - `I:\SOFTWARE\SCHEDULATORE\UPDATE_PYTHON\start.bat` per i caricamenti **giornalieri** - `I:\SOFTWARE\SCHEDULATORE\UPDATE_PYTHON_WEEKLY\start.bat` per quelli **settimanali** Quindi `PIPELINE_PYTHON` (appena copiato in `PYTHON/`) è il codice sorgente/repository, ma l'esecuzione avviene tramite `UPDATE_PYTHON`/`UPDATE_PYTHON_WEEKLY` su una macchina Direzione Sistemi, non tramite Frank né sulla macchina A. `[IPOTESI aperta]`: non ancora chiarito se `UPDATE_PYTHON`/`UPDATE_PYTHON_WEEKLY` siano una copia deployata di `PIPELINE_PYTHON` (stesso codice, posizione diversa per l'esecuzione) o moduli/wrapper distinti che richiamano `PIPELINE_PYTHON` — da verificare quando si guarda dentro quelle due cartelle. **Nome macchina esecutrice (Mauro, 2026-07-17)**: `[CONFERMATO]` — la macchina Direzione Sistemi che esegue `start.bat` si chiama **`wincoarcapp01`**. **Accesso remoto disponibile ma non usato (Mauro, 2026-07-17)**: `[CONFERMATO]` — Mauro ha la possibilità di accesso remoto a `wincoarcapp01` se volesse, ma in pratica non gli serve: la macchina non ha mai problemi, e il suo ruolo è puramente eseguire il Python (`start.bat`) — non fa nient'altro di suo, è solo l'host di esecuzione. **Passo 3 — architettura completa ricostruita da Adrian (2026-07-17, letta da `PYTHON/PIPELINE_PYTHON/CLAUDE.md` appena copiato)**: `[CONFERMATO]` (fonte: documentazione ufficiale del progetto, non inferenza) — invitato da Mauro a ricostruire da qui in poi usando gli strumenti disponibili. Catena completa: ``` run_weekly.bat / run_daily.bat ← lanciato da Direzione Sistemi su wincoarcapp01 │ ├─ [1] Venv check C:\venv_ICR (auto-crea se assente, pacchetti: pyodbc pywin32 duckdb pandas loguru) │ (NON lo stesso venv di sviluppo C:\PYTHON\_runtimes\venv su macchina A) │ ├─ [2] CsvToParquet_layer1 │ CSV sorgente (da INPUT/, postati da Direzione Sistemi) → Parquet Layer 1 locali │ → C:\PYTHON_LOCAL\CsvToParquet_layer1\parquet\ │ ├─ [3] Pubblica Layer 1 su rete │ → I:\SOFTWARE\PYTHON_SRV\parquet_layer1\ │ ├─ [4] Parquet_layer1ToParquet_layer2 │ L1 rete → L2 rete (trasformazioni business + enrich), solo DuckDB │ → I:\SOFTWARE\PYTHON_SRV\PYTHON_LOCAL\MyICR_Suite\local_db\parquet\ │ ⚠️ QUESTO è (presumibilmente) l'origine dei parquet che analizziamo in powerbricks-expert │ (diritti.parquet, prodotti.parquet, emesso.parquet, ecc.) — path da riconciliare │ con `Dropbox/adrian/PYTHON/MyICR_Suite/local_db/parquet/` che leggiamo noi: stesso file │ sincronizzato in qualche modo, o due copie distinte? [IPOTESI aperta, da verificare] │ ├─ [5, solo weekly] SQLite mirror L2 (`sqlite_mirror.py`) │ L2 parquet → mirror_l2.sqlite │ → I:\SOFTWARE\PYTHON_SRV\sqlite_layer2\mirror_l2.sqlite │ ⚠️ Relazione con i mirror SQLite che leggiamo noi (linker.db, ott.db, mediatrack.db in │ `sync_db/`/`local_db/`) non ancora chiarita — nomi diversi, da verificare se è lo stesso │ file rinominato/ricollocato o un mirror parallelo. [IPOTESI aperta] │ ├─ [6/7, weekly] ParquetToAccessWeekly → DB_WEEK.accdb (6 tabelle Access) │ → I:\SOFTWARE\ProcedureICR\ICRmonitor\MonitorLinker\DB_WEEK.accdb │ └─ [6/7, daily] ParquetToAccessDaily → DB_DAY.accdb (enrichment giornaliero) → I:\SOFTWARE\ProcedureICR\ICRmonitor\MonitorLinker\DB_DAY.accdb ``` **Moduli**: `CsvToParquet_layer1/` (CSV→L1, CLAUDE.md proprio), `Parquet_layer1ToParquet_layer2/` (L1→L2 + `sqlite_mirror.py`), `ParquetToAccessWeekly/`, `ParquetToAccessDaily/` (CLAUDE.md proprio), `WriteToAccess/` (mattoncino riusabile DataFrame→Access via TransferText). **Gotcha rilevanti per l'interpretazione dei dati** (utili anche per `powerbricks-expert`): - **Date sentinella**: `2201-01-01` (lato parquet) diventa `9999-12-31` (lato VBA/legacy); `0001-01-01` diventa `1001-01-01` (Access non gestisce anni <100) — **spiega retroattivamente** il valore `decr=1001-01-01` osservato in `diritti.parquet` durante l'esplorazione del blocco WIN (sessione precedente, causale '01'/'03') — non un dato sporco, è una sentinella nota della pipeline. - **TCDURLORDA**: formula secondi→minuti `(x + 10) // 60`, soglia 50s (non 30s come si potrebbe assumere) — coerente con lo scarto residuo ±1min segnalato nel parallelo Python vs VBA su CUSTOM_EMESSO (97,47% match). - Separatore decimale italiano (Access usa virgola), schema.ini order-sensitive, Access non deve essere aperto durante i run. **Stato parallelo Python vs VBA (dal CLAUDE.md, post-fix 2026-04-23)**: CUSTOM_EMESSO 97,47%, CUSTOM_ANAGR_ORIZ 99,76%, EDIZIONI 100%, SUPERSERIE 99,84%, CUSTOM_DIRITTI 100%, BOXOFFICE 99,55% — la pipeline Python è quindi già validata contro il legacy VBA con match altissimo, non un sistema nuovo/non testato. **Passo 4 — dettaglio codice `CsvToParquet_layer1` (Adrian, 2026-07-17, letto codice + CLAUDE.md interno del modulo, non solo il riepilogo)**: `[CONFERMATO]` (fonte diretta: codice + documentazione del modulo) - **Nome storico**: questo modulo si chiamava **"DataHub v2"** internamente (v. suo CLAUDE.md, non ancora rinominato ovunque) — `[IPOTESI]`: probabile che sia la stessa cosa della voce `DataHub_v2` in `PYTHON/CLAUDE.md` (mappa progetti, path `I:\SOFTWARE\SCHEDULATORE\UPDATE_PYTHON\DataHub_v2`), solo con nome/posizione diversi tra i due documenti — da riconciliare, non dato per certo. - **`INPUT_DIR` reale** (da `config/settings.py`): `\\mediaset.it\share\Indirizzo_controllo_risorse\SOFTWARE\SCHEDULATORE\INPUT` — **conferma testuale diretta** che "Indirizzo e Controllo Risorse" (ICR) è nel nome della share di rete, non solo un acronimo detto a voce. - **Mapping CSV → parquet** (da `CSV_FILES` in `settings.py`): `prodotti←estr_prod.csv`, `emesso←estr_emesso.csv`, `diritti←estr_acq.csv` (nome file interno: "acq", coerente con l'ipotesi che i diritti siano prevalentemente contratti di **acq**uisizione), `imdb←estr_imdb.csv`, `boxoffice←estr_box_totali.csv`, `boxoffice_det←estr_box_dettaglio.csv`, `cast←estr_cast.csv`, `veg←estr_veg.csv`, `scelte_rete←estr_scelte_rete.csv`, `gemma←estr_gemma.csv`, `osservatorio←osservatorio.txt` (unico senza prefisso `estr_`, formato posizionale senza header). - **`osservatorio.txt` ha più colonne del parquet finale**: il file sorgente elenca 25 colonne inclusi `titolo_int`, `produzione`, `distribuzione`, `sceneggiatura`, `showrunner`, `anno_screening` — non tutte viste nello schema di `osservatorio.parquet` esplorato in sessione 1 (dove ne avevamo contate meno). `[IPOTESI aperta]`: alcune colonne vengono scartate/rinominate nella trasformazione CSV→parquet, da verificare con un confronto diretto schema-vs-schema. - **`weekly.py` produce tutti i parquet; `daily.py` produce solo `gemma.parquet` e `osservatorio.parquet`** — quindi questi due dataset hanno una cadenza di aggiornamento più frequente (giornaliera) rispetto al resto (settimanale). - **Filosofia "bidone"**: parquet = dato unico trasversale per tutto l'ecosistema (MyICR_Suite, DBeaver, tool futuri). Tabelle raw fedeli al CSV (solo dedup+type parsing), tabelle `_enrich` con colonne derivate lette dal parquet stesso (non dal CSV) — **niente logica di business nelle tabelle raw**, coerente con la nostra regola "prendi i dati as-is". - **Ponte trovato con il path Dropbox che leggiamo noi**: dal CLAUDE.md del modulo — *"Sync su A: `C:\PYTHON\MyICR_Suite\sync_DEV_LOCAL.bat` include sync Parquet"*. `[IPOTESI, ma molto probabile]`: questo è il meccanismo che porta il parquet da `I:\SOFTWARE\PYTHON_SRV\...\MyICR_Suite\local_db\parquet\` (destinazione della pipeline) fino a `Dropbox/adrian/PYTHON/MyICR_Suite/local_db/parquet/` (quello che leggiamo noi) — un sync script sulla macchina A che gira dentro la cartella ora Dropbox-sincronizzata. Da confermare leggendo `sync_DEV_LOCAL.bat` in `MyICR_Suite/`. - **Gotcha DuckDB rilevanti**: date in parquet sono `%Y-%m-%d` (non `%d/%m/%Y` come nel CSV sorgente); fan-out da JOIN se `_prod_anno` ha più righe per prodotto, fix con `GROUP BY prodotto + MAX(anno_produzione)` — utile per capire eventuali duplicazioni inattese in analisi future. - **Roadmap dichiarata nel modulo**: "prossimo: integrare `run_weekly/daily_SYS_SRV.bat` in Frank" — conferma che oggi Frank **non** orchestra questa pipeline (coerente con quanto detto da Mauro: gira su `wincoarcapp01`, non tramite Frank), ma è previsto in futuro. **Passo 5 — letto `sqlite_mirror.py` per intero (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice diretto) - **`mirror_l2.sqlite` NON è `linker.db`/`ott.db`/`mediatrack.db`** — sono **sistemi distinti**, question chiusa. Il file `mirror_l2.sqlite` contiene UN SOLO database con le tabelle: `emesso`, `emesso_enrich`, `reti_cluster`, `osservatorio`, `imdb`, `scelte_rete`, `boxoffice`, `prodotti`, `diritti_enrich`, `diritti`, `cast`, `gemma` — copiate full da Layer 2 (con `_id` surrogato via `ROW_NUMBER()` per le tabelle senza PK intera, per compatibilità Access ODBC). Nessuna tabella `linker`/`ott`/`mediatrack` qui dentro — quei mirror devono avere una pipeline di provenienza completamente diversa (probabile: moduli interni MyICR_Suite — Linker, integrazione OTT, MediaTrack — non ancora indagati). - **Path L2 confermato per intero** (da codice, non ipotesi): `\\mediaset.it\share\Indirizzo_controllo_risorse\SOFTWARE\PYTHON_SRV\PYTHON_LOCAL\MyICR_Suite\local_db\parquet` — conferma che `I:\` = `\\mediaset.it\share\Indirizzo_controllo_risorse\SOFTWARE\` (stesso prefisso ICR visto anche in `INPUT_DIR`). - **Uso di `mirror_l2.sqlite`**: dichiarato nel docstring come "per uso con MS Access via ODBC" — quindi è un ponte verso Access, non verso MyICR_Suite/le nostre analisi. - **Verifica non completata**: `sync_DEV_LOCAL.bat` (il presunto ponte verso il path Dropbox che leggiamo) è **gitignored esplicitamente** in `MyICR_Suite/.gitignore` ("Solo su A, non deve andare su B/C(n)") — non leggibile da Nave, il collegamento resta un'ipotesi non verificata direttamente da codice. Da chiedere a Mauro o verificare da sessione ufficio. **Passo 6 — letto `ParquetToAccessDaily` per intero, codice + CLAUDE.md (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice diretto) - **⚠️ Scoperta importante — collegamento diretto `osservatorio.parquet` ↔ `MonitorOsservatorioPy`**: il job `osservatorio_to_db_day` (in `config/exports.json`) esporta `osservatorio.parquet` → `DB_DAY.accdb`, tabella **`SCHEDA_PRODOTTO`**. Questo è **lo stesso identico nome di tabella** che `MonitorOsservatorioPy` mirrora in `monitor.db` via ODBC all'avvio ("Tabella `SCHEDA_PRODOTTO` — mirror da `db.accdb` Access", v. sezione dedicata sopra). **Ipotesi fortemente supportata** (stesso nome tabella, stesso dominio Osservatorio): `DB_DAY.accdb` generato qui **è** (o è imparentato con) il `db.accdb` che `MonitorOsservatorioPy` legge — chiudendo quasi per intero la catena: `osservatorio.txt` (Direzione Sistemi) → `CsvToParquet_layer1` (daily) → `osservatorio.parquet` → `ParquetToAccessDaily` → `DB_DAY.accdb/SCHEDA_PRODOTTO` → `MonitorOsservatorioPy/monitor.db`. `[IPOTESI forte, non confermata a voce da Mauro]` — da validare esplicitamente: sono davvero lo stesso file `.accdb`, o due file distinti con schema identico per coincidenza? - **Altro job noto**: `gemma_to_db_day` — `gemma.parquet` → `DB_DAY.accdb`, tabella `GEMMA`. Consumer non ancora identificato (candidato: qualcosa lato Acquisti, coerente col filone `gemma.parquet`/W:\ non confermato in precedenza — v. sopra). - **⚠️ Natura dichiarata del modulo — target di smantellamento futuro**: il CLAUDE.md del modulo dice esplicitamente: *"Strato legacy temporaneo — esiste finché i consumer Access non vengono migrati a Parquet. Obiettivo finale: spegnere Access del tutto."* — **questo è precisamente il tipo di "pezzo da smantellare in futuro" che Mauro ha segnalato essere l'obiettivo di questa indagine**. `ParquetToAccessDaily`/`Weekly` sono ponti verso un sistema (Access) che si vuole eliminare, non componenti permanenti dell'architettura. - **Architettura**: `main.py` → legge `config/exports.json` (lista job: source parquet, target accdb, tabella, mode `replace`/`append`, mapping colonne opzionale) → per ogni job, `core/exporter.py` (DuckDB legge il parquet, pyodbc scrive in Access) → step finale di enrichment (`core/enrichment.py`) e pubblicazione su rete (copia il file `.accdb` locale verso destinazioni di rete elencate in `settings.json`). - **Girato su `wincoarcapp01`** con lo stesso venv `C:\venv_ICR` già visto, richiede **MS Access Application installata** (non solo driver ODBC) per la fase di enrichment. - **Gotcha rilevanti**: DAO/COM non funziona su path UNC (serve sempre pyodbc); `mode=replace` fa DROP+CREATE+INSERT (la struttura tabella viene ricreata ogni volta, non solo i dati); batch insert a chunk da 5000 righe per evitare timeout; Access non ha tipo DATE puro via ODBC, va sempre coerto a DATETIME. **Passo 7 — letto `ParquetToAccessWeekly/core/custom_diritti.py` per intero (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice diretto) — **rilevante anche per `powerbricks-expert`**, risolve domande aperte lì. - **Colonne `tipo_diritto`/`piattaforma` — CORREZIONE (v. Passo 16 sotto)**: non fanno parte del `diritti.parquet` che leggiamo in `powerbricks-expert` (L2) — vivono solo nella versione **L1 (grezza)** che questo codice legge (`ParquetToAccessWeekly` punta a `parquet_dir=C:\PYTHON_LOCAL\CsvToParquet_layer1\parquet`, non a L2). Nel nostro L2 quelle due colonne vengono rimosse dopo il filtro (verificato empiricamente: il nostro parquet non le ha). Non erano "colonne mancanti nel campione precedente" come pensavo inizialmente qui sotto — semplicemente non esistono nella versione che analizziamo. - **`fr_rr` — RISOLTO** (era `[IPOTESI aperta]` in `project_powerbricks_knowledge.md`): la logica `CalFrRr` calcola **F = First (prima occorrenza)**, **R = Repeat/rinnovo** — un diritto diventa `R` se esiste un'altra riga per lo stesso `PROD` con `DECR` (decorrenza) **antecedente** (escludendo l'anno sentinella 1001). Quindi `fr_rr`/`CalFrRr` è sostanzialmente **First-run vs Repeat/Rinnovo** del diritto sullo stesso prodotto — ipotesi confermata dal codice, va comunque validata a voce con Mauro prima di considerarla definitiva per `powerbricks-expert` (che resta la sede giusta per questa informazione, qui la registriamo come cross-reference). - **Nuovo termine scoperto**: `ICR_MONDO` (colonna di `CUSTOM_EMESSO`/probabilmente derivata da `emesso`), valori visti nel codice: `MED_GEN`, `MED_TEM` — usati per determinare se un prodotto è stato effettivamente trasmesso ai fini del calcolo disponibilità/inibizione. Significato esteso non ancora chiarito — `[IPOTESI aperta]`. - **Business logic di `calMotivoInibiz`** (motivo di inibizione per anno, dal 2015 a oggi+5): per ogni prodotto/anno, controlla se i diritti attivi in quell'anno sono "disponibili" o "inibiti", con motivi possibili: `"passaggi esauriti"` (se `PASS_CONS_TOT - PASS_EFF_TOT <= 0`), `"inibito"` (se `FLAG_INIB='S'`), `"<100%"` (se la somma percentuali diritti nel periodo è sotto 100%), `"<30gg"` (se i giorni di disponibilità nell'overlap sono <30). Se il prodotto risulta comunque trasmesso (`emesso_dates` con `ICR_MONDO` in `MED_GEN`/`MED_TEM`) nel periodo nonostante l'inibizione calcolata, il motivo viene azzerato (considerato disponibile). Logica applicata solo alle tipologie: FILM, MINISERIE, SIT COM, TELEFILM, TV MOVIE, SOAP, TELENOVELAS — **esattamente le tipologie core ICR già identificate in `powerbricks-expert`** (manca CORTOMETRAGGIO e DOCUMENTARI rispetto all'elenco core — da segnalare come discrepanza minore). - **`CUSTOM_DIRITTI` non è "tutti i diritti"**: è una vista filtrata — solo `tipo_diritto='FREE'` + `piattaforma='ANALOGICO'`, più un fallback per `piattaforma='DVB-T'` quando non esiste una riga Analogico corrispondente (stesso prod/decr/scad) — un workaround esplicito documentato nel docstring ("workaround DVB-T"). Quindi non tutti i diritti passano in questa tabella legacy, solo quelli rilevanti per la trasmissione free/analogica. - **Conferma sentinelle date nel codice**: `IF(decr = '0001-01-01', '1001-01-01', decr)` — stessa sentinella già documentata, vista ora applicata esplicitamente in query SQL. **Passo 8 — letto `custom_emesso.py` per intero (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice diretto) — **cross-validazione importante con `powerbricks-expert`**. - **Le 10 tipologie filtrate in `_TIPOLOGIE_EMESSO`** (`FILM, MINISERIE, SIT COM, TELEFILM, TV MOVIE, SOAP, TELENOVELAS, DOCUMENTARI, CORTOMETRAGGIO, TELEROMANZO`) **coincidono esattamente** con le 10 tipologie core ICR confermate a voce da Mauro in `project_powerbricks_knowledge.md` — commento nel codice: *"Tipologie 'scripted/acquistato' — contenuti che hanno diritti"*. Ottima conferma indipendente (codice vs dichiarazione a voce) che quella lista è corretta e stabile. - **`ICR_MONDO` chiarito ulteriormente**: viene da un file di lookup esterno `emesso_tyfx0160.csv`, joinato su `rete = CCRETETEL` — non è nel parquet `emesso` stesso, è un arricchimento da tabella di mapping rete→mondo. Filtro applicato: solo reti con `ICR_MONDO LIKE 'CON%'` o `'MED%'` (es. `MED_GEN`/`MED_TEM` già visti) — quindi `ICR_MONDO` ha più prefissi/categorie oltre a `CON`/`MED`, non ancora tutte censite. `[IPOTESI aperta]`: `CON` potrebbe stare per "Concorrenza" e `MED` per "Mediaset" — coerente con `emesso.parquet` che include anche reti concorrenti (già noto da sessione precedente) — da validare a voce. - **`PDF_ESISTE`**: flag business — vero solo per reti `C5`/`I1`/`R4`, fascia `PR` (probabile "PRIMA SERATA"), nelle date in cui esiste un PDF secondo la tabella `tabPdfEsiste` in un DB Access "warehouse" (`db_warehouse.accdb`) — presumibilmente traccia l'esistenza di un documento/palinsesto cartaceo/scansionato per la prima serata di quelle 3 reti principali. Dettaglio di nicchia, non approfondito oltre. - **`emesso.parquet` è "Layer 1"**: confermato dal docstring — tutte le reti, nessun dedup — `CUSTOM_EMESSO` applica poi il filtro CON/MED + tipologie sopra. **Passo 9 — letto `custom_anagrafica.py` per intero, il più ricco finora (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice diretto) — moltissimi cross-reference per `powerbricks-expert`. - **"RIFER" è letteralmente il nome di colonna nel codice** (`prodotto AS RIFER`) — conferma definitiva, non solo terminologia parlata, che è il nome ufficiale usato anche nelle pipeline. - **`TIPOL_MAP` — codici legacy VBA a 1 carattere per tipologia** (campo Access `VARCHAR(1)`): FILM=`F`, TV MOVIE=`M`, CORTOMETRAGGIO=`7`, DOCUMENTARI=`D`, TELEFILM=`T`, MINISERIE=`E`, SIT COM=`G`, TELENOVELAS=`A`, SOAP=`O`, TELEROMANZO=`Z` — utile tabella di riferimento se si incontrano questi codici altrove (es. in report legacy). - **Mapping `veg` → codice breve Access**: `DA ATTRIBUIRE→N.C`, `UNIVOCA→UNIV.`, `EVER GREEN→EV.GR`, `FICTION AUTOPRODOTTA→F-AUT`, **`FICTION AUTOPRODOTTA RAI→NULL`** (esclusa esplicitamente, coerente con "meno affidabile" già segnalato da Mauro), `NON GESTITA→N.G`, `SUPER TOP→SUPER`, `VISTO-DA ATTRIBUIRE→VISTO`; le lettere A-H passano invariate. - **`DF_ICR_FLAG` — semantica scoperta** (flag business sui diritti free/analogico di un prodotto): `'I'` = inibito (`flag_inib='S'`), `'+'` = più contratti attivi contemporaneamente (append), `'S'` = scaduto/Senza diritti attivi, `'T'` = solo DVB-T (nessun Analogico) — con logica di override esplicitamente replicata dal comportamento legacy VBA (il commento nel codice descrive un workaround storico: "VBA inseriva DVB-T come 'analogico' PRIMA della logica DF_*"). - **`tipo_diritto` ha almeno 3 valori**: `FREE`, `TVOD` (Transactional Video On Demand — nuovo valore mai visto prima), e implicitamente altri non filtrati qui. - **Decodifica `scelte_rete` — rete e slot per esteso** (mai vista prima, utile per completare lo schema di `scelte_rete.parquet` in `powerbricks-expert`): - Reti: `CANALE 5→C5`, `ITALIA 1→I1`, `RETEQUATTRO→R4`, `LA 5→L5`, `IRIS→IR`, `CINE34→C34`, `TWENTYSEVEN→C27`, `ITALIA 2→I2`, `20→20`, `FOCUS→FO`, `TOP CRIME→TC` - Slot: `NOTTE→NO`, `PRIMETIME GARANZIA→PT G`, `MATTINA→MA`, `POMERIGGIO→PO`, `SECONDA SERATA GARANZIA→SS G`, `PRIMETIME ESTATE→PT E`, `SECONDA SERATA ESTATE→SS E`, `ACCESS PRIMETIME GARANZIA→AP G`, `PRIMETIME STRENNE→PT S`, `SECONDA SERATA STRENNE→SS S`, `ACCESS PRIMETIME STRENNE→AP S`, `ACCESS PRIMETIME ESTATE→AP E` - **`ICR_MONDO` — lista più ampia di categorie** scoperta qui: `CONCOR`, `MED_GEN` (generaliste), `MED_TEM`, `CON_TEM` (tematiche), `SKY`, `FOX` (satellite), `PREMIUM` (legacy, commento nel codice: *"VBA eliminava i record Premium come obsoleti"* — categoria di fatto dismessa). - **Ruoli cast identificati**: `'FRE'` = regista (usato per popolare REGISTA_NOME/COGN), `'C001'` = attore principale (i primi 3 per `progr_cast` diventano ATTORE1/2/3). - **Boxoffice — logica di aggregazione precisa**: prima uscita = `MIN(data_debutto)` tra i soli record `tipo_programmazione='D'` (debutto); incasso/spettatori sommati **solo per l'anno solare della prima uscita**, non su tutta la storia. - **Logica stagione corrente**: cutoff 10 settembre — dopo quella data la stagione è `anno/anno+1`, prima è `anno-1/anno`. **Passo 10 — letto `boxoffice.py` (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice diretto) - **Mappa completa rete→broadcaster** (`_RETE_TO_NETWORK`, decine di codici canale): gruppi risultanti sono `MEDIASET` (generaliste C5/I1/R4 + tematiche + Premium legacy), `RAI` (R1/R2/R3/RAI4/RAIM/RAIP), `LA7` (LA7/LA7C/LA7D), `SKY` (canali tematici + ex-FOX confluiti in Sky), `BOING`, `DISCOVERY` (+NOVE, canali correlati), `PARAMOUNT`, `SONY` — è di fatto la mappa competitiva completa usata dalla pipeline per calcolare `RETE_FREE`/broadcaster di prima TV. Utile riferimento se serve raggruppare `emesso.parquet`/reti per competitor. - **`BOXOFFICE` (vista legacy) filtra solo prodotti con uscita cinema dal 2010** (`BOX_DATA >= 2010-01-01`) — coerente con l'essere una vista storica per confronto VBA, non tutto `boxoffice.parquet`. - **`PRIMA_TV_LIBERA`**: prima trasmissione free (generalista o tematica, la più antica tra `EM_PG_*`/`EM_PT_*`), formattata come stringa `"RETE dd/mm/yy HH.MM"` — poi usata per derivare il broadcaster. **Passo 11 — letto `WriteToAccess/writer.py` (Adrian, 2026-07-17)**: `[CONFERMATO]` — il "mattoncino" riusabile che tutti i moduli usano per scrivere DataFrame→Access. Conferma i gotcha già noti dal CLAUDE.md (fix decimali italiani, `schema.ini` con ordine colonne del DataFrame, gestione date/None) — nessuna sorpresa rispetto a quanto già documentato, coerente e ben fattorizzato. **Passo 12 — ricerca provenienza `linker.db`/`ott.db`/`mediatrack.db` (Adrian, 2026-07-17)**: `[IPOTESI aperta, NON risolta]` — cercato dentro `PYTHON/MyICR_Suite/app/` per capire dove questi tre mirror SQLite vengono generati. **Non ancora trovato un generatore esplicito** nel codice esplorato finora — sono presenti come file dati (`sync_db/linker.db`, `sync_db/mediatrack.db`, `local_db/ott.db`, `local_db/linker.db`) ma non ho ancora individuato lo script/job che li scrive. Ipotesi da verificare: (a) generati da moduli interni di MyICR_Suite (`app/modules/linker/`, integrazione OTT, MediaTrack — nomi che corrispondono a moduli visti in `PYTHON/CLAUDE.md`), non dalla pipeline SCHEDULATORE; (b) alimentati da fonti esterne (es. OTT da piattaforme streaming, MediaTrack da un sistema terzo) più che da CSV Direzione Sistemi. **Prossimo passo consigliato**: usare `myicr-flussi-expert`/`glob` per cercare riferimenti a `linker.db`/`ott.db`/`mediatrack.db` dentro `app/modules/` — non ancora fatto in questa sessione per limiti di tempo, segnato come thread aperto prioritario. **Passo 13 — RISOLTO: provenienza `linker.db`/`mediatrack.db`** (Adrian, 2026-07-17, `grep` mirato + letti `app/modules/linker/CLAUDE.md` e `MyICR_Suite/CLAUDE.md`): `[CONFERMATO]` (codice/doc diretti) - **`linker.db`** = repository sidecar SQLite del modulo **Linker** (PyQt6, erede del vecchio tool VBA "MyICR" in Excel) — **non generato dalla pipeline SCHEDULATORE**, è lo storage delle scritture proprie dell'app Linker (aggancio manuale RIFER↔titoli, valutazioni, ecc.). Migrazione da MS Access completata il 2026-04-29 — prima usava Access, ora SQLite + Parquet DataHub_v2 in sola lettura per i dati di riferimento. - **`mediatrack.db`** = aggiornato dal job Frank **"OMDB Batch Enrich"** (attivo, lun-ven 13:30 — già in elenco job Frank nella Costituzione). Server = fonte di verità: `I:\SOFTWARE\PYTHON_SRV\sync_db\mediatrack.db`, sincronizzato via `pull_and_preserve_local()` in `launcher_src/shared_db_sync.py` (pull server → merge locale → push modifiche nuove, ogni 5 min + al quit app). - **`ott.db`**: non ancora trovato un generatore esplicito, ma dato il pattern (modulo `ott_sheet.py` dentro Linker) probabile sidecar analogo, gestito dallo stesso modulo Linker — `[IPOTESI]`, non verificato quanto gli altri due. **⚠️ Scoperta rilevante — rischio operativo reale (non solo curiosità)**: `MyICR_Suite/CLAUDE.md` documenta che **`\\mediaset.it\share\Indirizzo_Controllo_Risorse` è un namespace DFS con repliche fuori sync dal 17/03/2026** — regola esplicita: *"Non usare mai UNC nel codice"*, va sempre usato il path locale/drive mappato (`I:\`) invece. Questo è esattamente il tipo di "pezzo da conoscere per problemi futuri" segnalato da Mauro come motivazione dell'indagine — un disallineamento di replica DFS silenzioso potrebbe restituire dati stale a seconda di quale replica risponde, se qualcosa nel sistema usasse ancora UNC invece di `I:\`. **RISOLTO — "diritti.parquet solo FREE/Analogico-DVB-T" (Adrian, 2026-07-17)**: `[CONFERMATO]` (codice + verifica empirica diretta) — non era una discrepanza, era comprensione incompleta mia. Spiegazione completa: - **Esistono due `diritti.parquet` distinti, stesso nome, contenuto diverso**: L1 (grezzo, `C:\PYTHON_LOCAL\CsvToParquet_layer1\parquet\diritti.parquet` — fedele al CSV sorgente `estr_acq.csv`, contiene tutti i `tipo_diritto`: FREE, TVOD, IPTV, DVB-H, Internet) e **L2** (filtrato, `...\MyICR_Suite\local_db\parquet\diritti.parquet` — quello che leggiamo noi in `powerbricks-expert`). - **La trasformazione L1→L2** (`_transform_diritti()` in `Parquet_layer1ToParquet_layer2/main.py`) filtra esplicitamente: mantiene **tutti** i Free+Analogico, più i Free+DVB-T **solo se non coperti** da un Analogico per lo stesso prodotto/periodo (decr/scad) — esclude TVOD, IPTV, DVB-H, Internet, e i DVB-T ridondanti. Rimuove anche le colonne `tipo_diritto`/`piattaforma` dopo il filtro perché diventano ridondanti (tutto ciò che resta è Free by construction). - **Verifica empirica**: il nostro `diritti.parquet` (Dropbox, L2) **non ha le colonne `tipo_diritto`/`piattaforma`** — confermato via query diretta, coerente al 100% col codice. - **`ParquetToAccessWeekly` (custom_diritti.py/custom_anagrafica.py) legge L1, non L2**: il suo `config/settings.json` ha `"parquet_dir": "C:\\PYTHON_LOCAL\\CsvToParquet_layer1\\parquet"` — cioè l'output grezzo di Layer 1, non quello filtrato di Layer 2. Ecco perché quel codice può referenziare `tipo_diritto='TVOD'` (per i campi legacy `DTVOD_*`) — lavora sul dataset completo non filtrato, e applica la propria logica di filtro Free/Analogico+DVB-T in autonomia per costruire `CUSTOM_DIRITTI`, indipendentemente dal filtro già applicato a L2. - **Conclusione per `powerbricks-expert`**: il nostro `diritti.parquet` è davvero solo Free/Analogico+DVB-T(non coperto) — `MyICR_Suite/CLAUDE.md` aveva ragione. Nessuna azione necessaria sulla KB powerbricks, era solo la mia comprensione della catena a essere incompleta. **Architettura contrastante MyICR_Suite vs pipeline SCHEDULATORE**: `MyICR_Suite/CLAUDE.md` dichiara una regola architetturale opposta a quella della pipeline: *"MyICR_Suite non deve usare MS Access... NO MS Access"* — mentre `ParquetToAccessDaily`/`Weekly` esistono apposta per scrivere in Access (legacy temporaneo per consumer non ancora migrati). Le due parti dell'ecosistema hanno direzioni tecniche opposte e consapevoli: MyICR_Suite va verso DuckDB/Parquet/SQLite puro, la pipeline SCHEDULATORE mantiene un ponte Access finché serve. **Passo 14 — TROVATO IL DIZIONARIO DATI COMPLETO (Adrian, 2026-07-17)**: `[CONFERMATO]` — esiste già un documento che copre gran parte di quello che abbiamo ricostruito a mano: **`PYTHON/MyICR_Suite/app/modules/powerbricks/NL_semantic_layer/NL_QUERY_SCHEMA.md`** (v1.1, 2026-06-08) — dizionario dati completo per tutti i parquet L2 + `linker.db` + `ott.db` + `mediatrack.db`, con 16 gotcha già documentati. **Da trattare come fonte primaria di riferimento per entrambe le KB (flussi + powerbricks) andando avanti** — leggerlo direttamente quando serve un dettaglio di schema, non duplicarlo qui riga per riga. **Risoluzioni ottenute da questo documento**: - **`ott.db` — provenienza RISOLTA**: mirror del file Excel settimanale Qlik/Marketing, **rimpiazzato completamente ad ogni import** (non incrementale), distribuito ai client via pull al boot (come i parquet). Fonte server: `I:\SOFTWARE\PYTHON_SRV\PYTHON_LOCAL\MyICR_Suite\local_db\ott.db`. Quindi non viene dalla pipeline SCHEDULATORE né da OnAir — è un canale a parte, alimentato da un file Excel Marketing/Qlik. - **`OTT_EXT`/`LINKER_REPOSITORY`/`LINKER_VALUTAZIONE`/`COMPETITIVE_IMDB_MAP`** vivono tutte in `sync_db/linker.db` (server: `I:\SOFTWARE\PYTHON_SRV\sync_db\linker.db`) — schema completo con PK, join, e note già in `NL_QUERY_SCHEMA.md`. - **`fr_rr` — versione più precisa**: nel parquet grezzo i valori sono `F`=First Run, `R`=Repeat, `S`=Sospeso, `M`=Mixed (i valori M/S che avevamo lasciato "da spiegare" in powerbricks-expert sono quindi stati "Mixed"/"Sospeso" — non anomalie). `CalFrRr` (calcolato in `custom_diritti.py`, v. Passo 7) è una **ricalcolazione derivata** F/R per la vista legacy, non il valore originale. - **⚠️ Discrepanza trovata — probabile ERRORE nel documento, non un dubbio alla pari**: il documento dice esplicitamente *"⚠ RIFER vs prodotto: In tutta l'applicazione il termine RIFER indica `prodotti.program_id` (VARCHAR, es. 'ANA00101', '338467'). Il campo `prodotto` (INTEGER) è l'ID interno numerico. Sono due chiavi diverse."* — questo **contraddice direttamente** la conferma esplicita di Mauro all'inizio di questa sessione (2026-07-17): *"program_id è un campo che non uso... non rilevante per il business"* e *"rifer = il campo prodotto"* (il codice, INTEGER). Contraddice anche il codice di `custom_anagrafica.py` (Passo 9), che fa letteralmente `prodotto AS RIFER`. **Conclusione — CHIUSO, confermato esplicitamente da Mauro (2026-07-17)**: *"nel parquet prodotti il rifer si chiama prodotto. Program_id non so cosa sia"* — la frase in `NL_QUERY_SCHEMA.md` era **sbagliata/superata** (documento datato 2026-06-08, mai validato a voce con Mauro). `[CONFERMATO]` in via definitiva: `rifer` = `prodotti.prodotto`, `program_id` è un campo di cui Mauro stesso non conosce lo scopo — da trattare come non rilevante/non affidabile per il business, non come chiave alternativa di rifer. **Nota generale**: questo è un buon esempio del perché documenti trovati nel codice vanno sempre incrociati con la conferma diretta di Mauro prima di darli per validi — possono essere scritti da altri agenti (es. Elon, autore della spec PowerBricks originale) senza revisione business successiva. - **Tabelle di decodifica aggiuntive trovate** (non ancora in `powerbricks-expert`): `fascia` in emesso (MA/ME/PO/PS/PR/SS/NO/AR/GR con orari indicativi), ruoli cast estesi (FSS=sceneggiatore, FMN=montatore, FFO=fotografo, C025=musicista, FSU=autore soggetto, C006=conduttore, VOC=voce, SS=se stesso, FDP=doppiatore — oltre a FRE/C001 già noti). - **Il progetto PowerBricks stesso** (per cui questo documento esiste) ha già convenzioni di output stabilite indipendentemente dalle nostre: prima colonna sempre `program_id AS rifer`, scadenze aggregate con `MAX(scad)`, "ambiguità → chiedere sempre prima di eseguire" — coerenti nello spirito con le convenzioni già fissate in `powerbricks-expert` (sintesi prima del dettaglio, segnalare semplificazioni). **Passo 15 — RISOLTO definitivamente: `DB_DAY.accdb` → `MonitorOsservatorioPy`, con meccanismo esatto** (Adrian, 2026-07-17, letti `config/settings.json` + `core/enrichment.py` per intero): `[CONFERMATO]` (codice diretto, non più ipotesi) **Path esatti** (da `ParquetToAccessDaily/config/settings.json`): - `DB_DAY.accdb` locale: `C:\PYTHON_LOCAL\ParquetToAccessDaily\DB_DAY.accdb`, pubblicato su `\\mediaset.it\share\Indirizzo_Controllo_Risorse\SOFTWARE\ProcedureICR\ICRmonitor\MonitorLinker\DB_DAY.accdb` - `db.accdb` (MonitorOsservatorioPy): **`\\mediaset.it\cologno\osservatorio_progetti\MonitorOsservatorio\db.accdb`** — path diverso, share diversa (`cologno` vs `share\Indirizzo_Controllo_Risorse`) **Meccanismo esatto** (funzione `enrich_osservatorio()` in `core/enrichment.py`): 1. Conta le righe in `SCHEDA_PRODOTTO` dentro `DB_DAY.accdb` locale 2. **Solo se count > 30.000** (soglia di sicurezza contro un mirroring di dato incompleto/corrotto), copia la tabella `SCHEDA_PRODOTTO` da `DB_DAY.accdb` verso `db.accdb` (il file di MonitorOsservatorioPy) via `Access.Application.TransferDatabase` (COM) 3. **In parallelo**, copia anche `osservatorio.txt` grezzo (lo stesso file letto da `CsvToParquet_layer1`) direttamente dentro la cartella progetto di MonitorOsservatorio — un secondo canale di distribuzione, indipendente dal parquet/Access **Conferma quindi la catena completa**: `osservatorio.txt` (Direzione Sistemi, in `SCHEDULATORE\INPUT`) → **due strade in parallelo**: (a) via `CsvToParquet_layer1`/`ParquetToAccessDaily`/`enrich_osservatorio` → `db.accdb` di MonitorOsservatorioPy (tabella `SCHEDA_PRODOTTO`, solo se count>30k); (b) copia diretta del file grezzo nella cartella progetto MonitorOsservatorio. **Scoperta collaterale — terzo file Osservatorio**: esiste ANCHE un `db_Osservatorio.accdb` (path: `...\SOFTWARE\ProcedureICR\ICRmonitor\MonitorLinker\db_Osservatorio.accdb`, diverso sia da `DB_DAY.accdb` che da `db.accdb`), popolato dalla funzione `integrazione_update_day()` con una tabella `tabOsservatorio` (schema quasi identico a `osservatorio.parquet`) — consumer non ancora identificato, probabilmente un modulo Access legacy separato da MonitorOsservatorioPy. `[IPOTESI aperta]`. **Passo 17 — quantificato con precisione il filtro Free+DVB-T (Adrian, 2026-07-17, query dirette via Frank-relay `duckdb_query` su L1)**: `[CONFERMATO]` (dato reale, non solo codice) - **Distribuzione `tipo_diritto` su tutto `diritti.parquet` L1** (273.213 righe totali): Free 96.516 (35,3%), Pay-SVOD 58.949 (21,6%), TVOD 42.527 (15,6%), Pay-Premium 31.273 (11,4%), Pay-Basic 30.272 (11,1%), FOD-AVOD 13.676 (5,0%). - **Distribuzione `piattaforma` dentro `tipo_diritto='Free'`** (96.516 righe): Analogico 51.209 (53,0%), DVB-T 27.232 (28,2%), DVB-H 18.075 (18,7%). - **Filtro Free+DVB-T verificato con query diretta**: delle 27.232 righe Free+DVB-T, solo **169 (0,6%)** non sono coperte da un Analogico corrispondente (stesso `prod`, intervallo `decr-scad` incluso) — le altre 27.063 (99,4%) sono scartate come ridondanti. `51.209 (Analogico) + 169 (DVB-T non coperto) = 51.378` — combacia esattamente con la cardinalità nota di `diritti.parquet` L2. - **Free+DVB-H (18.075 righe) escluso interamente**, nessuna eccezione — a differenza del DVB-T non ha un percorso di "salvataggio" condizionale. - **Quadro completo**: solo il **18,8%** dei 273.213 diritti totali gestiti da ICR arriva in L2/MyICR_Suite/PowerBricks; l'**81,2%** (quasi tutto DVB-H e DVB-T, tutto il pay/streaming: SVOD/TVOD/Premium/Basic/AVOD) resta invisibile a chi usa quei sistemi. - **Composizione delle 169 righe Free+DVB-T "salvate"** (query su `ragsoc_distr`, confermando ipotesi di Mauro): **Paramount Pictures International Limited = 75/169 (44,4%)**, di gran lunga il distributore più rappresentato. Resto frammentato: Universal Studios (28, 16,6%), vari festival cinema, Disney Italia (10), BBC Worldwide (2), Rai Cinema (2), altri distributori minori a 1-2 righe ciascuno. **Passo 18 — la storia vera dietro il workaround DVB-T (Mauro, 2026-07-17)**: `[CONFERMATO]` — **il pezzo di business logic più importante scoperto finora**, spiega tutto quello quantificato al Passo 17. - **Il bacino "core" per ICR è sempre stato Free+Analogico al 100%** — sono i diritti usati dalle reti Free principali di Mediaset (C5/I1/R4 e affini). Tutte le elaborazioni ICR sono storicamente pensate per questo bacino. - **Origine del DVB-T (~1-2 anni fa)**: l'**ufficio diritti/contratti** — lo stesso che sovraintende anagrafica ed emesso OnAir, responsabile **Sara Ragazzi** — ha introdotto la categoria "Free DVB-T" per gestire contratti di prodotti trasmissibili su **canali secondari** (non C5/I1/R4). **ICR non è stato informato/coinvolto in questa decisione.** - **Conseguenza per ICR**: i prodotti con diritto solo-DVB-T sono diventati "invisibili"/introvabili nei sistemi ICR (perché il bacino core era solo Analogico). ICR ha dovuto risalire da sola alla causa (la "sporcacciata" fatta dall'ufficio diritti) e costruire una **pezza** per rendere questi prodotti assimilabili al resto — è esattamente la logica `NOT EXISTS`/"workaround DVB-T" trovata in `_transform_diritti()` (Passo 16/17): tenere il DVB-T solo se non già coperto da un Analogico, per evitare duplicati, e farlo confluire nello stesso bacino. - **Il problema che resta aperto, esplicitamente segnalato da Mauro come da sistemare**: la pezza attuale è considerata "orrenda" perché **non espone da nessuna parte** (né in L2/MyICR_Suite, né in PowerBricks) il fatto che un prodotto sia arrivato tramite questo canale DVB-T "secondario" — anzi, il codice **rimuove esplicitamente** le colonne `tipo_diritto`/`piattaforma` dopo il filtro (Passo 16), cancellando ogni traccia distintiva. **Dovrebbe invece essere sempre segnalato ovunque** (specialmente in MyICR_Suite) che un prodotto è "utilizzabile sui secondary channels" — oggi non lo è. Nella vista legacy Access (`DB_WEEK.accdb`/`CUSTOM_ANAGR_ORIZ`) esiste un flag parziale (`DF_ICR_FLAG='T'`, v. Passo 9) ma **solo se il prodotto non ha ALCUN diritto Analogico** — non copre il caso (probabilmente più comune) di un prodotto con diritti misti Analogico+DVB-T su edizioni/periodi diversi, e comunque MyICR_Suite (che ha bandito Access per policy propria) non eredita nemmeno questo flag parziale. - **Candidato concreto per lavoro futuro** (esattamente il tipo di "pezzo da fixare" anticipato da Mauro a inizio di questa indagine): aggiungere un flag esplicito "canale secondario/DVB-T" visibile in MyICR_Suite/PowerBricks per i prodotti che passano da questo workaround — oggi assente. *(catena principale ricostruita, quantificata e ora spiegata nel suo perché business — resta aperta solo l'identificazione del consumer di `db_Osservatorio.accdb`, punto minore rispetto alla scoperta del Passo 18)* ## Anomalie diagnosticate *(vuoto — popolare quando emerge un caso concreto da diagnosticare)* ## Limiti di visibilità da Nave — SUPERATO per la sola lettura (2026-07-17) `[CONFERMATO]` (2026-07-17, aggiornato) — `DataHub_v2`, `ParquetToAccess`, `PIPELINE_PYTHON` vivono su `I:\SOFTWARE\SCHEDULATORE\...`, share di rete Windows-ufficio non raggiungibile **direttamente** da Nave/Linux (nessuna via di rete). **Ma da oggi è esplorabile in sola lettura tramite il Frank-relay** (v. sezione sotto) — scrivendo una richiesta in `tasks/to-frank/`, Frank (che gira sulla rete Mediaset) legge il path per conto nostro. Resta vero che non c'è accesso diretto/di rete da Nave, ma il canale file-based lo rende comunque raggiungibile in pratica per diagnosi read-only. ## Protocollo Frank-relay — IMPLEMENTATO (2026-07-17) `[CONFERMATO]` (implementato in sessione ufficio il 2026-07-17) — Frank legge richieste di sola lettura scritte da Nave via file Dropbox, le esegue localmente (con accesso naturale a `I:\` essendo fisicamente sulla rete Mediaset), scrive la risposta nello stesso modo. Nessuna connessione di rete tra Nave e ufficio — solo file sincronizzati, stesso meccanismo già usato per `memory/`. **Struttura file**: `adrian/tasks/to-frank/richiesta-.json` (scritta da Nave) → `adrian/tasks/from-frank/risposta-.json` (scritta da Frank). **Schema richiesta**: ```json { "id": "req-0001", "timestamp": "2026-07-17T16:00:00+02:00", "action": "list_dir", "params": { "path": "I:\\SOFTWARE\\SCHEDULATORE\\UPDATE_PYTHON\\DataHub_v2" } } ``` **Schema risposta**: ```json { "id": "req-0001", "timestamp": "...", "status": "ok", "result": [ {"name": "main.py", "is_dir": false, "size": 4821, "mtime": "..."} ] } ``` (`"status": "denied"` + `"error": "..."` se l'action non è in allowlist) **Allowlist azioni (chiusa, sola lettura)**: `list_dir`, `read_file` (max 200KB), `glob` (ricerca pattern ricorsiva), `duckdb_query` (SQL read-only via DuckDB — params: `sql`, `params` lista bind opzionale, `limit` default 1000; solo SELECT/WITH come prima keyword; nessun `;` interno; blocca keyword di scrittura/comando: INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/TRUNCATE/REPLACE/MERGE/COPY/EXPORT/CALL/PRAGMA/ATTACH/DETACH). Nessun comando libero/shell, mai. **Nessuna restrizione sui path (Mauro, 2026-07-17)**: `[CONFERMATO]` — a differenza della prima bozza, **non c'è allowlist di path root**. Tutto ciò che è visibile/leggibile dal PC ufficio (incluso `I:\`, non solo `PYTHON/`) è accessibile tramite questo canale — la restrizione è solo sul tipo di azione (sola lettura), non su dove si può guardare. **Stato**: `[CONFERMATO]` implementato **e testato con successo** il 2026-07-17. File: `PYTHON/Frank/daemon/relay_handler.py` (handler) + hook in `scheduler_daemon.py/_health_check()` (polling ogni 30s). Directory `tasks/to-frank/` e `tasks/from-frank/` create nel repo `adrian/` e versionate. **Test eseguiti (2026-07-17)**, tre richieste scritte da Nave, processate senza riavvio di Frank (il polling ha caricato il nuovo codice a runtime, contrariamente all'aspettativa iniziale): - `list_dir "C:\\"` → `ok`, meccanismo base funzionante - `list_dir "I:\\SOFTWARE\\SCHEDULATORE"` → `ok`, risultato: `FTP`, `INPUT`, `PIPELINE_PYTHON`, `UPDATE_PYTHON`, `UPDATE_PYTHON_WEEKLY`, `vecchia procedura VBA` — **conferma che il gotcha "path di rete non affidabili da processo background" (documentato in `PYTHON/CLAUDE.md`) NON si applica al Frank-relay**: `I:\` è pienamente raggiungibile. Questo sblocca il caso d'uso principale (esplorare `DataHub_v2`/`ParquetToAccess`/`PIPELINE_PYTHON` da Nave). - Azione `delete_file` (non allowlisted) → `denied` correttamente, con messaggio d'errore che elenca l'allowlist reale. **Limite di visibilità da Nave aggiornato**: con questo canale operativo, `I:\SOFTWARE\SCHEDULATORE\...` **è ora esplorabile in sola lettura anche da Nave** tramite richieste al Frank-relay — non più un limite tecnico assoluto come segnato in precedenza in questo documento (sezione "Limiti di visibilità da Nave" sopra, da considerare superata per la parte di sola lettura). ## Altri drive di rete mappati — organigramma aziendale `[CONFERMATO]` (Mauro, 2026-07-17) — oltre a `I:\` (SCHEDULATORE, territorio ICR), risultano mappati anche `W:\` e `X:\`, ma appartengono a **Direzioni aziendali diverse da ICR**: - **`X:\` = struttura Osservatorio (per esteso: Osservatorio Internazionale)** — Direzione separata. - **`W:\` = struttura Acquisti** — Direzione separata. - **Verso del flusso confermato (Mauro, 2026-07-17)**: `[CONFERMATO]` — **è ICR che fornisce informazioni/applicativi a Osservatorio e Acquisti, non il contrario**. La mia prima ipotesi ("questi drive sono probabile fonte di osservatorio.parquet/gemma.parquet") aveva il verso sbagliato. - **Provenienza reale di `osservatorio.parquet` chiarita (Mauro, 2026-07-17)**: `[CONFERMATO]` — arriva sì (indirettamente) da Osservatorio, ma **tramite Direzione Sistemi**, non dal drive mappato `X:\`. `X:\` è quindi scartato come canale di provenienza per questo dataset — il collegamento nome/dataset era fuorviante. Il percorso reale (Osservatorio → Direzione Sistemi → ICR/parquet) non è ancora stato indagato nel dettaglio: non sappiamo se Direzione Sistemi esponga un export/feed che Frank/una pipeline preleva, o se avvenga altrimenti. - **`gemma.parquet`/Acquisti**: nessun chiarimento ancora ricevuto — non dare per scontato lo stesso pattern (via Direzione Sistemi) senza conferma esplicita. - Testati entrambi via Frank-relay (2026-07-17): primo tentativo `WinError 3` (drive non ancora mappato in quella sessione), secondo tentativo `ok` — contenuto osservato: `X:\` = trailer PDF, template PPT; `W:\` = presentazioni/documenti (es. "Amazon MGM Slate Presentation", "backup slide library cinema") — coerente con condivisioni documentali di Direzioni diverse da ICR, non dati strutturati come i parquet SCHEDULATORE. - **Nota per lo scope**: essendo Direzioni diverse da ICR e riceventi (non fonte) rispetto a ICR, questi drive sono di interesse marginale per la diagnostica flussi quotidiana — il focus resta `I:\SOFTWARE\SCHEDULATORE\` e `PYTHON/`. ## MonitorOsservatorioPy — applicativo ICR per Osservatorio `[CONFERMATO]` (2026-07-17, letto `PYTHON/MonitorOsservatorioPy/CLAUDE.md`, sola lettura) — esempio concreto del flusso ICR→Osservatorio appena confermato: Mauro sta sviluppando questo applicativo (evoluzione Python di un tool VBA/Excel) **per** la Direzione Osservatorio. - **Scopo**: importa file `.xlsx` con dati ascolti TV (share/rating) per Spagna e Germania, **ricevuti dal Marketing** (non da Osservatorio direttamente, non da Direzione Sistemi) — mostra in griglia editabile, permette aggancio a `SCHEDA_PRODOTTO` (mirror Access via `monitor.db`), esporta xlsx sidecar. - **Deploy**: `\\Mediaset.it\cologno\Osservatorio_Progetti\MonitorOsservatorio\MonitorOsservatorioPy\` (path UNC, da CLAUDE.md) = **`X:\MonitorOsservatorio\MonitorOsservatorioPy`** (Mauro, 2026-07-17) — stesso posto, il drive `X:\` (struttura Osservatorio, v. sopra) è il mapping locale di quella share. Quindi `X:\` **non è solo di interesse marginale come segnato sopra** — è anche la destinazione di deploy di un applicativo che ICR sviluppa attivamente, esplorabile via Frank-relay se serve ispezionare la versione in produzione. Tier 1 (Python embedded, zero setup), non passa da Frank per ora (`frank_sync/sync_db.py` è solo un placeholder). - **Relazione con `osservatorio.parquet` chiarita (Mauro, 2026-07-17)**: `[CONFERMATO]` — **`osservatorio.parquet` è sostanzialmente il database di Osservatorio** (mirror/copia del loro DB dentro MyICR_Suite), mentre **MonitorOsservatorioPy è lo strumento che carica i dati grezzi di "emesso internazionale" nella loro intranet** — cioè alimenta a monte il database di cui `osservatorio.parquet` è poi la copia lato ICR. Non sono lo stesso sistema, ma sono nella stessa catena: MonitorOsservatorioPy (carica dati grezzi → intranet Osservatorio) → database Osservatorio → `osservatorio.parquet` (mirror lato ICR). Il passaggio esatto "database Osservatorio → osservatorio.parquet" (quale pipeline/export lo fa, con che frequenza) non è ancora stato indagato.