Files
athena-oracle/oracle/weekly.py
T
Epictetus 641d531d88 Sprint 3: Anti-bot retrieval layer + metrics module
Anti-bot changes (all 6 adapters):
- Browser-grade User-Agent rotation (Chrome/Firefox on Linux/Windows)
- Shared browser_headers() with Accept, Accept-Language, DNT
- Session-consistent UA fingerprint (picked once, not per-request)
- jitter_sleep() replaces fixed time.sleep() on all adapters
- Exponential backoff on 429/503 already on reddit, now consistent

New shared module:
- adapters/__init__.py: browser_user_agent(), browser_headers(), jitter_sleep()
- adapters/_http.py: HTTPClient class for future browser-mode adapters

Metrics module (from Sprint 2 carry):
- oracle/metrics.py: MetricsRun for log_adapter/log_verdicts/log_scores
- oracle/weekly.py: SYSTEM HEALTH section wired to adapter_health
- oracle/cli.py: metrics subparser with --adapters/--publish/--scores/--alerts

Before: bot signatures like 'ai-oracle/0.1', 'python:athena:v0.1'
After: 'Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0'
2026-07-22 14:26:42 +00:00

343 lines
11 KiB
Python

"""Weekly review — synthesize the week's activity into patterns and trends.
CyrilXBT masterclass pattern: Sunday 7PM synthesis that identifies
recurring themes, tracks source performance, and generates insights.
Operates as a pure-rules aggregation layer — no AI synthesis needed.
"""
import sqlite3
from datetime import datetime, timedelta
from pathlib import Path
from collections import Counter
from oracle.metrics import get_adapter_health, get_metric_alerts
WEEKLY_TEMPLATE = """# Weekly Review — {week_start} to {week_end}
## SIGNAL SUMMARY
- **Total entries**: {total_entries}
- **New this week**: {new_this_week}
- **PUBLISH verdicts**: {publish_count}
- **WATCH verdicts**: {watch_count}
- **Average signal score**: {avg_score:.2f}
## TOP SOURCES (by entry count)
{sources_md}
## TOP CATEGORIES (by frequency)
{categories_md}
## SOURCE TIER BREAKDOWN
{tier_md}
## TRENDING THEMES
{trending_md}
## WEEK OVER WEEK COMPARISON
{wow_md}
## SYSTEM HEALTH
{health_md}
## RECOMMENDATIONS
{recs_md}
"""
def get_connection(db_path: str) -> sqlite3.Connection:
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
return conn
def _week_bounds(days_ago: int = 7) -> tuple:
end = datetime.utcnow()
start = end - timedelta(days=days_ago)
return start, end
def fetch_weekly_stats(conn: sqlite3.Connection, days: int = 7) -> dict:
"""Aggregate weekly statistics from Athena entries."""
start, end = _week_bounds(days)
# Total entries
cur = conn.execute("SELECT COUNT(*) as c FROM entries")
total = cur.fetchone()["c"]
# New this week
cur = conn.execute(
"SELECT COUNT(*) as c FROM entries WHERE first_seen >= ?",
(start.isoformat(),),
)
new_this_week = cur.fetchone()["c"]
# Verdict breakdown
cur = conn.execute(
"SELECT verdict, COUNT(*) as c FROM entries "
"WHERE first_seen >= ? GROUP BY verdict",
(start.isoformat(),),
)
verdicts = {row["verdict"]: row["c"] for row in cur}
# Average signal score
cur = conn.execute(
"SELECT AVG(signal_score) as avg_score FROM entries WHERE first_seen >= ?",
(start.isoformat(),),
)
avg_score = cur.fetchone()["avg_score"] or 0
# Top sources
cur = conn.execute(
"SELECT source, COUNT(*) as c FROM entries "
"WHERE first_seen >= ? GROUP BY source ORDER BY c DESC LIMIT 10",
(start.isoformat(),),
)
top_sources = [(row["source"], row["c"]) for row in cur]
# Top categories
cur = conn.execute(
"SELECT category_tags, COUNT(*) as c FROM entries "
"WHERE first_seen >= ? AND category_tags IS NOT NULL AND category_tags != '' "
"GROUP BY category_tags ORDER BY c DESC LIMIT 10",
(start.isoformat(),),
)
top_categories = [(row["category_tags"], row["c"]) for row in cur]
# Tier breakdown
cur = conn.execute(
"SELECT source_tier, COUNT(*) as c FROM entries "
"WHERE first_seen >= ? GROUP BY source_tier ORDER BY source_tier",
(start.isoformat(),),
)
tier_breakdown = {
{1: "Tier 1 (High)", 2: "Tier 2 (Mid)", 3: "Tier 3 (Low)"}
.get(row["source_tier"], f"Tier {row['source_tier']}"): row["c"]
for row in cur
}
return {
"week_start": start.strftime("%Y-%m-%d"),
"week_end": end.strftime("%Y-%m-%d"),
"total_entries": total,
"new_this_week": new_this_week,
"verdicts": verdicts,
"publish_count": verdicts.get("PUBLISH", 0),
"watch_count": verdicts.get("WATCH", 0),
"avg_score": avg_score,
"top_sources": top_sources,
"top_categories": top_categories,
"tier_breakdown": tier_breakdown,
}
def _trending_themes(conn: sqlite3.Connection, days: int = 7) -> list:
"""Identify themes that appear more frequently this week vs last week."""
start, end = _week_bounds(days)
prev_start = start - timedelta(days=days)
# This week's categories
cur = conn.execute(
"SELECT category_tags, COUNT(*) as c FROM entries "
"WHERE first_seen >= ? AND first_seen < ? AND category_tags IS NOT NULL "
"GROUP BY category_tags",
(start.isoformat(), end.isoformat()),
)
this_week = {row["category_tags"]: row["c"] for row in cur}
# Last week's categories
cur = conn.execute(
"SELECT category_tags, COUNT(*) as c FROM entries "
"WHERE first_seen >= ? AND first_seen < ? AND category_tags IS NOT NULL "
"GROUP BY category_tags",
(prev_start.isoformat(), start.isoformat()),
)
last_week = {row["category_tags"]: row["c"] for row in cur}
# Find trending (increased by 50%+ or new)
trending = []
for cat, count in this_week.items():
prev_count = last_week.get(cat, 0)
if prev_count == 0 and count >= 2:
trending.append((cat, count, "NEW"))
elif prev_count > 0 and count >= prev_count * 1.5:
trending.append((cat, count, f"+{int((count - prev_count) / prev_count * 100)}%"))
trending.sort(key=lambda x: x[1], reverse=True)
return trending
def _generate_recommendations(stats: dict, trending: list) -> list:
"""Generate recommendations based on weekly patterns."""
recs = []
# Source diversity check
if stats["top_sources"]:
top_source_pct = stats["top_sources"][0][1] / max(stats["new_this_week"], 1) * 100
if top_source_pct > 40:
recs.append(
f"⚠ High concentration from {stats['top_sources'][0][0]} "
f"({top_source_pct:.0f}% of entries). Consider diversifying sources."
)
# PUBLISH rate
if stats["publish_count"] == 0 and stats["new_this_week"] > 10:
recs.append(
"🔍 Zero PUBLISH verdicts despite "
f"{stats['new_this_week']} new entries. "
"Signal scoring may need recalibration."
)
# Trending themes
if trending:
recs.append(
f"📈 Trending themes: {', '.join(t[0] for t in trending[:3])}. "
"Consider deeper coverage."
)
# Score distribution
if stats["avg_score"] < 3.0 and stats["new_this_week"] > 0:
recs.append(
f"📉 Average signal score ({stats['avg_score']:.2f}) is low. "
"Check adapter signal_score assignments."
)
if not recs:
recs.append("✅ System operating within normal parameters.")
return recs
def generate_weekly(
conn: sqlite3.Connection,
days: int = 7,
output_path: str | None = None,
) -> dict:
"""Generate a complete weekly review."""
db_path = conn.execute("SELECT file FROM pragma_database_list LIMIT 1").fetchone()[0] or "oracle.db"
stats = fetch_weekly_stats(conn, days)
trending = _trending_themes(conn, days)
recommendations = _generate_recommendations(stats, trending)
# System health from metrics layer
health_data = get_adapter_health(db_path, days=days)
alerts = get_metric_alerts(db_path)
health_lines = []
if health_data:
for h in health_data:
status = ""
if h.get("max_consecutive_failures", 0) and h["max_consecutive_failures"] >= 3:
status = "🔴"
elif h.get("total_errors", 0) and h["runs"] and h["total_errors"] / h["runs"] > 0.5:
status = "🟡"
health_lines.append(
f"- {status} **{h['adapter_name']}**: {h['total_fetched']} fetched, "
f"{h['total_errors']} errors over {h['runs']} runs "
f"(avg {h['avg_runtime_ms']:.0f}ms)"
)
else:
health_lines.append("_No adapter health data yet (metrics tracking started recently)._")
if alerts:
health_lines.extend(f"- {a}" for a in alerts)
health_md = "\n".join(health_lines)
# Format sections
top_sources_md = "\n".join(
f"- **{s}**: {c} entries" for s, c in stats["top_sources"]
) or "_No sources._"
top_cats_md = "\n".join(
f"- **{c}**: {n} entries" for c, n in stats["top_categories"]
) or "_No categories._"
tier_md = "\n".join(
f"- **{label}**: {count}" for label, count in stats["tier_breakdown"].items()
) or "_No data._"
trending_md = "\n".join(
f"- **{t[0]}**: {t[1]} entries ({t[2]})" for t in trending
) or "_No trending themes detected._"
# Week-over-week
prev_count = stats["total_entries"] - stats["new_this_week"]
if prev_count > 0:
change = (stats["new_this_week"] - prev_count) / prev_count * 100
wow = (
f"New this week: {stats['new_this_week']} vs previous: {prev_count} "
f"({change:+.0f}%)"
)
else:
wow = f"First full week of data: {stats['new_this_week']} entries"
recs_md = "\n".join(f"- {r}" for r in recommendations)
review = {
**stats,
"trending": trending,
"recommendations": recommendations,
"health": health_data,
"alerts": alerts,
}
review["markdown"] = WEEKLY_TEMPLATE.format(
week_start=stats["week_start"],
week_end=stats["week_end"],
total_entries=stats["total_entries"],
new_this_week=stats["new_this_week"],
publish_count=stats["publish_count"],
watch_count=stats["watch_count"],
avg_score=stats["avg_score"],
sources_md=top_sources_md,
categories_md=top_cats_md,
tier_md=tier_md,
trending_md=trending_md,
wow_md=wow,
health_md=health_md,
recs_md=recs_md,
)
if output_path:
out = Path(output_path)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(review["markdown"], encoding="utf-8")
review["saved"] = str(out)
return review
# ── CLI ──
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Generate Athena weekly review")
parser.add_argument("--db", default="oracle.db")
parser.add_argument("--days", type=int, default=7)
parser.add_argument("--output", "-o", help="Output path for markdown review")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
conn = get_connection(args.db)
try:
review = generate_weekly(conn, days=args.days)
if args.json:
import json
print(json.dumps({k: v for k, v in review.items() if k != "markdown"}, indent=2, default=str))
elif args.output:
generate_weekly(conn, days=args.days, output_path=args.output)
print(f"Saved: {args.output}")
else:
print(review.get("markdown", "No review generated"))
finally:
conn.close()