""" enrichment.py — logiche di arricchimento DB_DAY.accdb post-populate. Eseguito dopo che ParquetToAccess ha popolato GEMMA e SCHEDA_PRODOTTO. Replica le sub di update_day.py (senza i populate iniziali, già fatti). Sub eseguite in ordine: 1. enrich_gemma — V_* flags, M_1TV_*, copia in GemmaLoader 2. enrich_valutazioni — popola VALUTAZIONI da GEMMA + linked tables 3. enrich_osservatorio — copia SCHEDA_PRODOTTO in MonitorOsservatorio + osservatorio.txt 4. build_file_onairplus — costruisce tabella FILE + esporta FILE.xls 5. manutenzione_info_global — pulisce INFO_GLOBAL 6. manutenzione_cal_ric_stato — aggiorna RICHIESTE_DETT e GEMMA.CalRicStato 7. integrazione_update_day — aggiorna tabOsservatorio in db_Osservatorio.accdb (ex IntegrazioneUpdateDay) """ import gc import glob import os import shutil import time import datetime import pyodbc import xlwt import win32com.client from pathlib import Path # --------------------------------------------------------------------------- # Utility # --------------------------------------------------------------------------- def _ts(): return datetime.datetime.now().strftime('%H:%M:%S') def _conn(accdb_path: str) -> pyodbc.Connection: conn_str = ( "DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};" f"DBQ={accdb_path};" ) return pyodbc.connect(conn_str, autocommit=True) def _exec(conn, sql, retry_on="-3034", retries=3): """Esegue query con retry automatico su errori transient Access ODBC.""" for attempt in range(retries + 1): cur = conn.cursor() try: cur.execute(sql) return except pyodbc.Error as e: cur.close() if retry_on in str(e) and attempt < retries: time.sleep(3) continue raise type(e)(str(e) + f"\nSQL: {sql.strip()[:300]}") from e finally: try: cur.close() except Exception: pass def _drop(conn, table): try: conn.cursor().execute(f"DROP TABLE [{table}]") except Exception: pass def _s_updater(nome, evento, updates_log): """Scrive timestamp file su rete (equivalente sUpdater VBA).""" now = datetime.datetime.now() print(f"[{_ts()}] {nome} — {evento}") if evento == "End" and updates_log and os.path.isdir(updates_log): pattern = os.path.join(updates_log, f"UPDATE_{nome}*.txt") for old in glob.glob(pattern): try: os.remove(old) except Exception: pass date_str = now.strftime("%d-%m-%y") try: open(os.path.join(updates_log, f"UPDATE_{nome} al {date_str}.txt"), "w").close() except Exception: pass # --------------------------------------------------------------------------- # COM helpers # --------------------------------------------------------------------------- def _transfer_table(src_accdb, dst_accdb, src_table, dst_table, dst_password=None): """ Copia una tabella da src_accdb a dst_accdb via Access.Application.TransferDatabase. Apre dst_accdb come CurrentDatabase per evitare errori su query linked in src. """ app = win32com.client.Dispatch("Access.Application") app.Visible = False try: if dst_password: app.OpenCurrentDatabase(dst_accdb, False, dst_password) else: app.OpenCurrentDatabase(dst_accdb) try: app.DoCmd.DeleteObject(0, dst_table) # acTable = 0 except Exception: pass app.DoCmd.TransferDatabase( 0, # acImport "Microsoft Access", src_accdb, 0, # acTable src_table, dst_table, False ) app.CloseCurrentDatabase() finally: try: app.Quit() except Exception: pass del app gc.collect() time.sleep(5) # --------------------------------------------------------------------------- # 1. enrich_gemma # --------------------------------------------------------------------------- def enrich_gemma(db_path: str, cfg: dict): """ - Aggiunge '!' ai campi V_* non valorizzati per titoli IN CORSO - Aggiorna M_1TV_* (prima TV free) da DB_WEEK_CUSTOM_ANAGR_ORIZ - Copia GEMMA in GemmaLoader.accdb """ updates_log = cfg.get('updates_log', '') _s_updater("GEMMA", "Start", updates_log) conn = _conn(db_path) # V_* flags per titoli IN CORSO cur = conn.cursor() cur.execute("SELECT * FROM GEMMA WHERE 1=0") all_columns = [desc[0] for desc in cur.description] cur.close() for field in all_columns: if field.upper().startswith("V_") and field.lower() not in ("v_stato", "v_rda"): a_field = "A_" + field[2:] try: _exec(conn, f""" UPDATE GEMMA SET GEMMA.[{field}] = '!' WHERE ( (GEMMA.V_Stato = 'IN CORSO') AND (GEMMA.[{a_field}] <> '0') AND (GEMMA.[{field}] IS NULL) ) """) except Exception: pass # M_1TV_* — prima visione in prima serata (EM_PG) _drop(conn, "temp") _exec(conn, """ SELECT GEMMA.[A_Cod IMDB], Min(DB_WEEK_CUSTOM_ANAGR_ORIZ.EM_PG_DATA) AS MinDiEM_PG_DATA INTO temp FROM GEMMA INNER JOIN DB_WEEK_CUSTOM_ANAGR_ORIZ ON GEMMA.[A_Cod IMDB] = DB_WEEK_CUSTOM_ANAGR_ORIZ.IMDB_CODICE WHERE DB_WEEK_CUSTOM_ANAGR_ORIZ.EM_PG_DATA IS NOT NULL GROUP BY GEMMA.[A_Cod IMDB] """) _exec(conn, """ UPDATE (GEMMA INNER JOIN temp ON GEMMA.[A_Cod IMDB] = temp.[A_Cod IMDB]) INNER JOIN DB_WEEK_CUSTOM_ANAGR_ORIZ ON (temp.MinDiEM_PG_DATA = DB_WEEK_CUSTOM_ANAGR_ORIZ.EM_PG_DATA) AND (temp.[A_Cod IMDB] = DB_WEEK_CUSTOM_ANAGR_ORIZ.IMDB_CODICE) SET GEMMA.M_1TV_RETE = [EM_PG_RETE], GEMMA.M_1TV_DATA = [EM_PG_DATA], GEMMA.M_1TV_HI = [EM_PG_HI], GEMMA.M_1TV_FASCIA = [EM_PG_FASCIA], GEMMA.M_1TV_AUD = [EM_PG_AUD], GEMMA.M_1TV_SHA = [EM_PG_SHARE] """) # M_1TV_* — prima visione in prima TV (EM_PT) — solo se M_1TV_DATA manca _drop(conn, "temp") _exec(conn, """ SELECT GEMMA.[A_Cod IMDB], Min(DB_WEEK_CUSTOM_ANAGR_ORIZ.EM_PT_DATA) AS MinDiEM_PT_DATA INTO temp FROM GEMMA INNER JOIN DB_WEEK_CUSTOM_ANAGR_ORIZ ON GEMMA.[A_Cod IMDB] = DB_WEEK_CUSTOM_ANAGR_ORIZ.IMDB_CODICE WHERE DB_WEEK_CUSTOM_ANAGR_ORIZ.EM_PT_DATA IS NOT NULL GROUP BY GEMMA.[A_Cod IMDB] """) _exec(conn, """ UPDATE (GEMMA INNER JOIN temp AS temp_1 ON GEMMA.[A_Cod IMDB] = temp_1.[A_Cod IMDB]) INNER JOIN DB_WEEK_CUSTOM_ANAGR_ORIZ ON (temp_1.MinDiEM_PT_DATA = DB_WEEK_CUSTOM_ANAGR_ORIZ.EM_PT_DATA) AND (temp_1.[A_Cod IMDB] = DB_WEEK_CUSTOM_ANAGR_ORIZ.IMDB_CODICE) SET GEMMA.M_1TV_RETE = [EM_PT_RETE], GEMMA.M_1TV_DATA = [EM_PT_DATA], GEMMA.M_1TV_HI = [EM_PT_HI], GEMMA.M_1TV_FASCIA = [EM_PT_FASCIA], GEMMA.M_1TV_AUD = [EM_PT_AUD], GEMMA.M_1TV_SHA = [EM_PT_SHARE] WHERE (GEMMA.M_1TV_DATA IS NULL) OR ([EM_PG_DATA] < [M_1TV_DATA]) """) _drop(conn, "temp") conn.close() # Copia GEMMA in GemmaLoader gemma_loader = cfg.get('gemma_loader') if gemma_loader and os.path.isfile(gemma_loader): _transfer_table( db_path, gemma_loader, "GEMMA", "GEMMA", dst_password=cfg.get('gemma_loader_password') ) else: print(f"[{_ts()}] GEMMA -> GemmaLoader: saltato (file non trovato)") _s_updater("GEMMA", "End", updates_log) # --------------------------------------------------------------------------- # 2. enrich_valutazioni # --------------------------------------------------------------------------- def enrich_valutazioni(db_path: str, cfg: dict): updates_log = cfg.get('updates_log', '') _s_updater("VALUTAZIONI", "Start", updates_log) conn = _conn(db_path) # 1. INSERT valutazioni da GEMMA (origine 'G') _exec(conn, """ INSERT INTO VALUTAZIONI (IMDB, DATA, ORIGINE) SELECT GEMMA.[A_Cod IMDB], Max(GEMMA.A_Data) AS MaxDiA_Data, 'G' AS Espr1 FROM GEMMA GROUP BY GEMMA.[A_Cod IMDB], 'G' HAVING GEMMA.[A_Cod IMDB] Like 'tt%' """) # 2. DELETE righe superate da LINKER_VALUTAZIONE _drop(conn, "temp_maxlinker") _exec(conn, """ SELECT COD_IMDB, Max(DATA) AS MaxData INTO temp_maxlinker FROM LINKER_VALUTAZIONE GROUP BY COD_IMDB """) _exec(conn, """ DELETE VALUTAZIONI.*, VALUTAZIONI.DATA FROM VALUTAZIONI INNER JOIN temp_maxlinker ON VALUTAZIONI.IMDB = temp_maxlinker.COD_IMDB WHERE VALUTAZIONI.DATA IS NOT NULL AND temp_maxlinker.MaxData IS NOT NULL AND CDate(VALUTAZIONI.DATA) < CDate(temp_maxlinker.MaxData) """) _drop(conn, "temp_maxlinker") # 3. INSERT valutazioni da sinottico (origine 'S') _drop(conn, "temp_linker") _drop(conn, "temp_existing_imdb") _exec(conn, """ SELECT FORNITORE, COD_IMDB, DATA, C5, I1, R4, LA5, I2, IRIS, TOPCRIME, FOCUS, C20, CINE34, TWENTYSEVEN INTO temp_linker FROM LINKER_VALUTAZIONE """) _exec(conn, "SELECT IMDB INTO temp_existing_imdb FROM VALUTAZIONI") _exec(conn, """ DELETE temp_linker.* FROM temp_linker INNER JOIN temp_existing_imdb ON temp_linker.COD_IMDB = temp_existing_imdb.IMDB """) _drop(conn, "temp_existing_imdb") _exec(conn, """ INSERT INTO VALUTAZIONI ( FORNITORE, IMDB, DATA, C5, I1, R4, LA5, I2, IRIS, TOPCRIME, FOCUS, C20, CINE34, TWENTYSEVEN, ORIGINE ) SELECT FORNITORE, COD_IMDB, DATA, C5, I1, R4, LA5, I2, IRIS, TOPCRIME, FOCUS, C20, CINE34, TWENTYSEVEN, 'S' AS Espr1 FROM temp_linker """) _drop(conn, "temp_linker") # 4. UPDATE: arricchisce righe 'G' con dati GEMMA _drop(conn, "temp_gemma_vals") _exec(conn, """ SELECT [A_Cod IMDB], A_Data, KEY_COD, A_DISTRIBUTORE, V_RDA, V_C5, V_I1, V_R4, V_LA5, V_I2, V_IRIS, V_TOP, V_FOC, V_C20, V_CI34, V_C27, A_SUPPORTO INTO temp_gemma_vals FROM GEMMA """) _exec(conn, """ UPDATE VALUTAZIONI INNER JOIN temp_gemma_vals ON VALUTAZIONI.IMDB = temp_gemma_vals.[A_Cod IMDB] SET VALUTAZIONI.GEMMA = temp_gemma_vals.KEY_COD, VALUTAZIONI.FORNITORE = temp_gemma_vals.A_DISTRIBUTORE, VALUTAZIONI.RDA = temp_gemma_vals.V_RDA, VALUTAZIONI.C5 = temp_gemma_vals.V_C5, VALUTAZIONI.I1 = temp_gemma_vals.V_I1, VALUTAZIONI.R4 = temp_gemma_vals.V_R4, VALUTAZIONI.LA5 = temp_gemma_vals.V_LA5, VALUTAZIONI.I2 = temp_gemma_vals.V_I2, VALUTAZIONI.IRIS = temp_gemma_vals.V_IRIS, VALUTAZIONI.TOPCRIME = temp_gemma_vals.V_TOP, VALUTAZIONI.FOCUS = temp_gemma_vals.V_FOC, VALUTAZIONI.C20 = temp_gemma_vals.V_C20, VALUTAZIONI.CINE34 = temp_gemma_vals.V_CI34, VALUTAZIONI.TWENTYSEVEN = temp_gemma_vals.V_C27, VALUTAZIONI.SUPPORTO = temp_gemma_vals.A_SUPPORTO WHERE VALUTAZIONI.ORIGINE = 'G' AND VALUTAZIONI.DATA IS NOT NULL AND CDate(VALUTAZIONI.DATA) = temp_gemma_vals.A_Data """) _drop(conn, "temp_gemma_vals") # 5. INSERT valutazioni extra Gemma (origine 'I') _exec(conn, """ INSERT INTO VALUTAZIONI ( IMDB, ORIGINE, DATA, C5, I1, R4, LA5, I2, IRIS, TOPCRIME, FOCUS, C20, CINE34, TWENTYSEVEN ) SELECT tabValutazioniExtraGemma.IMDB, 'I' AS Espr1, Format(tabValutazioniExtraGemma.data_creazione,'dd/mm/yyyy'), tabValutazioniExtraGemma.C5, tabValutazioniExtraGemma.I1, tabValutazioniExtraGemma.R4, tabValutazioniExtraGemma.LA5, tabValutazioniExtraGemma.I2, tabValutazioniExtraGemma.IRIS, tabValutazioniExtraGemma.TOPCRIME, tabValutazioniExtraGemma.FOCUS, tabValutazioniExtraGemma.C20, tabValutazioniExtraGemma.CINE34, tabValutazioniExtraGemma.TWENTYSEVEN FROM tabValutazioniExtraGemma LEFT JOIN VALUTAZIONI ON tabValutazioniExtraGemma.IMDB = VALUTAZIONI.IMDB WHERE VALUTAZIONI.IMDB IS NULL """) # 6. UPDATE: sovrascrive con '-' le valutazioni extra con NoIcr=True _exec(conn, """ UPDATE VALUTAZIONI INNER JOIN tabValutazioniExtraGemma ON VALUTAZIONI.IMDB = tabValutazioniExtraGemma.IMDB SET VALUTAZIONI.C5 = '-', VALUTAZIONI.I1 = '-', VALUTAZIONI.R4 = '-', VALUTAZIONI.LA5 = '-', VALUTAZIONI.I2 = '-', VALUTAZIONI.IRIS = '-', VALUTAZIONI.TOPCRIME = '-', VALUTAZIONI.FOCUS = '-', VALUTAZIONI.C20 = '-', VALUTAZIONI.CINE34 = '-', VALUTAZIONI.TWENTYSEVEN = '-', VALUTAZIONI.C37 = '-' WHERE tabValutazioniExtraGemma.NoIcr = True AND VALUTAZIONI.ORIGINE = 'I' """) conn.close() _s_updater("VALUTAZIONI", "End", updates_log) # --------------------------------------------------------------------------- # 3. enrich_osservatorio # --------------------------------------------------------------------------- def enrich_osservatorio(db_path: str, cfg: dict): updates_log = cfg.get('updates_log', '') _s_updater("MOSSERVATORIO_Schede", "Start", updates_log) conn = _conn(db_path) cur = conn.cursor() cur.execute("SELECT COUNT(*) FROM SCHEDA_PRODOTTO") count = cur.fetchone()[0] cur.close() conn.close() if count > 30000: monitor_db = cfg.get('monitor_osservatorio') if monitor_db and os.path.isfile(monitor_db): try: _transfer_table(db_path, monitor_db, "SCHEDA_PRODOTTO", "SCHEDA_PRODOTTO") except Exception as e: print(f"[{_ts()}] WARNING MonitorOsservatorio: {e}") else: print(f"[{_ts()}] MonitorOsservatorio: saltato (file non trovato)") # Copia osservatorio.txt src = cfg.get('osservatorio_src') dst = cfg.get('osservatorio_dst') if src and dst: try: shutil.copy2(src, dst) except Exception as e: print(f"[{_ts()}] WARNING FileCopy osservatorio.txt: {e}") _s_updater("MOSSERVATORIO_Schede", "End", updates_log) # --------------------------------------------------------------------------- # 4. build_file_onairplus # --------------------------------------------------------------------------- def _parse_rda(conn): """Parsing V_RDA -> RDA GEN / RDA TEM (logica Python per evitare ODBC 22018).""" cur = conn.cursor() cur.execute(""" SELECT t.MinDiKEY_COD, g.[V_RDA] FROM temp2 AS t INNER JOIN GEMMA AS g ON t.MinDiKEY_COD = g.KEY_COD WHERE g.[V_RDA] IS NOT NULL """) rows = cur.fetchall() cur.close() gen_tags = {' C5', ' I1', ' R4'} upd = conn.cursor() for key_cod, v_rda in rows: if not v_rda or ';' not in v_rda: continue parts = v_rda.split(';') rda_gen = rda_tem = '' for part in parts[:3]: seg = part + ';' if seg[:3] in gen_tags: rda_gen += seg else: rda_tem += seg if rda_gen or rda_tem: upd.execute( "UPDATE temp2 SET [RDA GEN] = ?, [RDA TEM] = ? WHERE MinDiKEY_COD = ?", rda_gen, rda_tem, key_cod ) upd.close() def build_file_onairplus(db_path: str, cfg: dict): updates_log = cfg.get('updates_log', '') _s_updater("GEMMA_ONIARPLUS", "Start", updates_log) # typo originale preservato conn = _conn(db_path) _drop(conn, "temp") _drop(conn, "temp2") _drop(conn, "FILE") _exec(conn, """ SELECT Min(GEMMA.KEY_COD) AS MinDiKEY_COD, DB_WEEK_CUSTOM_ANAGR_ORIZ.RIFER INTO temp FROM GEMMA INNER JOIN DB_WEEK_CUSTOM_ANAGR_ORIZ ON GEMMA.[A_Cod IMDB] = DB_WEEK_CUSTOM_ANAGR_ORIZ.IMDB_CODICE GROUP BY DB_WEEK_CUSTOM_ANAGR_ORIZ.RIFER """) _exec(conn, """ SELECT temp.MinDiKEY_COD, temp.RIFER, GEMMA.V_C5 AS C5, GEMMA.V_I1 AS I1, GEMMA.V_R4 AS R4, GEMMA.V_LA5 AS LA5, GEMMA.V_I2 AS I2, GEMMA.V_IRIS AS IRIS, GEMMA.V_TOP AS [TOP], GEMMA.V_FOC AS FOC, GEMMA.V_C20 AS C20, GEMMA.V_CI34 AS CI34, GEMMA.V_INF AS INF, '' AS [RDA GEN], '' AS [RDA TEM] INTO temp2 FROM temp INNER JOIN GEMMA ON temp.MinDiKEY_COD = GEMMA.KEY_COD """) _parse_rda(conn) _exec(conn, """ SELECT RIFER, C5, I1, R4, LA5, I2, IRIS, TOP, FOC, C20, CI34, INF, [RDA GEN], [RDA TEM] INTO FILE FROM temp2 """) _exec(conn, """ DELETE [C5],[I1],[R4],[LA5],[I2],[IRIS],[TOP],[FOC],[C20],[CI34], [INF],[RDA GEN],[RDA TEM] FROM FILE WHERE [C5] IS NULL AND [I1] IS NULL AND [R4] IS NULL AND [LA5] IS NULL AND [I2] IS NULL AND [IRIS] IS NULL AND [TOP] IS NULL AND [FOC] IS NULL AND [C20] IS NULL AND [CI34] IS NULL AND [INF] IS NULL AND [RDA GEN] IS NULL AND [RDA TEM] IS NULL """) # Export FILE -> FILE.xls ftp_dir = cfg.get('ftp_dir') if ftp_dir: os.makedirs(ftp_dir, exist_ok=True) file_xls = os.path.join(ftp_dir, "FILE.xls") cur = conn.cursor() cur.execute("SELECT * FROM [FILE]") columns = [desc[0] for desc in cur.description] rows = cur.fetchall() cur.close() wb = xlwt.Workbook(encoding="utf-8") ws = wb.add_sheet("FILE") for ci, col in enumerate(columns): ws.write(0, ci, col) for ri, row in enumerate(rows, 1): for ci, val in enumerate(row): if val is not None: ws.write(ri, ci, val) wb.save(file_xls) print(f"[{_ts()}] FILE.xls -> {file_xls}") _drop(conn, "temp") _drop(conn, "temp2") conn.close() _s_updater("GEMMA_ONIARPLUS", "End", updates_log) # typo originale preservato # --------------------------------------------------------------------------- # 5. manutenzione_info_global # --------------------------------------------------------------------------- def manutenzione_info_global(db_path: str, cfg: dict): updates_log = cfg.get('updates_log', '') _s_updater("Manutenzione_InfoGlobal", "Start", updates_log) conn = _conn(db_path) _exec(conn, """ UPDATE INFO_GLOBAL INNER JOIN GEMMA ON INFO_GLOBAL.GEMMA = GEMMA.KEY_COD SET INFO_GLOBAL.IMDB_FORZATO = Null WHERE GEMMA.[A_Cod IMDB] = INFO_GLOBAL.IMDB_FORZATO """) _exec(conn, """ DELETE INFO_GLOBAL.NOTA_ICR, INFO_GLOBAL.NETWORK_FREE, INFO_GLOBAL.FORNITORE_DIRITTI, INFO_GLOBAL.IMDB_FORZATO, INFO_GLOBAL.VegTentative, INFO_GLOBAL.[dismesso-PropostaGiro] FROM INFO_GLOBAL WHERE INFO_GLOBAL.NOTA_ICR IS NULL AND INFO_GLOBAL.NETWORK_FREE IS NULL AND INFO_GLOBAL.FORNITORE_DIRITTI IS NULL AND INFO_GLOBAL.IMDB_FORZATO IS NULL AND INFO_GLOBAL.VegTentative IS NULL AND INFO_GLOBAL.[dismesso-PropostaGiro] IS NULL """) conn.close() _s_updater("Manutenzione_InfoGlobal", "End", updates_log) # --------------------------------------------------------------------------- # 6. manutenzione_cal_ric_stato # --------------------------------------------------------------------------- def manutenzione_cal_ric_stato(db_path: str, cfg: dict): updates_log = cfg.get('updates_log', '') _s_updater("Manutenzione_CalRicStato", "Start", updates_log) conn = _conn(db_path) # Passo 1: aggiorna KEY_COD in RICHIESTE_DETT dove manca _drop(conn, "temp") _exec(conn, """ SELECT RICHIESTE_DETT.IMDB, GEMMA.KEY_COD INTO temp FROM RICHIESTE_DETT INNER JOIN GEMMA ON RICHIESTE_DETT.IMDB = GEMMA.[A_Cod IMDB] WHERE RICHIESTE_DETT.KEY_COD IS NULL GROUP BY RICHIESTE_DETT.IMDB, GEMMA.KEY_COD HAVING RICHIESTE_DETT.IMDB Like 'tt%' AND Count(GEMMA.KEY_COD) = 1 """) _exec(conn, """ UPDATE temp INNER JOIN RICHIESTE_DETT ON temp.IMDB = RICHIESTE_DETT.IMDB SET RICHIESTE_DETT.KEY_COD = temp.KEY_COD WHERE RICHIESTE_DETT.KEY_COD IS NULL """) _drop(conn, "temp") # Passo 2: valorizza GEMMA.CalRicStato _drop(conn, "temp") _drop(conn, "temp2") _exec(conn, """ SELECT RICHIESTE_DETT.KEY_COD, Max(RICHIESTE_MAIN.ANNO_BUDGET) AS MaxDiANNO_BUDGET INTO temp FROM RICHIESTE_MAIN INNER JOIN RICHIESTE_DETT ON RICHIESTE_MAIN.KEY_COMMESSA = RICHIESTE_DETT.EXT_KEY_COMMESSA WHERE RICHIESTE_MAIN.STATO IS NOT NULL GROUP BY RICHIESTE_DETT.KEY_COD HAVING RICHIESTE_DETT.KEY_COD IS NOT NULL ORDER BY RICHIESTE_DETT.KEY_COD, Max(RICHIESTE_MAIN.ANNO_BUDGET) DESC """) _exec(conn, """ SELECT temp.KEY_COD, First(RICHIESTE_MAIN.STATO) AS PrimoDiSTATO INTO temp2 FROM RICHIESTE_MAIN INNER JOIN (temp INNER JOIN RICHIESTE_DETT ON temp.KEY_COD = RICHIESTE_DETT.KEY_COD) ON (temp.MaxDiANNO_BUDGET = RICHIESTE_MAIN.ANNO_BUDGET) AND (RICHIESTE_MAIN.KEY_COMMESSA = RICHIESTE_DETT.EXT_KEY_COMMESSA) GROUP BY temp.KEY_COD """) _exec(conn, """ UPDATE GEMMA INNER JOIN temp2 ON GEMMA.KEY_COD = temp2.KEY_COD SET GEMMA.CalRicStato = [PrimoDiSTATO] """) _drop(conn, "temp") _drop(conn, "temp2") conn.close() _s_updater("Manutenzione_CalRicStato", "End", updates_log) # --------------------------------------------------------------------------- # 7. integrazione_update_day # --------------------------------------------------------------------------- def integrazione_update_day(cfg: dict): """ Replica IntegrazioneUpdateDay: aggiorna tabOsservatorio in db_Osservatorio.accdb copiando da tabella osservatorio e aggiornando il campo episodi. Esegue CompactRepair finale via COM. """ updates_log = cfg.get('updates_log', '') db_path = cfg.get('db_osservatorio') if not db_path: print(f"[{_ts()}] integrazione_update_day: db_osservatorio non configurato — saltato") return if not os.path.isfile(db_path): print(f"[{_ts()}] integrazione_update_day: file non trovato ({db_path}) — saltato") return _s_updater("IntegrazioneUpdateDay", "Start", updates_log) conn = _conn(db_path) try: print(f"[{_ts()}] tabOsservatorio: cancellazione...") _exec(conn, "DELETE FROM tabOsservatorio") print(f"[{_ts()}] tabOsservatorio: inserimento da osservatorio...") _exec(conn, """ INSERT INTO tabOsservatorio ( codice, imdb, titolo, paese, genere, tipologia, subgenere, anno, rete, platform, stato, titolo_int, produttore, produzione, distribuzione, regia, [cast], sceneggiatura, showrunner, durata, giorno_ora, trama, note_programmazione, note_sceneggiatura, anno_screeening ) SELECT codice, imdb, titolo, paese, genere, tipologia, subgenere, anno, rete, platform, stato, titolo_int, produttore, produzione, distribuzione, regia, [cast], sceneggiatura, showrunner, durata, giorno_ora, trama, note_programmazione, note_sceneggiatura, anno_screeening FROM osservatorio """) _drop(conn, "tempOsservatorio") print(f"[{_ts()}] tempOsservatorio: creazione...") _exec(conn, """ SELECT DISTINCT osservatorio.codice, osservatorio.episodi INTO tempOsservatorio FROM osservatorio WHERE osservatorio.episodi > 0 """) print(f"[{_ts()}] tabOsservatorio: aggiornamento episodi...") _exec(conn, """ UPDATE tabOsservatorio INNER JOIN tempOsservatorio ON tabOsservatorio.codice = tempOsservatorio.codice SET tabOsservatorio.episodi = tempOsservatorio.episodi """) _drop(conn, "tempOsservatorio") finally: _drop(conn, "tempOsservatorio") conn.close() # CompactRepair via COM new_db_path = db_path.replace(".accdb", "_compact.accdb") try: print(f"[{_ts()}] db_Osservatorio: CompactRepair...") app = win32com.client.Dispatch("Access.Application") app.Visible = False try: app.CompactRepair(db_path, new_db_path) finally: app.Quit() gc.collect() os.remove(db_path) os.rename(new_db_path, db_path) print(f"[{_ts()}] db_Osservatorio: CompactRepair completato.") except Exception as e: print(f"[{_ts()}] WARNING CompactRepair db_Osservatorio: {e}") if os.path.isfile(new_db_path): try: os.remove(new_db_path) except Exception: pass _s_updater("IntegrazioneUpdateDay", "End", updates_log) # --------------------------------------------------------------------------- # Entry point # --------------------------------------------------------------------------- def run_all(db_path: str, cfg: dict): """Esegue tutte le sub di enrichment in ordine.""" enrich_gemma(db_path, cfg) enrich_valutazioni(db_path, cfg) enrich_osservatorio(db_path, cfg) build_file_onairplus(db_path, cfg) manutenzione_info_global(db_path, cfg) manutenzione_cal_ric_stato(db_path, cfg) integrazione_update_day(cfg)