"""newspaper generate — the one-shot CLI entry point (Q1=A, US5).""" from __future__ import annotations import argparse import sys from pathlib import Path from ..ai_draft import build_brief, draft_newspaper from ..funnies import FunniesBrief, FunniesBuilder from .content import load_config, load_content from .emitter import emit_empty_state, emit_newspaper from .layout import build_layout from .model import Article, Issue def _default_paths(project: Path) -> tuple[Path, Path, Path]: cfg = project / "config.json" content = project / "content" out = project / "newspaper.html" return cfg, content, out def run_generate(project: Path, draft: bool = False) -> Path: """Read config + content, build the issue layout, write newspaper.html.""" cfg, content, out = _default_paths(project) has_content = content.exists() and any( p.suffix.lower() in {".md", ".txt"} for p in content.rglob("*") ) if not has_content: # BR5.2 / US10: friendly guided empty-state, never a blank page masthead = None if cfg.exists(): try: masthead = load_config(cfg) except Exception: masthead = None emit_empty_state(out, masthead) return out masthead = load_config(cfg) if cfg.exists() else None if masthead is None: # Minimal default so the paper still builds from .model import MastheadConfig masthead = MastheadConfig("The Wedding Times", "The Happy Couple", "", "1", "1") articles = load_content(content) if draft: # Optional AI copy: ai-draft returns an empty DraftBundle on failure (BR4.1) brief = build_brief("The couple, the occasion, and these stories") articles = _apply_draft(articles, brief) pages = build_layout(articles) issue = Issue(issue_id="issue-1", masthead=masthead, articles=articles, pages=pages) emit_newspaper(issue, out) return out def _apply_draft(articles: list[Article], brief) -> list[Article]: """Fold an optional AI draft in, falling back to the original on failure.""" try: from newspaper.ai_draft.draft import draft_newspaper bundle = draft_newspaper(brief, _NoopModel()) if bundle.articles: return [Article(a.article_id, a.type, a.headline, a.body, a.byline, a.section) for a in bundle.articles] except Exception: pass return articles class _NoopModel: def generate(self, brief_text, article_type, article_id): raise RuntimeError("model transport not wired here") def _build_arg_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser(prog="newspaper", description="Generate a local wedding newspaper.") p.add_argument("--project", type=Path, default=Path.cwd()) p.add_argument("--draft", action="store_true", help="Optionally draft copy via ai-draft") return p def main(argv: list[str] | None = None) -> int: args = _build_arg_parser().parse_args(argv) out = run_generate(args.project, draft=args.draft) print(f"Newspaper written to: {out}") return 0 if __name__ == "__main__": sys.exit(main())