Files
newspaper_wedding/newspaper/funnies/cartoon.py
T
2026-09-14 11:57:22 +10:00

64 lines
2.2 KiB
Python

"""funnies cartoon + comic selection.
Per functional design: BR2.1 (search the real xkcd site at build time, embed
locally), BR2.2 (tasteful content-derived fallback when no cartoon found),
BR3.1 (content-enrichment comics). A build-time internet fetch is allowed to
enrich content; the EMITTED page stays self-contained (NFR4) because fetched
assets are embedded as a local ref, never a remote URL at print.
"""
from __future__ import annotations
from typing import Protocol
from .model import Cartoon, Comic
class CartoonSource(Protocol):
"""Injectably fetches/selects a cartoon from an enrichment source."""
def fetch(self, context: str) -> Cartoon | None: ...
class StubXkcdCartoonSource:
"""Deterministic stub (tests / offline): returns a local-bundled cartoon."""
def __init__(self, found: bool = True) -> None:
self.found = found
def fetch(self, context: str) -> Cartoon | None:
if not self.found:
return None
return Cartoon(
cartoon_id="xkcd-1",
source="xkcd",
art_ref="assets/xkcd-1.svg", # local/bundled — never remote
caption="A cartoon tied to the occasion",
)
def tasteful_fallback(context: str, cartoon_id: str = "fallback-1") -> Cartoon:
"""BR2.2: a tasteful content-derived strip when no cartoon is found.
Returns a locally-derived placeholder (never a remote URL at print).
"""
return Cartoon(
cartoon_id=cartoon_id,
source="generated",
art_ref="assets/fallback-strip.svg",
caption=f"A content-derived strip for: {context[:60]}",
)
def select_cartoon(context: str, source: CartoonSource | None = None) -> Cartoon:
"""BR2.1/BR2.2: fetch an xkcd cartoon, falling back to a tasteful strip."""
src = source or StubXkcdCartoonSource(found=True)
cartoon = src.fetch(context)
return cartoon if cartoon else tasteful_fallback(context)
def build_comic(comic_id: str, source: str, art_ref: str) -> Comic:
"""BR3.1: a comic strip placed in the funnies section (overridable per
article). The caller supplies a local/bundled art ref."""
return Comic(comic_id=comic_id, source=source, art_ref=art_ref)