From 41f6f960770b68737d2cced1ae5c8bff687ccbbe Mon Sep 17 00:00:00 2001 From: Jack Levy Date: Sat, 14 Mar 2026 19:04:22 -0400 Subject: [PATCH] fix: trending section blank when scores are stale + trend scorer error isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dashboard _get_trending() was querying scores within 1 day only — if the nightly trend task hadn't run (e.g. worker restarted mid-run), the trending section returned empty. Now falls back through 1→3→7→30 day windows so stale scores always surface something. Trend scorer now wraps per-bill scoring in try/except so a single bad newsapi/gnews call can't abort the entire 1600-bill run. Authored by: Jack Levy --- backend/app/api/dashboard.py | 24 +++++++++++--------- backend/app/workers/trend_scorer.py | 35 ++++++++++++++++------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/backend/app/api/dashboard.py b/backend/app/api/dashboard.py index 35cb9ff..00c8c99 100644 --- a/backend/app/api/dashboard.py +++ b/backend/app/api/dashboard.py @@ -16,16 +16,20 @@ router = APIRouter() async def _get_trending(db: AsyncSession) -> list[dict]: - trending_result = await db.execute( - select(Bill) - .options(selectinload(Bill.sponsor), selectinload(Bill.briefs), selectinload(Bill.trend_scores)) - .join(TrendScore, Bill.bill_id == TrendScore.bill_id) - .where(TrendScore.score_date >= date.today() - timedelta(days=1)) - .order_by(desc(TrendScore.composite_score)) - .limit(10) - ) - trending_bills = trending_result.scalars().unique().all() - return [_serialize_bill(b) for b in trending_bills] + # Try progressively wider windows so stale scores still surface results + for days_back in (1, 3, 7, 30): + trending_result = await db.execute( + select(Bill) + .options(selectinload(Bill.sponsor), selectinload(Bill.briefs), selectinload(Bill.trend_scores)) + .join(TrendScore, Bill.bill_id == TrendScore.bill_id) + .where(TrendScore.score_date >= date.today() - timedelta(days=days_back)) + .order_by(desc(TrendScore.composite_score)) + .limit(10) + ) + trending_bills = trending_result.scalars().unique().all() + if trending_bills: + return [_serialize_bill(b) for b in trending_bills] + return [] def _serialize_bill(bill: Bill) -> dict: diff --git a/backend/app/workers/trend_scorer.py b/backend/app/workers/trend_scorer.py index 0abfdc4..a005718 100644 --- a/backend/app/workers/trend_scorer.py +++ b/backend/app/workers/trend_scorer.py @@ -91,24 +91,27 @@ def calculate_all_trend_scores(self): gtrends_scores = trends_service.get_trends_scores_batch(keyword_groups) for i, bill in enumerate(batch): - query = bill_queries[i] - # NewsAPI + Google News counts (gnews served from 2-hour cache) - newsapi_articles = news_service.fetch_newsapi_articles(query, days=30) - newsapi_count = len(newsapi_articles) - gnews_count = news_service.fetch_gnews_count(query, days=30) - gtrends_score = gtrends_scores[i] + try: + query = bill_queries[i] + # NewsAPI + Google News counts (gnews served from 2-hour cache) + newsapi_articles = news_service.fetch_newsapi_articles(query, days=30) + newsapi_count = len(newsapi_articles) + gnews_count = news_service.fetch_gnews_count(query, days=30) + gtrends_score = gtrends_scores[i] - composite = calculate_composite_score(newsapi_count, gnews_count, gtrends_score) + composite = calculate_composite_score(newsapi_count, gnews_count, gtrends_score) - db.add(TrendScore( - bill_id=bill.bill_id, - score_date=today, - newsapi_count=newsapi_count, - gnews_count=gnews_count, - gtrends_score=gtrends_score, - composite_score=composite, - )) - scored += 1 + db.add(TrendScore( + bill_id=bill.bill_id, + score_date=today, + newsapi_count=newsapi_count, + gnews_count=gnews_count, + gtrends_score=gtrends_score, + composite_score=composite, + )) + scored += 1 + except Exception as exc: + logger.warning(f"Trend scoring skipped for {bill.bill_id}: {exc}") db.commit()