Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/api/v1/endpoints/claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def list_claims(
verified = session.exec(
select(func.count())
.select_from(Claim)
.where(col(Claim.fact_check_status).ilike("verified"))
.where(col(Claim.fact_check_status).ilike("verified%"))
).one()
disputed = session.exec(
select(func.count())
Expand Down
7 changes: 2 additions & 5 deletions app/api/v1/endpoints/overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,12 @@ def executive_overview(session: Session = Depends(get_session)):
)
).all()

# Bucket into 2-week intervals
# Bucket by day
buckets: dict[str, dict[str, int]] = defaultdict(
lambda: {label: 0 for label in top_labels}
)
for created_at, label in rows:
bucket_start = ninety_days_ago + timedelta(
weeks=2 * ((created_at - ninety_days_ago).days // 14)
)
bucket_key = bucket_start.strftime("%Y-%m-%d")
bucket_key = created_at.strftime("%Y-%m-%d")
buckets[bucket_key][label] += 1

for date_key in sorted(buckets):
Expand Down
16 changes: 9 additions & 7 deletions app/api/v1/endpoints/sentiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from __future__ import annotations

from collections import defaultdict
from datetime import datetime, timedelta, timezone
from typing import Optional
from datetime import UTC, datetime, timedelta

from fastapi import APIRouter, HTTPException, Query
from supabase import create_client
Expand Down Expand Up @@ -43,7 +42,7 @@
}


def _normalize_sentiment_label(raw: Optional[str]) -> Optional[str]:
def _normalize_sentiment_label(raw: str | None) -> str | None:
"""Map misinfo_checker's labels (POSITIVE / NEGATIVE / NEUTRAL) to
canonical lowercase keys. Tolerant of stray casing / variants."""
if raw is None:
Expand All @@ -58,7 +57,7 @@ def _normalize_sentiment_label(raw: Optional[str]) -> Optional[str]:
return None


def _parse_created_at(raw: Optional[str]) -> Optional[datetime]:
def _parse_created_at(raw: str | None) -> datetime | None:
"""Supabase returns timestamps as ISO strings; normalize to UTC datetime."""
if not raw:
return None
Expand All @@ -67,8 +66,8 @@ def _parse_created_at(raw: Optional[str]) -> Optional[datetime]:
except (ValueError, TypeError):
return None
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)
dt = dt.replace(tzinfo=UTC)
return dt.astimezone(UTC)


@router.get("/overview/sentiment-shift", response_model=SentimentShiftResponse)
Expand All @@ -89,7 +88,10 @@ def sentiment_shift(
raise HTTPException(status_code=503, detail="Supabase not configured")

window_days, bucket_size_days = _RANGE_CONFIG[range_]
now = datetime.now(timezone.utc)
# Truncate to midnight UTC so bucket boundaries fall on calendar dates,
# preventing a single day's claims from being split across two buckets
# when the window boundary lands mid-day.
now = datetime.now(UTC).replace(hour=0, minute=0, second=0, microsecond=0)
window_start = now - timedelta(days=window_days)

sb = create_client(settings.SUPABASE_URL, settings.SUPABASE_SERVICE_ROLE_KEY)
Expand Down
Loading