import json import os import shutil from datetime import datetime from loguru import logger _SERVER_BASE = r"\\Mediaset.it\cologno\Osservatorio_Progetti\MonitorOsservatorio\MonitorOsservatorioPy" _APPDATA_BASE = os.path.join( os.environ.get("APPDATA", os.path.expanduser("~")), "MonitorOsservatorio" ) _USERNAME = os.environ.get("USERNAME", "unknown") _NUM_BACKUPS = 3 _data_dir: str | None = None def _resolve_data_dir() -> str: global _data_dir if _data_dir is not None: return _data_dir server_user_dir = os.path.join(_SERVER_BASE, "utenti", _USERNAME) try: os.makedirs(server_user_dir, exist_ok=True) # quick write-test probe = os.path.join(server_user_dir, ".probe") with open(probe, "w") as f: f.write("ok") os.unlink(probe) _data_dir = server_user_dir logger.info("Session storage: {}", _data_dir) except Exception as e: logger.warning("Server share non raggiungibile ({}), uso APPDATA", e) _data_dir = _APPDATA_BASE return _data_dir def _ensure() -> str: d = _resolve_data_dir() os.makedirs(d, exist_ok=True) os.makedirs(os.path.join(d, "export"), exist_ok=True) return d def _log_path() -> str: return os.path.join(_resolve_data_dir(), "monitor.log") def init_logging(): log_file = _log_path() logger.add(log_file, rotation="1 MB", retention=3, encoding="utf-8", level="INFO") def _src_file() -> str: return os.path.join(_resolve_data_dir(), "current_source.xlsx") def _ses_file() -> str: return os.path.join(_resolve_data_dir(), "current_session.json") def _backup_file(n: int) -> str: return os.path.join(_resolve_data_dir(), f"session_backup_{n}.json") def _rotate_backups(): ses = _ses_file() if not os.path.exists(ses): return # shift backups: backup_2 → backup_3, backup_1 → backup_2, current → backup_1 for i in range(_NUM_BACKUPS, 1, -1): src = _backup_file(i - 1) dst = _backup_file(i) if os.path.exists(src): shutil.copy2(src, dst) shutil.copy2(ses, _backup_file(1)) def save_source(src_path: str) -> str: _ensure() dst = _src_file() shutil.copy2(src_path, dst) logger.info("Source salvato: {}", dst) return dst def save_annotations(records, source_basename: str, paese: str): _ensure() _rotate_backups() data = { "version": 1, "source_basename": source_basename, "paese": paese, "saved_at": datetime.now().isoformat(timespec="seconds"), "username": _USERNAME, "annotations": [ { "source_row": r.source_row, "prima_tv": r.prima_tv, "cod_oss": r.cod_oss, "agganciato": r.agganciato, "tooltip": r.tooltip, } for r in records ], } with open(_ses_file(), "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False) def load_session() -> dict | None: ses = _ses_file() src = _src_file() if not os.path.exists(ses) or not os.path.exists(src): # try backups if main session missing for i in range(1, _NUM_BACKUPS + 1): bak = _backup_file(i) if os.path.exists(bak) and os.path.exists(src): logger.warning("Session corrotta, ripristino backup_{}", i) shutil.copy2(bak, ses) break else: return None try: with open(_ses_file(), "r", encoding="utf-8") as f: return json.load(f) except Exception as e: logger.error("Errore lettura session: {}", e) return None def source_path() -> str: return _src_file() def export_dir() -> str: d = os.path.join(_resolve_data_dir(), "export") os.makedirs(d, exist_ok=True) return d def list_users() -> list[dict]: """Returns [{username, filename, saved_at}] for all users with an active session.""" result = [] utenti_dir = os.path.join(_SERVER_BASE, "utenti") current_user = _USERNAME try: for name in os.listdir(utenti_dir): if name == current_user: continue ses_path = os.path.join(utenti_dir, name, "current_session.json") src_path = os.path.join(utenti_dir, name, "current_source.xlsx") if not os.path.exists(ses_path) or not os.path.exists(src_path): continue try: with open(ses_path, "r", encoding="utf-8") as f: data = json.load(f) result.append({ "username": name, "filename": data.get("source_basename", ""), "saved_at": data.get("saved_at", ""), }) except Exception: pass except Exception as e: logger.warning("Impossibile leggere utenti da {}: {}", utenti_dir, e) return result def source_path_for_user(username: str) -> str | None: """Returns the xlsx path for the given user, or None if not found.""" src_path = os.path.join(_SERVER_BASE, "utenti", username, "current_source.xlsx") return src_path if os.path.exists(src_path) else None def load_session_from_user(username: str) -> tuple[dict | None, str | None]: """Returns (session_dict, xlsx_path) for given user, or (None, None).""" utenti_dir = os.path.join(_SERVER_BASE, "utenti") ses_path = os.path.join(utenti_dir, username, "current_session.json") src_path = os.path.join(utenti_dir, username, "current_source.xlsx") if not os.path.exists(ses_path) or not os.path.exists(src_path): return None, None try: with open(ses_path, "r", encoding="utf-8") as f: session = json.load(f) return session, src_path except Exception as e: logger.error("Errore lettura sessione di {}: {}", username, e) return None, None def clear_session(): for path in [_ses_file(), _src_file()]: try: if os.path.exists(path): os.unlink(path) except Exception as e: logger.warning("Impossibile cancellare {}: {}", path, e) def apply_annotations(records, session: dict): ann_by_row = {a["source_row"]: a for a in session.get("annotations", [])} for r in records: ann = ann_by_row.get(r.source_row) if ann: r.prima_tv = ann.get("prima_tv", "") r.cod_oss = ann.get("cod_oss", "") r.agganciato = ann.get("agganciato", False) r.tooltip = ann.get("tooltip", "")