Files
roux/scripts/import-posts.py
T
valknar 76f87d5009 One category per post, display in grid without tag
Import script now takes only the first CSV category per post.
All card grid templates simplified to show just that single
category in card__sub — no tag alongside it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:22:20 +02:00

113 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""
Import posts from ~/projects/ginger/posts.csv into Hugo content.
Only imports posts whose generated_image exists in the selected images folder.
Run from the site root: python3 scripts/import-posts.py
"""
import csv
import json
import os
import shutil
from pathlib import Path
GINGER = Path.home() / "projects" / "ginger"
CSV_PATH = GINGER / "posts.csv"
IMG_DIR = GINGER / "images" / "final" / "selected"
SITE = Path(__file__).parent.parent
CONTENT = SITE / "content" / "posts"
DATA_DIR = SITE / "data"
CONTENT.mkdir(parents=True, exist_ok=True)
DATA_DIR.mkdir(parents=True, exist_ok=True)
# Collect available images
available = {f.name for f in IMG_DIR.iterdir() if f.is_file()}
matched = []
with open(CSV_PATH, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
gen = row.get("generated_image", "").strip()
if gen and gen in available:
matched.append(row)
print(f"Matched {len(matched)} posts out of {len(available)} available images")
# Sort deterministically by generated_image filename for stable plate numbers
matched.sort(key=lambda r: r["generated_image"])
for idx, row in enumerate(matched, start=1):
plate = f"{idx:03d}"
gen_img = row["generated_image"].strip()
slug = gen_img.replace(".png", "")
title = row["title"].strip()
desc = row["description"].strip().replace('"', '\\"')
raw_cat = row.get("category", "").strip()
raw_tags = row.get("tags", "").strip()
cats = [raw_cat.split(",")[0].strip()] if raw_cat.strip() else ["Uncategorised"]
tags = [t.strip() for t in raw_tags.split(",") if t.strip()]
cats_yaml = "\n".join(f' - "{c}"' for c in cats)
tags_yaml = "\n".join(f' - "{t}"' for t in tags) if tags else ' []'
bundle = CONTENT / slug
bundle.mkdir(exist_ok=True)
src_img = IMG_DIR / gen_img
dst_img = bundle / gen_img
if not dst_img.exists():
shutil.copy2(src_img, dst_img)
md = f"""---
title: "{title.replace('"', '\\"')}"
description: "{desc}"
plate: "{plate}"
slug: "{slug}"
issues:
- "01"
image: "{gen_img}"
weight: {idx}
categories:
{cats_yaml}
tags:
{tags_yaml}
---
"""
(bundle / "index.md").write_text(md, encoding="utf-8")
print(f"Created {len(matched)} page bundles in {CONTENT}")
# Write data/issues.json
issues = [
{
"id": "01",
"number": "№ 01",
"title": "Fabric, Light & Gesture",
"season": "Spring MMXXVI",
"publishedAt": "2026-03-21",
"blurb": "The inaugural plates — one hundred photographs gathered across ateliers, streets, and quiet hotel corridors.",
"status": "current"
},
{
"id": "02",
"number": "№ 02",
"title": "The Glasshouse",
"season": "Summer MMXXVI",
"publishedAt": "2026-06-21",
"blurb": "Forthcoming — a study in transparency, condensation, and the long late light of June.",
"status": "forthcoming"
},
{
"id": "03",
"number": "№ 03",
"title": "Atelier After Hours",
"season": "Autumn MMXXVI",
"publishedAt": "2026-09-22",
"blurb": "Forthcoming — the slow work of cloth and thread, photographed when the studio is half-empty.",
"status": "forthcoming"
}
]
(DATA_DIR / "issues.json").write_text(json.dumps(issues, indent=2), encoding="utf-8")
print("Wrote data/issues.json")