Skip to content
Open
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
34 changes: 15 additions & 19 deletions backend/routers/field_officer.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,25 +409,21 @@ def get_visit_statistics(db: Session = Depends(get_db)):
"""
try:
# Use SQL aggregates instead of loading all visits into memory
total_visits = db.query(func.count(FieldOfficerVisit.id)).scalar() or 0

verified_visits = db.query(func.count(FieldOfficerVisit.id)).filter(
FieldOfficerVisit.verified_at.isnot(None)
).scalar() or 0

within_geofence_count = db.query(func.count(FieldOfficerVisit.id)).filter(
FieldOfficerVisit.within_geofence == True
).scalar() or 0

outside_geofence_count = db.query(func.count(FieldOfficerVisit.id)).filter(
FieldOfficerVisit.within_geofence == False
).scalar() or 0

unique_officers = db.query(func.count(func.distinct(FieldOfficerVisit.officer_email))).scalar() or 0

average_distance = db.query(func.avg(FieldOfficerVisit.distance_from_site)).filter(
FieldOfficerVisit.distance_from_site.isnot(None)
).scalar()
stats = db.query(
func.count(FieldOfficerVisit.id).label("total_visits"),
func.sum(case((FieldOfficerVisit.verified_at.isnot(None), 1), else_=0)).label("verified_visits"),
func.sum(case((FieldOfficerVisit.within_geofence == True, 1), else_=0)).label("within_geofence_count"),
func.sum(case((FieldOfficerVisit.within_geofence == False, 1), else_=0)).label("outside_geofence_count"),
func.count(func.distinct(FieldOfficerVisit.officer_email)).label("unique_officers"),
func.avg(FieldOfficerVisit.distance_from_site).label("average_distance")
).first()

total_visits = stats.total_visits or 0
verified_visits = int(stats.verified_visits or 0)
within_geofence_count = int(stats.within_geofence_count or 0)
outside_geofence_count = int(stats.outside_geofence_count or 0)
unique_officers = stats.unique_officers or 0
average_distance = stats.average_distance

# Round to 2 decimals if not None
if average_distance is not None:
Expand Down
9 changes: 7 additions & 2 deletions backend/routers/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,13 @@ def get_stats(db: Session = Depends(get_db)):
if cached_stats:
return JSONResponse(content=cached_stats)

total = db.query(func.count(Issue.id)).scalar()
resolved = db.query(func.count(Issue.id)).filter(Issue.status.in_(['resolved', 'verified'])).scalar()
stats = db.query(
func.count(Issue.id).label("total"),
func.sum(case((Issue.status.in_(['resolved', 'verified']), 1), else_=0)).label("resolved")
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Import case from SQLAlchemy before using it here; otherwise /stats will raise NameError at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/routers/utility.py, line 58:

<comment>Import `case` from SQLAlchemy before using it here; otherwise `/stats` will raise `NameError` at runtime.</comment>

<file context>
@@ -53,8 +53,13 @@ def get_stats(db: Session = Depends(get_db)):
-    resolved = db.query(func.count(Issue.id)).filter(Issue.status.in_(['resolved', 'verified'])).scalar()
+    stats = db.query(
+        func.count(Issue.id).label("total"),
+        func.sum(case((Issue.status.in_(['resolved', 'verified']), 1), else_=0)).label("resolved")
+    ).first()
+
</file context>
Fix with Cubic

).first()
Comment on lines +56 to +59
Comment on lines +56 to +59
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: case is not imported, causing a NameError at runtime.

The case function from SQLAlchemy is used on line 58 but is not imported. This will cause the /stats endpoint to fail.

🐛 Fix: Add `case` to the import statement
-from sqlalchemy import func
+from sqlalchemy import func, case
🧰 Tools
🪛 Ruff (0.15.5)

[error] 58-58: Undefined name case

(F821)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routers/utility.py` around lines 56 - 59, The stats query in
utility.py uses SQLAlchemy's case function (seen in the db.query call building
stats with func.sum(case(...))) but case is not imported, causing a NameError;
fix by adding case to the SQLAlchemy imports at the top of the file so the case
symbol is available to the stats query that references Issue, func.count and
func.sum.


total = stats.total or 0
resolved = int(stats.resolved or 0)
# Pending is everything else
pending = total - resolved

Expand Down
Loading