doocus first ver
This commit is contained in:
80
doocus/output_manager.py
Normal file
80
doocus/output_manager.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Manage per-document output folders and their sidecar files.
|
||||
|
||||
Parallels meetus/output_manager.py but for the document contract:
|
||||
|
||||
docs-output/<YYYYMMDD-NNN-stem>/
|
||||
content.md textified document — the product
|
||||
meta.json structured metadata
|
||||
thumb.jpg optional single preview image
|
||||
assets/ optional embedded images / per-page text (later)
|
||||
manifest.json run config + outputs inventory + source pointer
|
||||
"""
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from .naming import resolve_run_dir
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocOutputManager:
|
||||
"""Create and populate a run folder for one source document."""
|
||||
|
||||
def __init__(self, source_path: Path, base_output_dir: str = "docs-output", use_cache: bool = True):
|
||||
self.source_path = Path(source_path)
|
||||
self.base_output_dir = Path(base_output_dir)
|
||||
self.use_cache = use_cache
|
||||
self.output_dir = resolve_run_dir(self.base_output_dir, self.source_path.stem, use_cache)
|
||||
logger.info("Output directory: %s", self.output_dir)
|
||||
|
||||
def get_path(self, filename: str) -> Path:
|
||||
return self.output_dir / filename
|
||||
|
||||
def write_content(self, markdown: str) -> Path:
|
||||
path = self.output_dir / "content.md"
|
||||
path.write_text(markdown, encoding="utf-8")
|
||||
return path
|
||||
|
||||
def write_meta(self, meta: Dict[str, Any]) -> Path:
|
||||
path = self.output_dir / "meta.json"
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(meta, f, indent=2, ensure_ascii=False)
|
||||
return path
|
||||
|
||||
def write_thumb(self, jpeg_bytes: bytes) -> Path:
|
||||
path = self.output_dir / "thumb.jpg"
|
||||
path.write_bytes(jpeg_bytes)
|
||||
return path
|
||||
|
||||
def assets_dir(self) -> Path:
|
||||
d = self.output_dir / "assets"
|
||||
d.mkdir(exist_ok=True)
|
||||
return d
|
||||
|
||||
def save_manifest(self, config: Dict[str, Any], outputs: Dict[str, Any], source_sha256: str) -> Path:
|
||||
manifest_path = self.output_dir / "manifest.json"
|
||||
manifest = {
|
||||
"source": {
|
||||
"name": self.source_path.name,
|
||||
"path": str(self.source_path.absolute()),
|
||||
"sha256": source_sha256,
|
||||
},
|
||||
"processed_at": datetime.now().isoformat(),
|
||||
"configuration": config,
|
||||
"outputs": outputs,
|
||||
}
|
||||
with open(manifest_path, "w", encoding="utf-8") as f:
|
||||
json.dump(manifest, f, indent=2, ensure_ascii=False)
|
||||
logger.info("Saved manifest: %s", manifest_path)
|
||||
return manifest_path
|
||||
|
||||
def load_manifest(self) -> Optional[Dict[str, Any]]:
|
||||
manifest_path = self.output_dir / "manifest.json"
|
||||
if manifest_path.exists():
|
||||
with open(manifest_path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
return None
|
||||
Reference in New Issue
Block a user