92 lines
3.1 KiB
Python
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())
|