#!/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/preprod2" 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}

{title}

{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

Athena AI News

Auto-ranked by Clickability Index · decays with age so the stack flows top → bottom · generated {now} · {len(items)} stories

\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()