fix: blank company charts + competitor GUIDs instead of tickers

Trend charts blank:
- trend_windows uses upsert (1 row per ticker/window), so charts had
  at most 1 data point. Added trend_history table (migration 024) that
  appends every snapshot. New /api/trends/history endpoint serves the
  time series. Frontend now uses useTrendHistory for charts and
  useTrends for the latest summary card.

Competitor GUIDs:
- list_competitors query returned raw company_b_id UUIDs without
  joining companies table. Added LEFT JOIN with CASE to resolve the
  other company's ticker and legal_name. Updated Pydantic model to
  include enriched fields. Frontend fallback changed from truncated
  UUID to ticker/legal_name/Unknown.
This commit is contained in:
Celes Renata
2026-04-17 00:42:55 +00:00
parent f2d8744a4f
commit 7c589353f8
6 changed files with 169 additions and 16 deletions
+50
View File
@@ -415,6 +415,56 @@ async def list_trends(
return results
@app.get("/api/trends/history")
async def list_trend_history(
ticker: Optional[str] = None,
window: Optional[str] = None,
limit: int = Query(default=200, le=1000),
):
"""Return historical trend snapshots for charting.
Unlike /api/trends which returns the latest snapshot per entity/window,
this endpoint returns the time series from the trend_history table.
"""
conditions: list[str] = []
params: list[Any] = []
idx = 1
if ticker:
conditions.append(f"entity_id = ${idx}")
params.append(ticker.upper())
idx += 1
if window:
conditions.append(f"\"window\" = ${idx}")
params.append(window)
idx += 1
where = ("WHERE " + " AND ".join(conditions)) if conditions else ""
try:
rows = await pool.fetch(
f"""SELECT id, entity_type, entity_id, "window", trend_direction,
trend_strength, confidence, contradiction_score,
dominant_catalysts, material_risks, generated_at
FROM trend_history
{where}
ORDER BY generated_at ASC
LIMIT ${idx}""",
*params, limit,
)
except Exception:
# Table may not exist yet (pre-migration 024)
return []
results = []
for r in rows:
d = _row_to_dict(r)
d["dominant_catalysts"] = _parse_jsonb(d.get("dominant_catalysts"))
d["material_risks"] = _parse_jsonb(d.get("material_risks"))
results.append(d)
return results
@app.get("/api/trends/{trend_id}")
async def get_trend(trend_id: str):
"""Get a single trend summary by ID."""