Andrew Ridgway bec1eaac87
Some checks failed
Test / test (push) Has been cancelled
first pass at the newspaper builder
2026-09-14 11:57:22 +10:00

92 lines
3.1 KiB
Python

"""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())