Files

52 lines
3.7 KiB
JSON

{
"dashboard_title": "Source Coverage & Gaps",
"description": "Operational dashboard for identifying source coverage gaps, stale sources, and symbols missing expected data feeds.",
"slug": "source-coverage-gaps",
"position_json": {
"HEADER_ID": {"id": "HEADER_ID", "type": "HEADER", "meta": {"text": "Source Coverage & Gaps"}},
"ROW-1": {
"type": "ROW",
"children": ["CHART-coverage-matrix", "CHART-missing-types-table"]
},
"ROW-2": {
"type": "ROW",
"children": ["CHART-stale-sources-table", "CHART-failure-heatmap"]
}
},
"metadata": {
"refresh_frequency": 600,
"default_filters": "{}",
"color_scheme": "supersetColors"
},
"charts": [
{
"slice_name": "Source Coverage Matrix",
"viz_type": "table",
"description": "Per-symbol source type coverage showing active source counts",
"datasource_type": "query",
"query": "SELECT c.ticker, c.legal_name, c.sector, COUNT(s.id) FILTER (WHERE s.active) AS active_sources, COUNT(s.id) FILTER (WHERE s.source_type = 'market_api' AND s.active) AS market_sources, COUNT(s.id) FILTER (WHERE s.source_type = 'news_api' AND s.active) AS news_sources, COUNT(s.id) FILTER (WHERE s.source_type = 'filings_api' AND s.active) AS filings_sources, COUNT(s.id) FILTER (WHERE s.source_type = 'web_scrape' AND s.active) AS web_scrape_sources, COUNT(s.id) FILTER (WHERE s.source_type = 'broker' AND s.active) AS broker_sources FROM companies c LEFT JOIN sources s ON s.company_id = c.id WHERE c.active = TRUE GROUP BY c.ticker, c.legal_name, c.sector ORDER BY c.ticker"
},
{
"slice_name": "Symbols Missing Source Types",
"viz_type": "table",
"description": "Companies that lack one or more expected source types (market_api, news_api, filings_api)",
"datasource_type": "query",
"query": "SELECT c.ticker, c.legal_name, c.sector, ARRAY_AGG(DISTINCT s.source_type) FILTER (WHERE s.active) AS active_types FROM companies c LEFT JOIN sources s ON s.company_id = c.id AND s.active = TRUE WHERE c.active = TRUE GROUP BY c.ticker, c.legal_name, c.sector HAVING NOT ARRAY['market_api', 'news_api', 'filings_api'] <@ ARRAY_AGG(DISTINCT s.source_type) FILTER (WHERE s.active) OR ARRAY_AGG(DISTINCT s.source_type) FILTER (WHERE s.active) IS NULL ORDER BY c.ticker"
},
{
"slice_name": "Stale Sources (No Success in 24h)",
"viz_type": "table",
"description": "Active sources that have not completed a successful ingestion run in the last 24 hours",
"datasource_type": "query",
"query": "SELECT c.ticker, s.source_type, s.source_name, MAX(ir.started_at) FILTER (WHERE ir.status = 'completed') AS last_success, MAX(ir.started_at) AS last_attempt, COUNT(*) FILTER (WHERE ir.status = 'failed' AND ir.started_at >= NOW() - INTERVAL '24 hours') AS recent_failures FROM sources s JOIN companies c ON c.id = s.company_id LEFT JOIN ingestion_runs ir ON ir.source_id = s.id WHERE s.active = TRUE AND c.active = TRUE GROUP BY c.ticker, s.source_type, s.source_name HAVING MAX(ir.started_at) FILTER (WHERE ir.status = 'completed') < NOW() - INTERVAL '24 hours' OR MAX(ir.started_at) FILTER (WHERE ir.status = 'completed') IS NULL ORDER BY c.ticker, s.source_type"
},
{
"slice_name": "Source Failure Heatmap",
"viz_type": "heatmap",
"description": "Failure counts by source type and ticker in the last 24h",
"datasource_type": "query",
"query": "SELECT c.ticker, ir.source_type, COUNT(*) FILTER (WHERE ir.status = 'failed') AS failures FROM ingestion_runs ir JOIN companies c ON c.id = ir.company_id WHERE ir.started_at >= NOW() - INTERVAL '24 hours' GROUP BY c.ticker, ir.source_type HAVING COUNT(*) FILTER (WHERE ir.status = 'failed') > 0 ORDER BY failures DESC"
}
]
}