""" LinkerRenderer — pipeline di fetch dati parquet condivisa tra LinkerSheet e DiritiTab. Prende una lista di RIFER (e opzionalmente IMDB codes) e restituisce un RenderResult con tutti i lookup necessari per popolare la griglia standardizzata. """ import logging from dataclasses import dataclass, field from app.modules.linker.src.database.parquet_db import ParquetCatalog logger = logging.getLogger(__name__) def _rk(val) -> str: """Normalizza un valore RIFER a stringa pulita.""" if val is None: return '' s = str(val).strip() return s[:-2] if s.endswith('.0') else s @dataclass class RenderResult: """Lookup dicts restituiti da LinkerRenderer.fetch().""" diritti: dict = field(default_factory=dict) # {rifer: [Series, ...]} emesso_g: dict = field(default_factory=dict) # {rifer: Series} generaliste emesso_t: dict = field(default_factory=dict) # {rifer: Series} tematiche boxoffice: dict = field(default_factory=dict) # {rifer: Series} rete_slot: dict = field(default_factory=dict) # {rifer: str} gemma: dict = field(default_factory=dict) # {imdb_code: Series} class LinkerRenderer: """ Fetcha tutti i dati parquet per una lista di RIFER. Agnostico rispetto alla GUI — usato da LinkerSheet e DiritiTab. """ def __init__(self, catalog: ParquetCatalog): self._catalog = catalog def fetch(self, rifers: list, imdb_codes: list = None) -> RenderResult: """ Fetcha diritti, emesso, box office, rete_slot e valutazioni Gemma. Ogni sezione è protetta da try/except — un errore parziale non blocca il resto. """ result = RenderResult() rifers = [r for r in (rifers or []) if r] if not rifers: return result # ── Diritti ────────────────────────────────────────────────── try: df = self._catalog.get_diritti_full_by_rifers(rifers) if not df.empty: for _, r in df.iterrows(): k = _rk(r.get('RIFER', '')) if k: result.diritti.setdefault(k, []).append(r) logger.debug(f"LinkerRenderer diritti: {len(result.diritti)} rifers") except Exception as e: logger.warning(f"LinkerRenderer diritti: {e}") # ── Emesso (generaliste + tematiche, un solo scan) ──────────── try: both = self._catalog.get_emesso_both_last_by_rifers(rifers) if not both.empty: for _, r in both.iterrows(): k = _rk(r.get('RIFER', '')) if k: target = result.emesso_g if str(r.get('tipo_rete', '')) == 'G' \ else result.emesso_t target[k] = r except Exception as e: logger.warning(f"LinkerRenderer emesso: {e}") # ── Box office ──────────────────────────────────────────────── try: df = self._catalog.get_boxoffice_by_rifers(rifers) if not df.empty: for _, r in df.iterrows(): k = _rk(r.get('RIFER', '')) if k: result.boxoffice[k] = r except Exception as e: logger.warning(f"LinkerRenderer boxoffice: {e}") # ── Rete slot ───────────────────────────────────────────────── try: df = self._catalog.get_scelte_rete_by_rifers(rifers) if not df.empty: for _, r in df.iterrows(): k = _rk(r.get('RIFER', '')) if k: result.rete_slot[k] = str(r.get('rete_slot', '') or '') except Exception as e: logger.warning(f"LinkerRenderer rete_slot: {e}") # ── Gemma valutazioni (per IMDB) ────────────────────────────── if imdb_codes: try: df = self._catalog.get_gemma_valutazioni_by_imdb_codes(imdb_codes) if not df.empty: for _, r in df.iterrows(): k = str(r.get('A_COD_IMDB', '') or '').strip() if k: result.gemma[k] = r except Exception as e: logger.warning(f"LinkerRenderer gemma: {e}") return result