[object Object]

← back to Crazy News Channel

p24 daily-cartoons: approve.py defaults --story-id from meta.json, links source article

0b40f82a6caa3b66c100561c3d712cbde009c5c7 · 2026-09-25 10:20:22 -0700 · Steve Abrams

--story-id is no longer required on the command line — it defaults to the article
generate.py recorded in meta.json (story_id) and is still validated against
stories-data.js/real-news-data.js; pass --story-id to override. The generated
cartoon page now also shows a "Source: <headline>" link to the linked article when
present. Works unchanged with or without clip.mp4 (poster-only renders as a still
<img>, no style violations — only ../assets/style.css is linked).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq

Files touched

Diff

commit 0b40f82a6caa3b66c100561c3d712cbde009c5c7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Sep 25 10:20:22 2026 -0700

    p24 daily-cartoons: approve.py defaults --story-id from meta.json, links source article
    
    --story-id is no longer required on the command line — it defaults to the article
    generate.py recorded in meta.json (story_id) and is still validated against
    stories-data.js/real-news-data.js; pass --story-id to override. The generated
    cartoon page now also shows a "Source: <headline>" link to the linked article when
    present. Works unchanged with or without clip.mp4 (poster-only renders as a still
    <img>, no style violations — only ../assets/style.css is linked).
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq
---
 daily-cartoons/approve.py | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/daily-cartoons/approve.py b/daily-cartoons/approve.py
index 9a57f74..f13bd8b 100755
--- a/daily-cartoons/approve.py
+++ b/daily-cartoons/approve.py
@@ -1,11 +1,14 @@
 #!/usr/bin/env python3
 """Approve a queued cartoon INTO THE LOCAL SITE TREE ONLY. Never deploys/rsyncs.
 
-  python3 daily-cartoons/approve.py <slug> --date YYYY-MM-DD --story-id <id> [--section politics]
+  python3 daily-cartoons/approve.py <slug> --date YYYY-MM-DD [--story-id <id>] [--section politics]
 
-Copies poster/clip to cartoons/media/, writes cartoons/<date>-<slug>.html, and appends a
-manifest.js entry. --story-id is REQUIRED and must exist in stories-data.js or
-real-news-data.js (standing rule TK-12158: every cartoon links to a real article).
+Copies poster/clip (clip is optional — poster-only cartoons render as a still image) to
+cartoons/media/, writes cartoons/<date>-<slug>.html, and appends a manifest.js entry.
+--story-id DEFAULTS to the article the cartoon was generated from (meta.json's story_id,
+written by article-driven generate.py, TK-12237) and is still validated against
+stories-data.js / real-news-data.js; pass --story-id to override. A story_id is REQUIRED
+either way (standing rule TK-12158: every cartoon links to a real article).
 Publishing to p24.agentabrams.com stays a separate, Steve-run deploy."""
 import argparse, datetime, html, json, os, re, shutil, sys
 import common as C
@@ -23,12 +26,18 @@ def story_exists(sid):
 def main():
     ap = argparse.ArgumentParser()
     ap.add_argument("slug"); ap.add_argument("--date", default=datetime.date.today().isoformat())
-    ap.add_argument("--story-id", required=True); ap.add_argument("--section", default="politics")
+    ap.add_argument("--story-id", default=None); ap.add_argument("--section", default="politics")
     a = ap.parse_args()
     q = os.path.join(C.QUEUE, a.date, a.slug); mp = os.path.join(q, "meta.json")
     if not os.path.exists(mp): sys.exit(f"not queued: {q}")
-    if not story_exists(a.story_id): sys.exit(f"story_id '{a.story_id}' not found in stories-data.js / real-news-data.js")
-    m = json.load(open(mp)); base = f"{a.date.replace('-','')}-{a.slug}"
+    m = json.load(open(mp))
+    story_id = a.story_id or m.get("story_id")
+    if not story_id:
+        sys.exit("--story-id required (meta.json has no story_id — this cartoon predates the "
+                 "article-driven pipeline; pass --story-id explicitly)")
+    if not story_exists(story_id): sys.exit(f"story_id '{story_id}' not found in stories-data.js / real-news-data.js")
+    a.story_id = story_id
+    base = f"{a.date.replace('-','')}-{a.slug}"
     os.makedirs(os.path.join(CART, "media"), exist_ok=True)
     shutil.copy2(os.path.join(q, "poster.jpg"), os.path.join(CART, "media", base + ".jpg"))
     has_clip = os.path.exists(os.path.join(q, "clip.mp4"))
@@ -41,11 +50,14 @@ def main():
     # under body.cartoon-page-simple so every future approved cartoon page
     # renders in the site's one fixed palette + one font, same as every
     # other page, with nothing that changes on click.
+    src_url, src_title = m.get("story_url"), m.get("story_title")
+    src_html = (f'<p class="src"><a href="{e(src_url)}" target="_blank" rel="noopener noreferrer">Source: {e(src_title or "linked article")}</a></p>'
+                if src_url else "")
     open(os.path.join(CART, base + ".html"), "w").write(f'''<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">
 <meta name="viewport" content="width=device-width,initial-scale=1"><title>{e(m["title"])}</title>
 <link rel="stylesheet" href="../assets/style.css"></head>
 <body class="cartoon-page-simple"><p><a href="index.html">← Cartoon Desk</a></p>{media}<h1>{e(m["title"])}</h1>
-<p class="cap">&ldquo;{e(m["caption"])}&rdquo;</p><p><small>AI-generated editorial cartoon in classic editorial-ink style. Invented figures only.</small></p></body></html>''')
+<p class="cap">&ldquo;{e(m["caption"])}&rdquo;</p>{src_html}<p><small>AI-generated editorial cartoon in classic editorial-ink style. Invented figures only.</small></p></body></html>''')
     mf = os.path.join(CART, "manifest.js"); src = open(mf).read()
     mm = re.search(r"(window\.P24_CARTOONS\s*=\s*)(\[[\s\S]*\])(\s*;\s*)$", src)
     arr = json.loads(mm.group(2)); arr = [x for x in arr if x.get("id") != a.slug]

← b8bc8d0 p24 daily-cartoons: generate cartoons from real articles, st  ·  back to Crazy News Channel  ·  p24 daily-cartoons: review queue shows linked article, rende fb5ea85 →