#!/usr/bin/env python3
"""Render Athena entries into a static news site (two-layer: Top News + aging Stack).
Read-only against oracle.db. Writes static HTML to the preprod3 webroot.
Designed for a 20-min no_agent cron.
Usage:
python3 render_site.py # write to WEBROOT
python3 render_site.py --dry-run # print stats, write to ./_preview.html
"""
import argparse, html, os, json, sqlite3, datetime, re
from collections import OrderedDict
import clickability as cb
HERE = os.path.dirname(os.path.abspath(__file__))
WEBROOT = "/var/www/preprod3"
DB_PATH = os.path.join(HERE, "oracle.db")
TOP_N = 8
HALF_LIFE_H = 18.0
def _clean_summary(raw):
"""summary is stored as JSON {one_liner, key_technical_point, potential_use_case}.
Pull the most readable field; fall back to the raw text if it isn't JSON.
Strips markdown/latex noise so the card reads clean on the page."""
if not raw:
return ""
try:
d = json.loads(raw)
if isinstance(d, dict):
for k in ("one_liner", "key_technical_point", "potential_use_case"):
v = d.get(k)
if isinstance(v, str) and v.strip():
return re.sub(r"\\+|_|`", "", v).strip()
except Exception:
pass
return re.sub(r"\\+|_|`", "", raw).strip()
def _fmt_time(first_seen):
if not first_seen:
return ""
try:
dt = datetime.datetime.strptime(first_seen, "%Y-%m-%dT%H:%M:%SZ")
return dt.strftime("%H:%M")
except Exception:
return ""
def _card(it, big=False):
title = html.escape(it["title"] or "(untitled)")
url = html.escape(it["url"] or "#")
src = html.escape(it["source"])
sig = it.get("signal_score") or 0
t = _fmt_time(it.get("first_seen"))
summary_raw = _clean_summary(it.get("summary") or "")
summary = html.escape(summary_raw[:200])
cls = "card big" if big else "card"
summary_html = ('
{0}
'.format(summary)) if (summary and big) else ""
return f"""
{src}
{t}
sig {sig:.1f}
\U0001f525 {it['clickability_decayed']:.2f}
{summary_html}
"""
def build_html(items):
now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
ranked = sorted(items, key=lambda x: x["clickability_decayed"], reverse=True)
# Top News = fresh items only (ingested today, UTC). Yesterday's viral
# leftovers sink into the Stack instead of dominating the front page.
fresh = [it for it in ranked if it.get("fresh")]
top = fresh[:TOP_N]
stack = [it for it in ranked if it not in top]
# group stack by day (first_seen date)
by_day = OrderedDict()
for it in stack:
day = (it.get("first_seen") or "")[:10] or "unknown"
by_day.setdefault(day, []).append(it)
top_html = "".join(_card(it, big=True) for it in top)
stack_html = ""
for day, rows in by_day.items():
rows.sort(key=lambda x: x["clickability_decayed"], reverse=True)
cards = "".join(_card(it) for it in rows)
stack_html += f"""
\U0001f4c5 {html.escape(day)}
{cards}
"""
return f"""
Athena AI News — Ranked by Clickability
\U0001f534 Top News
{top_html}
\U0001f4f0 The Stack
{stack_html}
"""
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
conn = sqlite3.connect(DB_PATH)
items = cb.fetch_items(conn)
conn.close()
items = cb.compute_index(items)
items = cb.decay_index(items, HALF_LIFE_H)
page = build_html(items)
if args.dry_run:
out = os.path.join(HERE, "_preview.html")
with open(out, "w") as f:
f.write(page)
fresh = [it for it in items if it.get("fresh")]
top = sorted(fresh, key=lambda x: x["clickability_decayed"], reverse=True)[:TOP_N]
print(f"[dry-run] wrote {out} ({len(items)} items, {len(fresh)} fresh today)")
print(f"TOP {TOP_N} FRESH (today only) by decayed clickability:")
for i, it in enumerate(top, 1):
print(f" {i}. [{it['clickability_decayed']:.2f} | age {it['age_hours']:.0f}h] {it['source']:10} {it['title'][:55]}")
return
# Write to webroot if it exists (deployed); otherwise fall back to a
# user-owned dir so the no_agent cron never errors pre-deploy.
fallback = os.path.join(HERE, "site")
target = WEBROOT if os.path.isdir(WEBROOT) else fallback
os.makedirs(target, exist_ok=True)
with open(os.path.join(target, "index.html"), "w") as f:
f.write(page)
with open(os.path.join(target, "feed.json"), "w") as f:
json.dump([
{"title": i["title"], "url": i["url"], "source": i["source"],
"clickability_decayed": i["clickability_decayed"], "age_hours": i["age_hours"],
"first_seen": i.get("first_seen")}
for i in sorted(items, key=lambda x: x["clickability_decayed"], reverse=True)
], f, indent=2)
where = "WEBROOT" if target == WEBROOT else "fallback(~oracle/site)"
print(f"[render] wrote {target}/index.html ({len(items)} items) -> {where}")
if __name__ == "__main__":
main()