Last active
March 27, 2026 18:19
-
-
Save cemoody/7dd4b6b2aada090637b9f6af7be238eb to your computer and use it in GitHub Desktop.
Signal Collector Health Dashboard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Signal Collector Health</title> | |
| <link href="https://fonts.googleapis.com/css2?family=STIX+Two+Text:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Source+Code+Pro:wght@400;500&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --green: #3a7d44; | |
| --green-bg: rgba(58,125,68,0.06); | |
| --red: #8b3a3a; | |
| --red-bg: rgba(139,58,58,0.06); | |
| --amber: #8a7030; | |
| --amber-bg: rgba(138,112,48,0.06); | |
| --text: #1a1a1a; | |
| --text2: #555; | |
| --text3: #999; | |
| --rule: #d4d4d4; | |
| --rule-light: #ebebeb; | |
| --bg: #fafaf8; | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'STIX Two Text', Georgia, serif; | |
| font-size: 11.5px; | |
| line-height: 1.45; | |
| color: var(--text); | |
| background: var(--bg); | |
| padding: 1.8rem 2.2rem; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .mono { font-family: 'Source Code Pro', monospace; font-size: 0.92em; } | |
| /* Header */ | |
| header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: baseline; | |
| border-bottom: 1.5px solid var(--text); | |
| padding-bottom: 0.5rem; | |
| margin-bottom: 0.8rem; | |
| } | |
| header h1 { font-size: 1.35rem; font-weight: 600; letter-spacing: -0.01em; } | |
| header .meta { font-size: 0.85em; color: var(--text3); font-style: italic; } | |
| /* Summary strip */ | |
| .summary { | |
| display: flex; | |
| gap: 1.8rem; | |
| margin-bottom: 1rem; | |
| padding: 0.45rem 0; | |
| border-bottom: 0.75px solid var(--rule); | |
| font-size: 0.92em; | |
| } | |
| .summary .s { color: var(--text2); } | |
| .summary .v { font-weight: 600; margin-left: 0.25rem; font-variant-numeric: tabular-nums; } | |
| .summary .v.g { color: var(--green); } | |
| .summary .v.r { color: var(--red); } | |
| .summary .v.a { color: var(--amber); } | |
| /* Source group */ | |
| .group { | |
| margin-bottom: 0.7rem; | |
| } | |
| .group-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.6rem; | |
| padding: 0.3rem 0; | |
| border-bottom: 0.75px solid var(--rule); | |
| margin-bottom: 1px; | |
| } | |
| .group-name { | |
| font-weight: 700; | |
| font-size: 0.95em; | |
| } | |
| .group-count { | |
| font-size: 0.8em; | |
| color: var(--text3); | |
| } | |
| .group-status-summary { | |
| margin-left: auto; | |
| display: flex; | |
| gap: 0.5rem; | |
| font-size: 0.8em; | |
| } | |
| .group-status-summary .gs { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.2rem; | |
| } | |
| /* Table */ | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| th { | |
| font-size: 0.7em; | |
| font-weight: 500; | |
| text-transform: uppercase; | |
| letter-spacing: 0.07em; | |
| color: var(--text3); | |
| text-align: right; | |
| padding: 0.15rem 0.5rem 0.15rem 0; | |
| border-bottom: 0.5px solid var(--rule-light); | |
| } | |
| th:first-child, th:nth-child(2) { text-align: left; } | |
| td { | |
| padding: 0.25rem 0.5rem 0.25rem 0; | |
| text-align: right; | |
| border-bottom: 0.5px solid var(--rule-light); | |
| font-size: 0.92em; | |
| } | |
| td:first-child { text-align: center; width: 1.2rem; padding-right: 0.3rem; } | |
| td:nth-child(2) { text-align: left; font-weight: 500; } | |
| tr:last-child td { border-bottom: none; } | |
| tr.healthy td:first-child .dot { background: var(--green); } | |
| tr.stale td:first-child .dot { background: var(--amber); } | |
| tr.down td:first-child .dot { background: var(--red); } | |
| tr.down { background: var(--red-bg); } | |
| tr.stale { background: var(--amber-bg); } | |
| .dot { | |
| display: inline-block; | |
| width: 7px; | |
| height: 7px; | |
| border-radius: 50%; | |
| } | |
| /* Inline uptime bar */ | |
| .uptime-cell { | |
| display: flex; | |
| align-items: center; | |
| justify-content: flex-end; | |
| gap: 0.35rem; | |
| } | |
| .uptime-num { | |
| min-width: 2.5em; | |
| text-align: right; | |
| font-size: 0.9em; | |
| } | |
| .ubar { | |
| width: 40px; | |
| height: 4px; | |
| background: rgba(0,0,0,0.06); | |
| border-radius: 2px; | |
| overflow: hidden; | |
| flex-shrink: 0; | |
| } | |
| .ubar .fill { height: 100%; border-radius: 2px; } | |
| tr.healthy .ubar .fill { background: var(--green); } | |
| tr.stale .ubar .fill { background: var(--amber); } | |
| tr.down .ubar .fill { background: var(--red); } | |
| .dim { color: var(--text3); } | |
| /* Footer */ | |
| footer { | |
| margin-top: 0.8rem; | |
| padding-top: 0.5rem; | |
| border-top: 0.75px solid var(--rule); | |
| font-size: 0.78em; | |
| color: var(--text3); | |
| display: flex; | |
| gap: 1.5rem; | |
| flex-wrap: wrap; | |
| } | |
| footer .note { max-width: 45em; } | |
| footer .note b { font-weight: 600; color: var(--text2); } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>Signal Collector Health</h1> | |
| <div class="meta" id="ts"></div> | |
| </header> | |
| <div class="summary" id="summary"></div> | |
| <div id="groups"></div> | |
| <footer> | |
| <div class="note"><b>Status.</b> Thresholds per grain: daily ≤48h / ≤7d; weekly ≤14d / ≤30d; monthly ≤60d / ≤120d; quarterly ≤120d / ≤180d.</div> | |
| <div class="note"><b>Uptime.</b> Fraction of last 30 days with at least one insert. >100% = multiple inserts/day.</div> | |
| </footer> | |
| <script> | |
| const DATA = [ | |
| {"signal_id":5,"signal_name":"Shovels Building Permits","source_system":"Shovels AI","date_grain":"daily","total_rows":31562506,"last_observation":"2025-12-31 00:00:00+00:00","last_inserted_at":"2026-02-06 05:06:11.433276+00:00","hours_since_last_insert":1188,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":16,"signal_name":"Google Trends Search Interest","source_system":"google_trends","date_grain":"weekly","total_rows":26014,"last_observation":"2026-02-08 00:00:00+00:00","last_inserted_at":"2026-02-14 23:21:50.693703+00:00","hours_since_last_insert":978,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":57,"signal_name":"GTrends Gutters/Drainage","source_system":"Google","date_grain":"weekly","total_rows":73340,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 15:57:37.565130+00:00","hours_since_last_insert":890,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":56,"signal_name":"GTrends Fire/Smoke","source_system":"Google","date_grain":"weekly","total_rows":4215,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 15:54:54.406346+00:00","hours_since_last_insert":890,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":59,"signal_name":"GTrends Financing","source_system":"Google","date_grain":"weekly","total_rows":47050,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 15:56:56.895504+00:00","hours_since_last_insert":890,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":61,"signal_name":"GTrends Solar Bundling","source_system":"Google","date_grain":"weekly","total_rows":4126,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 15:49:18.267040+00:00","hours_since_last_insert":890,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":63,"signal_name":"GTrends Near Me Intent","source_system":"Google","date_grain":"weekly","total_rows":32964,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 15:51:33.162959+00:00","hours_since_last_insert":890,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":60,"signal_name":"GTrends Permits/Codes","source_system":"Google","date_grain":"weekly","total_rows":21236,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 15:55:15.220877+00:00","hours_since_last_insert":890,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":62,"signal_name":"GTrends Storm Events","source_system":"Google","date_grain":"weekly","total_rows":251179,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:11:58.820261+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":54,"signal_name":"GTrends Wind/Tree Damage","source_system":"Google","date_grain":"weekly","total_rows":39047,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:10:38.335421+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":51,"signal_name":"GTrends Insurance/Claims","source_system":"Google","date_grain":"weekly","total_rows":20339,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:11:36.662938+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":52,"signal_name":"GTrends Replacement Intent","source_system":"Google","date_grain":"weekly","total_rows":158170,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:10:36.634057+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":55,"signal_name":"GTrends Ice/Winter","source_system":"Google","date_grain":"weekly","total_rows":29737,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:08:05.320276+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":58,"signal_name":"GTrends Mold/Moisture","source_system":"Google","date_grain":"weekly","total_rows":45906,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:03:16.250101+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":53,"signal_name":"GTrends Roofing Materials","source_system":"Google","date_grain":"weekly","total_rows":175807,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:23:22.013000+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":50,"signal_name":"GTrends Leak/Intrusion","source_system":"Google","date_grain":"weekly","total_rows":73826,"last_observation":"2026-02-16 00:00:00+00:00","last_inserted_at":"2026-02-18 16:10:06.742113+00:00","hours_since_last_insert":889,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":70,"signal_name":"GDELT News Events","source_system":"GDELT","date_grain":"daily","total_rows":1342722,"last_observation":"2026-02-18 00:00:00+00:00","last_inserted_at":"2026-02-19 03:41:53.194987+00:00","hours_since_last_insert":878,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"down","uptime_pct_30d":0.0}, | |
| {"signal_id":111,"signal_name":"Lowes Original Price","source_system":"Lowes","date_grain":"daily","total_rows":12,"last_observation":"2026-03-05 06:00:00+00:00","last_inserted_at":"2026-03-05 06:26:29.002459+00:00","hours_since_last_insert":539,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":12,"active_days_last_30":2,"status":"down","uptime_pct_30d":6.7}, | |
| {"signal_id":113,"signal_name":"Lowes Quantity","source_system":"Lowes","date_grain":"daily","total_rows":2566,"last_observation":"2026-03-09 21:00:00+00:00","last_inserted_at":"2026-03-09 21:32:40.525591+00:00","hours_since_last_insert":428,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":2566,"active_days_last_30":3,"status":"down","uptime_pct_30d":10.0}, | |
| {"signal_id":112,"signal_name":"Lowes In Stock","source_system":"Lowes","date_grain":"daily","total_rows":2566,"last_observation":"2026-03-09 21:00:00+00:00","last_inserted_at":"2026-03-09 21:32:40.525591+00:00","hours_since_last_insert":428,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":2566,"active_days_last_30":3,"status":"down","uptime_pct_30d":10.0}, | |
| {"signal_id":100,"signal_name":"GDELT News Signals","source_system":"GDELT","date_grain":"daily","total_rows":3888332,"last_observation":"2026-03-14 00:00:00+00:00","last_inserted_at":"2026-03-16 04:38:17.217681+00:00","hours_since_last_insert":277,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":3397,"active_days_last_30":5,"status":"down","uptime_pct_30d":16.7}, | |
| {"signal_id":22,"signal_name":"Flood Claims","source_system":"FEMA NFIP","date_grain":"monthly","total_rows":58320,"last_observation":"2026-02-01 00:00:00+00:00","last_inserted_at":"2026-02-06 02:22:22.099327+00:00","hours_since_last_insert":1191,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"healthy","uptime_pct_30d":0.0}, | |
| {"signal_id":39,"signal_name":"BLS Roofing Employment","source_system":"BLS QCEW","date_grain":"monthly","total_rows":31008,"last_observation":"2026-02-01 00:00:00+00:00","last_inserted_at":"2026-02-08 01:48:29.880993+00:00","hours_since_last_insert":1144,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":0,"active_days_last_30":0,"status":"healthy","uptime_pct_30d":0.0}, | |
| {"signal_id":110,"signal_name":"BuildZoom Roofing Permits","source_system":"BuildZoom","date_grain":"monthly","total_rows":462628,"last_observation":"2026-03-09 21:00:00+00:00","last_inserted_at":"2026-03-10 18:09:16.500752+00:00","hours_since_last_insert":407,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":462628,"active_days_last_30":6,"status":"healthy","uptime_pct_30d":20.0}, | |
| {"signal_id":26,"signal_name":"RMA Liability","source_system":"USDA RMA","date_grain":"monthly","total_rows":987317,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:07:07.855135+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":785,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":35,"signal_name":"Lumber Futures","source_system":"FRED","date_grain":"monthly","total_rows":1953,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:06:11.089190+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":3,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":36,"signal_name":"Mortgage Rates","source_system":"FRED","date_grain":"weekly","total_rows":15869,"last_observation":"2026-03-12 00:00:00+00:00","last_inserted_at":"2026-03-15 01:05:24.859095+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":19,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":38,"signal_name":"Adzuna Adjuster Jobs","source_system":"Adzuna","date_grain":"weekly","total_rows":168,"last_observation":"2026-03-09 00:00:00+00:00","last_inserted_at":"2026-03-15 01:05:34.380415+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":80,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":25,"signal_name":"RMA Total Premium","source_system":"USDA RMA","date_grain":"monthly","total_rows":987317,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:07:07.855135+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":785,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":13,"signal_name":"FRED Material Prices","source_system":"FRED","date_grain":"monthly","total_rows":2602,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:05:23.235353+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":4,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":24,"signal_name":"RMA Policies Indemnified","source_system":"USDA RMA","date_grain":"monthly","total_rows":986186,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:07:07.855135+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":785,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":23,"signal_name":"RMA Loss Acres","source_system":"USDA RMA","date_grain":"monthly","total_rows":829777,"last_observation":"2026-02-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:07:07.855135+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":249,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":18,"signal_name":"RMA Indemnity Amount","source_system":"USDA RMA","date_grain":"monthly","total_rows":1032385,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-15 01:07:07.855135+00:00","hours_since_last_insert":304,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":785,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":73,"signal_name":"Census ACS Vacancy Status","source_system":"Census ACS","date_grain":"quarterly","total_rows":3273249,"last_observation":"2024-01-01 00:00:00+00:00","last_inserted_at":"2026-03-16 04:58:06.242727+00:00","hours_since_last_insert":277,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":472808,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":72,"signal_name":"Census ACS Occupancy","source_system":"Census ACS","date_grain":"quarterly","total_rows":935214,"last_observation":"2024-01-01 00:00:00+00:00","last_inserted_at":"2026-03-16 04:58:06.242727+00:00","hours_since_last_insert":277,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":135088,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":71,"signal_name":"Census ACS Housing Units","source_system":"Census ACS","date_grain":"quarterly","total_rows":467607,"last_observation":"2024-01-01 00:00:00+00:00","last_inserted_at":"2026-03-16 04:58:06.242727+00:00","hours_since_last_insert":277,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":67544,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":37,"signal_name":"Redfin Home Sales","source_system":"Redfin","date_grain":"monthly","total_rows":1206214,"last_observation":"2026-01-01 00:00:00+00:00","last_inserted_at":"2026-03-16 04:40:54.466723+00:00","hours_since_last_insert":277,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":72252,"active_days_last_30":1,"status":"healthy","uptime_pct_30d":3.3}, | |
| {"signal_id":2,"signal_name":"Diesel Price","source_system":"FRED","date_grain":"weekly","total_rows":539,"last_observation":"2026-03-09 00:00:00+00:00","last_inserted_at":"2026-03-16 13:51:32.385497+00:00","hours_since_last_insert":268,"rows_last_24h":0,"rows_last_7d":0,"rows_last_30d":7,"active_days_last_30":3,"status":"healthy","uptime_pct_30d":10.0}, | |
| {"signal_id":80,"signal_name":"Census Building Permits","source_system":"Census BPS","date_grain":"monthly","total_rows":3428,"last_observation":"2026-03-01 00:00:00+00:00","last_inserted_at":"2026-03-22 21:00:35.447556+00:00","hours_since_last_insert":117,"rows_last_24h":0,"rows_last_7d":28,"rows_last_30d":3428,"active_days_last_30":4,"status":"healthy","uptime_pct_30d":13.3}, | |
| {"signal_id":20,"signal_name":"Drought Level","source_system":"NIDIS","date_grain":"weekly","total_rows":362520,"last_observation":"2026-03-17 00:00:00+00:00","last_inserted_at":"2026-03-24 22:00:56.738328+00:00","hours_since_last_insert":68,"rows_last_24h":0,"rows_last_7d":265,"rows_last_30d":1060,"active_days_last_30":4,"status":"healthy","uptime_pct_30d":13.3}, | |
| {"signal_id":14,"signal_name":"Weather Temperature","source_system":"Open-Meteo","date_grain":"daily","total_rows":903,"last_observation":"2026-03-26 00:00:00+00:00","last_inserted_at":"2026-03-27 07:00:28.541906+00:00","hours_since_last_insert":11,"rows_last_24h":21,"rows_last_7d":147,"rows_last_30d":903,"active_days_last_30":17,"status":"healthy","uptime_pct_30d":56.7}, | |
| {"signal_id":15,"signal_name":"Storm Events","source_system":"NOAA NCEI","date_grain":"daily","total_rows":172633,"last_observation":"2026-03-26 00:00:00+00:00","last_inserted_at":"2026-03-27 07:00:25.629512+00:00","hours_since_last_insert":11,"rows_last_24h":7,"rows_last_7d":49,"rows_last_30d":238,"active_days_last_30":21,"status":"healthy","uptime_pct_30d":70.0}, | |
| {"signal_id":42,"signal_name":"HD Online Availability","source_system":"Home Depot","date_grain":"daily","total_rows":1617458,"last_observation":"2026-03-26 00:00:00+00:00","last_inserted_at":"2026-03-27 09:00:18.341438+00:00","hours_since_last_insert":9,"rows_last_24h":175695,"rows_last_7d":1081243,"rows_last_30d":1122330,"active_days_last_30":11,"status":"healthy","uptime_pct_30d":36.7}, | |
| {"signal_id":40,"signal_name":"Home Depot Quantity","source_system":"Home Depot","date_grain":"daily","total_rows":729276450,"last_observation":"2026-03-27 18:00:00+00:00","last_inserted_at":"2026-03-27 18:02:43.587393+00:00","hours_since_last_insert":0,"rows_last_24h":16417992,"rows_last_7d":125094846,"rows_last_30d":722251194,"active_days_last_30":31,"status":"healthy","uptime_pct_30d":103.3}, | |
| {"signal_id":10,"signal_name":"Home Depot Price","source_system":"Home Depot","date_grain":"daily","total_rows":744297742,"last_observation":"2026-03-27 18:00:00+00:00","last_inserted_at":"2026-03-27 18:02:43.587393+00:00","hours_since_last_insert":0,"rows_last_24h":24147602,"rows_last_7d":183988542,"rows_last_30d":737551308,"active_days_last_30":31,"status":"healthy","uptime_pct_30d":103.3}, | |
| {"signal_id":41,"signal_name":"Home Depot Discontinued","source_system":"Home Depot","date_grain":"daily","total_rows":834347534,"last_observation":"2026-03-27 18:00:00+00:00","last_inserted_at":"2026-03-27 18:02:43.587393+00:00","hours_since_last_insert":0,"rows_last_24h":26963892,"rows_last_7d":205450908,"rows_last_30d":827322507,"active_days_last_30":31,"status":"healthy","uptime_pct_30d":103.3}, | |
| {"signal_id":12,"signal_name":"Home Depot In Stock","source_system":"Home Depot","date_grain":"daily","total_rows":834348037,"last_observation":"2026-03-27 18:00:00+00:00","last_inserted_at":"2026-03-27 18:02:43.587393+00:00","hours_since_last_insert":0,"rows_last_24h":26963896,"rows_last_7d":205450920,"rows_last_30d":827322717,"active_days_last_30":31,"status":"healthy","uptime_pct_30d":103.3}, | |
| {"signal_id":11,"signal_name":"Home Depot Original Price","source_system":"Home Depot","date_grain":"daily","total_rows":742678007,"last_observation":"2026-03-27 18:00:00+00:00","last_inserted_at":"2026-03-27 18:02:43.587393+00:00","hours_since_last_insert":0,"rows_last_24h":23971901,"rows_last_7d":182906541,"rows_last_30d":736427386,"active_days_last_30":31,"status":"healthy","uptime_pct_30d":103.3} | |
| ]; | |
| // Helpers | |
| const fmt = n => { | |
| if (n === 0) return '<span class="dim">--</span>'; | |
| if (n >= 1e9) return (n/1e9).toFixed(2) + 'B'; | |
| if (n >= 1e6) return (n/1e6).toFixed(1) + 'M'; | |
| if (n >= 1e3) return (n/1e3).toFixed(1) + 'K'; | |
| return n.toLocaleString(); | |
| }; | |
| const age = h => { | |
| if (h < 1) return 'now'; | |
| if (h < 48) return Math.round(h) + 'h'; | |
| const d = Math.round(h/24); | |
| return d < 60 ? d + 'd' : Math.round(d/30) + 'mo'; | |
| }; | |
| // Group by source (merge Google + google_trends) | |
| const SOURCE_MAP = { | |
| 'Google': 'Google Trends', 'google_trends': 'Google Trends', | |
| 'Home Depot': 'Home Depot', 'Lowes': "Lowe's", | |
| 'FRED': 'FRED', 'USDA RMA': 'USDA RMA', | |
| 'Census ACS': 'US Census', 'Census BPS': 'US Census', | |
| 'GDELT': 'GDELT', 'FEMA NFIP': 'FEMA', | |
| 'BLS QCEW': 'BLS', 'BuildZoom': 'BuildZoom', | |
| 'Shovels AI': 'Shovels AI', 'Open-Meteo': 'Open-Meteo', | |
| 'NOAA NCEI': 'NOAA', 'NIDIS': 'NIDIS', | |
| 'Redfin': 'Redfin', 'Adzuna': 'Adzuna', | |
| }; | |
| const groups = {}; | |
| DATA.forEach(d => { | |
| const g = SOURCE_MAP[d.source_system] || d.source_system; | |
| (groups[g] = groups[g] || []).push(d); | |
| }); | |
| // Sort groups: most-down first, then by total rows desc | |
| const groupOrder = Object.keys(groups).sort((a, b) => { | |
| const aDown = groups[a].filter(d => d.status === 'down').length; | |
| const bDown = groups[b].filter(d => d.status === 'down').length; | |
| if (aDown !== bDown) return bDown - aDown; | |
| const aRows = groups[a].reduce((s, d) => s + d.total_rows, 0); | |
| const bRows = groups[b].reduce((s, d) => s + d.total_rows, 0); | |
| return bRows - aRows; | |
| }); | |
| // Summary | |
| const nH = DATA.filter(d => d.status==='healthy').length; | |
| const nS = DATA.filter(d => d.status==='stale').length; | |
| const nD = DATA.filter(d => d.status==='down').length; | |
| const tot = DATA.reduce((a,d) => a + d.total_rows, 0); | |
| const r24 = DATA.reduce((a,d) => a + d.rows_last_24h, 0); | |
| document.getElementById('ts').textContent = new Date().toISOString().slice(0,19).replace('T',' ') + ' UTC'; | |
| document.getElementById('summary').innerHTML = ` | |
| <span class="s">Signals</span><span class="v">${DATA.length}</span> | |
| <span class="s">Healthy</span><span class="v g">${nH}</span> | |
| <span class="s">Down</span><span class="v r">${nD}</span> | |
| <span class="s">Total rows</span><span class="v">${fmt(tot)}</span> | |
| <span class="s">Last 24h</span><span class="v">${fmt(r24)}</span> | |
| <span class="s">Sources</span><span class="v">${groupOrder.length}</span> | |
| `; | |
| // Render | |
| const container = document.getElementById('groups'); | |
| groupOrder.forEach(gName => { | |
| const signals = groups[gName].sort((a,b) => a.hours_since_last_insert - b.hours_since_last_insert); | |
| const gH = signals.filter(d=>d.status==='healthy').length; | |
| const gD = signals.filter(d=>d.status==='down').length; | |
| const gS = signals.filter(d=>d.status==='stale').length; | |
| let statusHtml = ''; | |
| if (gH) statusHtml += `<span class="gs"><span class="dot" style="background:var(--green)"></span>${gH}</span>`; | |
| if (gS) statusHtml += `<span class="gs"><span class="dot" style="background:var(--amber)"></span>${gS}</span>`; | |
| if (gD) statusHtml += `<span class="gs"><span class="dot" style="background:var(--red)"></span>${gD}</span>`; | |
| const rows = signals.map(d => { | |
| const up = Math.min(d.uptime_pct_30d, 100); | |
| const upLabel = d.uptime_pct_30d > 100 ? '>100' : d.uptime_pct_30d.toFixed(0); | |
| return `<tr class="${d.status}"> | |
| <td><span class="dot"></span></td> | |
| <td>${d.signal_name}</td> | |
| <td class="mono">${d.date_grain}</td> | |
| <td>${fmt(d.total_rows)}</td> | |
| <td>${age(d.hours_since_last_insert)}</td> | |
| <td>${fmt(d.rows_last_24h)}</td> | |
| <td>${fmt(d.rows_last_7d)}</td> | |
| <td><div class="uptime-cell"><span class="uptime-num">${upLabel}%</span><div class="ubar"><div class="fill" style="width:${up}%"></div></div></div></td> | |
| </tr>`; | |
| }).join(''); | |
| container.innerHTML += ` | |
| <div class="group"> | |
| <div class="group-header"> | |
| <span class="group-name">${gName}</span> | |
| <span class="group-count">${signals.length} signal${signals.length>1?'s':''}</span> | |
| <div class="group-status-summary">${statusHtml}</div> | |
| </div> | |
| <table> | |
| <tr><th></th><th>Signal</th><th>Grain</th><th>Total</th><th>Last</th><th>24h</th><th>7d</th><th>30d uptime</th></tr> | |
| ${rows} | |
| </table> | |
| </div>`; | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment