Files
roux/scripts/build-issue.py
T

101 lines
3.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Assemble a Roux issue's Hugo page bundles from a prompts JSON file.
python3 scripts/build-issue.py --issue 02 --prompts data/prompts/issue-02.json
Reads an array of objects with keys:
slug, title, description, category, tags[], prompt, featured (optional bool)
Writes content/posts/<issue>/<slug>/index.md for each, with plate numbers assigned
by stable sort on slug (matches scripts/import-posts.py). Frontmatter shape mirrors
the existing issue-01 bundles. `featured` comes from the JSON, so re-runs never lose
the homepage flags (unlike issue 01's hand-added ones).
Images are expected at content/posts/<issue>/<slug>/<slug>.png (written there by
scripts/generate-images.py). Pass --images DIR to copy them in from a staging dir
that holds either <slug>.png or <slug>/<slug>.png.
"""
import argparse
import json
import shutil
from pathlib import Path
SITE = Path(__file__).resolve().parent.parent
def norm_desc(desc: str) -> str:
desc = desc.strip()
if desc:
desc = desc[0].upper() + desc[1:]
if not desc.endswith((".", "!", "?")):
desc += "."
return desc.replace('"', '\\"')
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--issue", required=True)
ap.add_argument("--prompts", required=True, type=Path)
ap.add_argument("--images", type=Path, help="staging dir to copy <slug>.png from")
args = ap.parse_args()
content = SITE / "content" / "posts" / args.issue
content.mkdir(parents=True, exist_ok=True)
entries = json.loads(args.prompts.read_text(encoding="utf-8"))
entries.sort(key=lambda e: e["slug"])
missing_img = []
for idx, entry in enumerate(entries, start=1):
slug = entry["slug"]
plate = f"{idx:03d}"
title = entry["title"].strip().replace('"', '\\"')
desc = norm_desc(entry["description"])
cat = (entry.get("category") or "Uncategorised").strip()
tags = [t.strip() for t in entry.get("tags", []) if t.strip()]
bundle = content / slug
bundle.mkdir(exist_ok=True)
img_name = f"{slug}.png"
dst_img = bundle / img_name
if args.images and not dst_img.exists():
for cand in (args.images / img_name, args.images / slug / img_name):
if cand.is_file():
shutil.copy2(cand, dst_img)
break
if not dst_img.exists():
missing_img.append(slug)
cats_yaml = f' - "{cat}"'
tags_yaml = "\n".join(f' - "{t}"' for t in tags) if tags else " []"
featured_yaml = "\nfeatured: true" if entry.get("featured") else ""
md = f"""---
title: "{title}"
description: "{desc}"
plate: "{plate}"
slug: "{slug}"
issues:
- "{args.issue}"
image: "{img_name}"
weight: {idx}{featured_yaml}
categories:
{cats_yaml}
tags:
{tags_yaml}
---
"""
(bundle / "index.md").write_text(md, encoding="utf-8")
print(f"Wrote {len(entries)} bundles to {content}")
if missing_img:
print(f"WARNING: {len(missing_img)} bundles have no image yet: "
+ ", ".join(missing_img[:10]) + ("…" if len(missing_img) > 10 else ""))
return 0
if __name__ == "__main__":
raise SystemExit(main())