"""Utils.""" from datetime import date from typing import Any, Generator, Optional from dateutil.parser import parse # Cells starting with any of these are evaluated as formulas by Excel / # LibreOffice when a report is opened for review. Prefix with `'` to # neutralise — the apostrophe is stripped by the spreadsheet on display # and ignored by downstream CSV readers. CSV_INJECTION_PREFIXES = ('=', '+', '-', '@', '\t', '\r') def chunks(items: list[Any], chunk_size: int) -> Generator[list[Any], None, None]: """Yield chunks from list.""" for i in range(0, len(items), chunk_size): yield items[i : i + chunk_size] def sanitize_csv_cell(value: Any) -> Any: """Neutralise CSV-injection prefixes on string cells.""" if isinstance(value, str) and value.startswith(CSV_INJECTION_PREFIXES): return "'" + value return value def empty_str_to_none(value: str | None) -> str | None: """Parse empty strings as None.""" return value or None def parse_str_to_bool(value: str | bool | None) -> bool | None: """Parse boolean value.""" if value is None or value == '': return None if isinstance(value, str): if value.upper() == 'TRUE': return True elif value.upper() == 'FALSE': return False elif isinstance(value, bool): return value raise ValueError('Invalid boolean string') def parse_date(value: Optional[str | date]) -> Optional[date]: """Validate string as date in format %m/%d/%Y""" if not value: return None if isinstance(value, date): return value return parse(value).date()