61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""funnies — builds the newspaper's fun section from the content itself.
|
|
|
|
Orchestrates theme derivation, find-a-word, crossword (AI-drafted clues),
|
|
cartoon/comic selection into a FunniesResult. Graceful empty/fallback when no
|
|
viable content (Q5=A / BR3.2); the emitted result stays self-contained (BR4.1).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .crossword import ClueDrafter, build_crossword
|
|
from .find_a_word import build_find_a_word, derive_theme_words
|
|
from .cartoon import CartoonSource, Comic, build_comic, select_cartoon
|
|
from .model import FunniesBrief, FunniesResult
|
|
|
|
|
|
class FunniesBuilder:
|
|
"""Funnies library entry point — assemble the funnies section."""
|
|
|
|
def __init__(
|
|
self,
|
|
clue_drafter: ClueDrafter | None = None,
|
|
cartoon_source: CartoonSource | None = None,
|
|
) -> None:
|
|
self._clue_drafter = clue_drafter
|
|
self._cartoon_source = cartoon_source
|
|
|
|
def build(self, brief: FunniesBrief) -> FunniesResult:
|
|
if not brief.theme_words and not brief.context.strip():
|
|
# BR3.2 / Q5=A: nothing viable — graceful empty, never a broken block
|
|
return FunniesResult(
|
|
funnies_result_id="funnies-1",
|
|
status="empty",
|
|
status_message="No theme words or context provided; funnies skipped.",
|
|
)
|
|
|
|
words = brief.theme_words or derive_theme_words(brief.context)
|
|
if not words:
|
|
return FunniesResult(
|
|
funnies_result_id="funnies-1",
|
|
status="empty",
|
|
status_message="Could not derive any theme words from content.",
|
|
)
|
|
|
|
find_word = build_find_a_word(words)
|
|
crossword = build_crossword(words, brief.context, self._clue_drafter)
|
|
cartoon = select_cartoon(brief.context, self._cartoon_source)
|
|
|
|
result = FunniesResult(
|
|
funnies_result_id="funnies-1",
|
|
crossword=crossword,
|
|
find_a_word=find_word,
|
|
cartoons=[cartoon],
|
|
comics=[],
|
|
status="ready",
|
|
)
|
|
# If the cartoon had to fall back, mark fallback (Q5=A)
|
|
if cartoon.source == "generated":
|
|
result.status = "fallback"
|
|
result.status_message = "No xkcd found; used a content-derived strip."
|
|
return result
|