"""Raw source archive — content-addressed, gzip-compressed, deduplicated. Historical snapshots are never discarded. Layout (under AIA_DATA_DIR): raw///.gz original bytes (HTML, JSON, XML, PDF…) text///.txt.gz cleaned text """ from __future__ import annotations import gzip import hashlib from pathlib import Path from aiatlas.config import settings def _path(root: Path, digest: str, suffix: str) -> Path: return root / digest[:2] / digest[2:4] / f"{digest}{suffix}" def store_raw(content: bytes, *, sha256: str | None = None) -> str: """Write bytes once (dedupe by hash); return the path relative to raw_dir.""" digest = sha256 or hashlib.sha256(content).hexdigest() path = _path(settings.raw_dir, digest, ".gz") if not path.exists(): path.parent.mkdir(parents=True, exist_ok=True) tmp = path.with_suffix(".gz.tmp") with gzip.open(tmp, "wb", compresslevel=6) as fh: fh.write(content) tmp.replace(path) return str(path.relative_to(settings.raw_dir)) def store_text(text: str) -> tuple[str, str]: """Write cleaned text; return (relative path, sha256 of the text).""" data = text.encode("utf-8") digest = hashlib.sha256(data).hexdigest() path = _path(settings.text_dir, digest, ".txt.gz") if not path.exists(): path.parent.mkdir(parents=True, exist_ok=True) tmp = path.with_suffix(".gz.tmp") with gzip.open(tmp, "wb", compresslevel=6) as fh: fh.write(data) tmp.replace(path) return str(path.relative_to(settings.text_dir)), digest def load_raw(rel_path: str) -> bytes: with gzip.open(settings.raw_dir / rel_path, "rb") as fh: return fh.read() def load_text(rel_path: str) -> str: with gzip.open(settings.text_dir / rel_path, "rb") as fh: return fh.read().decode("utf-8") def archive_size() -> dict[str, int]: out: dict[str, int] = {} for name, root in (("raw", settings.raw_dir), ("text", settings.text_dir)): total = 0 count = 0 if root.exists(): for p in root.rglob("*.gz"): try: total += p.stat().st_size count += 1 except OSError: pass out[f"{name}_bytes"] = total out[f"{name}_files"] = count return out __all__ = ["archive_size", "load_raw", "load_text", "store_raw", "store_text"]