Comments on news posts: one forum discussion per post
oc:pages command in the forum extension, tools/forum_sync.py and 'make forum-sync' (run by deploy) keep data/forum_pages.json up to date; post pages end with their discussion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDRjtmAiNpo5mK5N3d7QwZ
This commit is contained in:
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Make sure every published news post has its comment discussion in the forum.
|
||||
|
||||
Lists the posts with `hugo list all`, sends the list to the forum, where `php flarum oc:pages`
|
||||
creates the missing discussions, and stores the returned map (content file -> discussion number)
|
||||
in data/forum_pages.json, which the layouts read. Run before building (see `make forum-sync`).
|
||||
|
||||
--run is the command that executes a shell line where the forum lives, e.g.
|
||||
'ssh -p 27 user@host' or 'docker compose -f forum/docker-compose.yml exec -T flarum sh -c'
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import json
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
MAP = ROOT / "data" / "forum_pages.json"
|
||||
|
||||
|
||||
def posts():
|
||||
"""Published posts as the forum command expects them, oldest first."""
|
||||
out = subprocess.run(["hugo", "list", "all"], cwd=ROOT, check=True, capture_output=True, text=True).stdout
|
||||
now, pages = datetime.now(timezone.utc), []
|
||||
for row in csv.DictReader(io.StringIO(out)):
|
||||
if row["kind"] != "page" or row["section"] != "post" or row["draft"] == "true":
|
||||
continue
|
||||
date = datetime.fromisoformat(row["date"].replace("Z", "+00:00"))
|
||||
published = datetime.fromisoformat(row["publishDate"].replace("Z", "+00:00"))
|
||||
if published > now:
|
||||
continue
|
||||
pages.append({
|
||||
"key": row["path"].removeprefix("content/"),
|
||||
"title": row["title"],
|
||||
"date": date.strftime("%Y-%m-%d %H:%M:%S") if date.year > 1 else "",
|
||||
"path": urlparse(row["permalink"]).path,
|
||||
})
|
||||
return sorted(pages, key=lambda p: p["date"])
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
||||
ap.add_argument("--run", required=True, help="command prefix that runs a shell line on the forum host")
|
||||
ap.add_argument("--dir", required=True, help="forum folder on that host")
|
||||
ap.add_argument("--php", default="php", help="PHP command line there")
|
||||
ap.add_argument("--site-url", required=True, help="public address of the site, for the link in each discussion")
|
||||
args = ap.parse_args()
|
||||
|
||||
pages = posts()
|
||||
remote = (f"cd {shlex.quote(args.dir)} && cat > storage/oc-pages.json && "
|
||||
f"{args.php} flarum oc:pages storage/oc-pages.json --site-url={shlex.quote(args.site_url.rstrip('/'))} "
|
||||
f"--map=storage/oc-pages-map.json >&2 && cat storage/oc-pages-map.json; "
|
||||
f"rm -f storage/oc-pages.json storage/oc-pages-map.json")
|
||||
done = subprocess.run([*shlex.split(args.run), remote], input=json.dumps(pages), capture_output=True, text=True)
|
||||
notes = [line for line in done.stderr.splitlines() if "discussions created" in line]
|
||||
if done.returncode != 0 or not done.stdout.strip().startswith(("{", "[")):
|
||||
sys.exit(f"forum sync failed:\n{done.stderr.strip()[-800:]}")
|
||||
mapping = json.loads(done.stdout) or {}
|
||||
text = json.dumps(dict(sorted(mapping.items())), indent=1) + "\n"
|
||||
if not MAP.exists() or MAP.read_text() != text:
|
||||
MAP.write_text(text)
|
||||
print(f"{len(pages)} posts; {notes[-1].strip() if notes else 'done'}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user