scripts/report.py
scripts/report.pyBrowse 56 files
1,810 tokens
7,189 bytes
Token encoding: o200k_base
Snapshot 24fd22b
← Back to SKILL.md
1"""Status dashboards, Markdown reports, human-task digest, and Google Sheets row export."""2from __future__ import annotations3 4import brokers as brokers_mod5import ledger as ledger_mod6 7STATE_LABELS = {8 "new": "Not started",9 "searching": "Searching",10 "not_found": "Not found",11 "found": "Found (action needed)",12 "indirect_exposure": "Indirect exposure (PII on a relative's record)",13 "action_selected": "Action selected",14 "submitted": "Submitted",15 "verification_pending": "Awaiting verification",16 "awaiting_processing": "Processing",17 "confirmed_removed": "Removed",18 "reappeared": "Reappeared",19 "human_task_queued": "Human task",20 "blocked": "Blocked",21}22 23 24def status_counts(subject_id: str) -> dict:25 counts: dict[str, int] = {}26 for case in ledger_mod.load(subject_id).values():27 state = case.get("state", "new")28 counts[state] = counts.get(state, 0) + 129 return counts30 31 32def metrics(subject_id: str) -> dict:33 """Outcome metrics: what's actually confirmed vs merely claimed, and what's overdue.34 35 removal_rate is confirmed_removed over cases we actually acted on (found/submitted/... ),36 NOT over the whole broker DB, so it reflects real progress on real exposure. `in_flight`37 is 'claimed' (submitted/verifying/processing) but not yet re-scan-confirmed. `overdue`38 counts cases whose recheck window has already passed (the cron backlog).39 """40 c = status_counts(subject_id)41 removed = c.get("confirmed_removed", 0)42 in_flight = c.get("submitted", 0) + c.get("verification_pending", 0) + c.get("awaiting_processing", 0)43 open_found = c.get("found", 0) + c.get("reappeared", 0) + c.get("action_selected", 0) \44 + c.get("indirect_exposure", 0)45 acted = removed + in_flight + open_found + c.get("human_task_queued", 0) + c.get("blocked", 0)46 return {47 "confirmed_removed": removed,48 "in_flight_claimed": in_flight, # submitted but NOT yet verified gone49 "open_needs_action": open_found,50 "blocked": c.get("blocked", 0),51 "human_tasks": c.get("human_task_queued", 0),52 "acted_total": acted,53 "removal_rate": round(removed / acted, 3) if acted else 0.0,54 "overdue_rechecks": len(ledger_mod.due(subject_id)),55 }56 57 58def render_markdown(subject_id: str) -> str:59 ledger = ledger_mod.load(subject_id)60 counts = status_counts(subject_id)61 total = sum(counts.values())62 removed = counts.get("confirmed_removed", 0)63 64 m = metrics(subject_id)65 lines = [66 f"# unbroker - status for `{subject_id}`",67 "",68 f"**{removed} / {total} confirmed removed** · removal rate (of acted-on cases): "69 f"{int(m['removal_rate'] * 100)}%",70 "",71 f"- Confirmed removed: {m['confirmed_removed']}",72 f"- In flight (submitted, not yet re-scan-confirmed): {m['in_flight_claimed']}",73 f"- Open / needs action: {m['open_needs_action']}",74 f"- Blocked (anti-bot): {m['blocked']} · Human tasks: {m['human_tasks']}",75 f"- Overdue rechecks (cron backlog): {m['overdue_rechecks']}",76 "",77 "| State | Count |",78 "|---|---|",79 ]80 for state in ledger_mod.STATES:81 if counts.get(state):82 lines.append(f"| {STATE_LABELS.get(state, state)} | {counts[state]} |")83 84 tasks = [c for c in ledger.values() if c.get("state") == "human_task_queued"]85 if tasks:86 lines += ["", "## Outstanding human tasks"]87 for c in tasks:88 reason = c.get("human_task_reason", "manual step required")89 lines.append(f"- **{c.get('broker_id')}** - {reason}")90 91 indirect = [c for c in ledger.values() if c.get("state") == "indirect_exposure"]92 if indirect:93 lines += ["", "## Indirect exposure (your PII on third-party records)",94 "Not removable via the broker's self-service opt-out (the record is about someone "95 "else). Lever: a targeted CCPA/GDPR delete-my-PII request naming only your own "96 "identifiers."]97 for c in indirect:98 ev = c.get("evidence") or {}99 note = ev.get("summary") or "subject's identifiers appear on another person's listing"100 lines.append(f"- **{c.get('broker_id')}** - {note}")101 return "\n".join(lines) + "\n"102 103 104def human_tasks_markdown(subject_id: str) -> str:105 """ONE consolidated digest of everything that genuinely needs a human.106 107 The autonomous run accumulates human-only work silently (never interrupting);108 this digest is presented once, at the end, so the operator clears it in a109 single sitting. Includes queued tasks and blocked-site operator-browser checks.110 """111 ledger = ledger_mod.load(subject_id)112 tasks = [(bid, c) for bid, c in sorted(ledger.items()) if c.get("state") == "human_task_queued"]113 blocked = [(bid, c) for bid, c in sorted(ledger.items()) if c.get("state") == "blocked"]114 115 lines = [f"# Human tasks for `{subject_id}`", ""]116 if not tasks and not blocked:117 lines.append("Nothing needs a human right now.")118 return "\n".join(lines) + "\n"119 120 lines.append(f"{len(tasks)} manual step(s) + {len(blocked)} blocked site(s). "121 "Everything else ran (or will run) autonomously.")122 if tasks:123 lines += ["", "## Manual steps"]124 for bid, c in tasks:125 b = brokers_mod.get(bid) or {}126 opt = b.get("optout") or {}127 lines.append(f"### {b.get('name', bid)}")128 lines.append(f"- Why: {c.get('human_task_reason', 'manual step required')}")129 where = opt.get("url") or opt.get("email") or "(see broker record)"130 lines.append(f"- Where: {where}")131 for q in (opt.get("quirks") or [])[:2]:132 lines.append(f"- Note: {q}")133 lines.append("- Withhold: SSN and full ID numbers - always.")134 lines.append(f"- When done, tell the agent so it records the outcome for `{bid}`.")135 if blocked:136 lines += ["", "## Blocked sites (open in YOUR browser - it gets through where bots don't)"]137 for bid, c in blocked:138 b = brokers_mod.get(bid) or {}139 url = ((b.get("search") or {}).get("url")) or "(see broker record)"140 lines.append(f"- **{b.get('name', bid)}** - open {url}, search the subject, and report "141 "the verdict (or a screenshot) back to the agent.")142 return "\n".join(lines) + "\n"143 144 145def sheets_rows(subject_id: str) -> list[list[str]]:146 """Header + one row per case for the optional Google Sheets tracker.147 148 The agent appends these via the `google-workspace` skill, e.g.:149 google_api.py sheets append <SHEET_ID> "Sheet1!A:F" --values <json-rows>150 """151 rows = [["broker_id", "state", "found", "tier", "removed_at", "next_recheck"]]152 for bid, c in sorted(ledger_mod.load(subject_id).items()):153 rows.append([154 bid,155 c.get("state", ""),156 str(c.get("found", "")),157 (c.get("automation") or {}).get("tier_used", ""),158 c.get("removal_confirmed_at") or "",159 c.get("next_recheck_at") or "",160 ])161 return rows162