"""pubsalesacc/utils/io.py — DataFrame and file I/O helpers.""" import logging import math import os from pathlib import Path import pandas as pd from pubsalesacc.utils.dates import get_timestamp logger = logging.getLogger(__name__) def df_excel_output( df: pd.DataFrame, filename: str, interval: int = 1_000_000, timestamp: bool = True, ) -> Path: """Write a DataFrame to an XLSX file, splitting into sheets if rows > interval. Args: df: DataFrame to write. filename: Output filename (extension optional; .xlsx appended if absent). interval: Max rows per sheet. Default 1,000,000 (Excel limit). timestamp: Append a YYYYMMDD-HHMMSS suffix to the filename. Returns: Path to the written file. """ base = Path(os.path.splitext(filename)[0]) stem = str(base) out = Path(f"{stem}-{get_timestamp()}.xlsx" if timestamp else f"{stem}.xlsx") if df.empty: logger.warning("Cannot write %s — DataFrame is empty.", out.name) return out logger.info("Writing %s (%d rows)...", out.name, len(df)) sheet_count = math.ceil(len(df) / interval) with pd.ExcelWriter(out) as writer: for sheet_idx in range(sheet_count): start = sheet_idx * interval end = min(start + interval, len(df)) logger.info( " Sheet %d/%d: rows %d–%d", sheet_idx + 1, sheet_count, start, end ) df.iloc[start:end].to_excel( writer, sheet_name=f"DataSheet{sheet_idx + 1}", index=False ) logger.info("Excel output complete: %s", out.name) return out def df_csv_output( df: pd.DataFrame, filename: str, encoding: str = "utf-8", ) -> Path: """Write a DataFrame to a timestamped CSV file. Returns: Path to the written file. """ base = Path(os.path.splitext(filename)[0]) out = Path(f"{base}-{get_timestamp()}.csv") if df.empty: logger.warning("Cannot write %s — DataFrame is empty.", out.name) return out logger.info("Writing %s (%d rows)...", out.name, len(df)) df.to_csv(out, encoding=encoding, index=False) logger.info("CSV output complete: %s", out.name) return out def read_sql_file(filepath: str, sep: str = ";") -> list[str]: """Read a SQL file and return a list of non-empty statements split on sep.""" with open(filepath, "r") as f: content = f.read() return [s.strip() for s in content.split(sep) if s.strip()] def make_dir(path: str | Path, quiet: bool = False) -> None: """Create directory (and parents) if it does not already exist.""" p = Path(path) if not p.exists(): p.mkdir(parents=True, exist_ok=True) logger.info("Created directory: %s", p) elif not quiet: logger.debug("Directory already exists: %s", p)