53 lines
1.8 KiB
Python
53 lines
1.8 KiB
Python
"""
|
|
Run-folder naming + content hashing for doocus.
|
|
|
|
Reuses meetus's `YYYYMMDD-NNN-<stem>` run-folder convention (see
|
|
meetus/output_manager.py) so both trees look the same. Kept here to keep doocus
|
|
self-contained; meetus could adopt this helper later.
|
|
"""
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
import hashlib
|
|
|
|
|
|
def sha256_file(path: Path, chunk: int = 1 << 20) -> str:
|
|
"""Streaming sha256 of a file — never loads the whole file into memory."""
|
|
h = hashlib.sha256()
|
|
with open(path, "rb") as f:
|
|
for block in iter(lambda: f.read(chunk), b""):
|
|
h.update(block)
|
|
return h.hexdigest()
|
|
|
|
|
|
def resolve_run_dir(base_output_dir: Path, stem: str, use_cache: bool = True) -> Path:
|
|
"""Return (creating if needed) the run folder for `stem` under `base_output_dir`.
|
|
|
|
Mirrors meetus/output_manager.py:37-84 — reuse the most recent
|
|
`*-<stem>` folder when caching, else create `YYYYMMDD-NNN-<stem>` with the
|
|
next run number for today.
|
|
"""
|
|
base = Path(base_output_dir)
|
|
|
|
if use_cache and base.exists():
|
|
existing = sorted(
|
|
[d for d in base.iterdir() if d.is_dir() and d.name.endswith(f"-{stem}")],
|
|
reverse=True,
|
|
)
|
|
if existing:
|
|
return existing[0]
|
|
|
|
date_str = datetime.now().strftime("%Y%m%d")
|
|
next_run = 1
|
|
if base.exists():
|
|
nums = []
|
|
for d in base.iterdir():
|
|
if d.is_dir() and d.name.startswith(date_str) and d.name.endswith(f"-{stem}"):
|
|
parts = d.name.split("-")
|
|
if len(parts) >= 2 and parts[1].isdigit():
|
|
nums.append(int(parts[1]))
|
|
next_run = max(nums) + 1 if nums else 1
|
|
|
|
run_dir = base / f"{date_str}-{next_run:03d}-{stem}"
|
|
run_dir.mkdir(parents=True, exist_ok=True)
|
|
return run_dir
|