mirror of
https://github.com/R0m1k3/Reviflow.git
synced 2026-10-22 14:38:09 +02:00
fix(backend): fix 500 error in mastery stats endpoint with robust error handling
This commit is contained in:
1 parent
a4cc1da0f3
commit
75ebcef87f
1 file changed
+15
-4
@@ -551,6 +551,7 @@ async def get_mastery_stats(
|
|||||||
db: AsyncSession = Depends(get_async_session)
|
db: AsyncSession = Depends(get_async_session)
|
||||||
):
|
):
|
||||||
"""Calculates mastery level per topic."""
|
"""Calculates mastery level per topic."""
|
||||||
|
try:
|
||||||
from app.modules.quiz.models import RemediationQueue
|
from app.modules.quiz.models import RemediationQueue
|
||||||
|
|
||||||
# 1. Get all scores
|
# 1. Get all scores
|
||||||
@@ -568,6 +569,7 @@ async def get_mastery_stats(
|
|||||||
topic_map = {}
|
topic_map = {}
|
||||||
|
|
||||||
for s in scores:
|
for s in scores:
|
||||||
|
if not s.topic: continue
|
||||||
# Normalize topic
|
# Normalize topic
|
||||||
clean_topic = s.topic.replace(" (Remediation)", "").replace(" (Remédiation)", "").strip()
|
clean_topic = s.topic.replace(" (Remediation)", "").replace(" (Remédiation)", "").strip()
|
||||||
|
|
||||||
@@ -589,12 +591,15 @@ async def get_mastery_stats(
|
|||||||
|
|
||||||
error_counts = {}
|
error_counts = {}
|
||||||
for e in errors:
|
for e in errors:
|
||||||
|
if not e.topic: continue
|
||||||
# Also clean topic for errors if needed, or rely on exact match?
|
# Also clean topic for errors if needed, or rely on exact match?
|
||||||
# Ideally errors should also be grouped by clean topic.
|
# Ideally errors should also be grouped by clean topic.
|
||||||
t_key = e.topic.replace(" (Remediation)", "").replace(" (Remédiation)", "").strip()
|
t_key = e.topic.replace(" (Remediation)", "").replace(" (Remédiation)", "").strip()
|
||||||
error_counts[t_key] = error_counts.get(t_key, 0) + 1
|
error_counts[t_key] = error_counts.get(t_key, 0) + 1
|
||||||
|
|
||||||
for topic, topic_scores in topic_map.items():
|
for topic, topic_scores in topic_map.items():
|
||||||
|
if not topic_scores: continue
|
||||||
|
|
||||||
# Sort by date asc (oldest first)
|
# Sort by date asc (oldest first)
|
||||||
topic_scores.sort(key=lambda x: x.created_at)
|
topic_scores.sort(key=lambda x: x.created_at)
|
||||||
|
|
||||||
@@ -627,13 +632,12 @@ async def get_mastery_stats(
|
|||||||
elif final_mastery >= 50:
|
elif final_mastery >= 50:
|
||||||
status = "REVIEWING"
|
status = "REVIEWING"
|
||||||
|
|
||||||
last_activity = topic_scores[-1].created_at
|
# Safely get last activity
|
||||||
|
last_activity = topic_scores[-1].created_at if topic_scores else datetime.utcnow()
|
||||||
|
|
||||||
# Find the latest revision for this topic to get synthesis and tips
|
# Find the latest revision for this topic to get synthesis and tips
|
||||||
latest_revision = None
|
latest_revision = None
|
||||||
# topic_scores are already sorted by date asc, so let's find latest revision
|
try:
|
||||||
# Actually, let's find the latest revision object directly.
|
|
||||||
# Revision might have a different topic name? No, normalization should match.
|
|
||||||
from app.modules.quiz.models import Revision
|
from app.modules.quiz.models import Revision
|
||||||
rev_stmt = select(Revision).where(
|
rev_stmt = select(Revision).where(
|
||||||
Revision.learner_id == learner_id,
|
Revision.learner_id == learner_id,
|
||||||
@@ -641,6 +645,8 @@ async def get_mastery_stats(
|
|||||||
).order_by(Revision.created_at.desc()).limit(1)
|
).order_by(Revision.created_at.desc()).limit(1)
|
||||||
rev_result = await db.execute(rev_stmt)
|
rev_result = await db.execute(rev_stmt)
|
||||||
latest_revision = rev_result.scalar_one_or_none()
|
latest_revision = rev_result.scalar_one_or_none()
|
||||||
|
except Exception:
|
||||||
|
pass # Ignore revision fetch errors
|
||||||
|
|
||||||
mastery_list.append({
|
mastery_list.append({
|
||||||
"topic": topic,
|
"topic": topic,
|
||||||
@@ -657,6 +663,11 @@ async def get_mastery_stats(
|
|||||||
mastery_list.sort(key=lambda x: x['last_activity'], reverse=True)
|
mastery_list.sort(key=lambda x: x['last_activity'], reverse=True)
|
||||||
|
|
||||||
return mastery_list
|
return mastery_list
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(f"ERROR get_mastery_stats: {str(e)}\n{traceback.format_exc()}")
|
||||||
|
# Return empty list instead of 500 to keep dashboard alive
|
||||||
|
return []
|
||||||
|
|
||||||
@router.get("/stats/activity")
|
@router.get("/stats/activity")
|
||||||
async def get_activity_stats(
|
async def get_activity_stats(
|
||||||
|
|||||||
Reference in new issue
Block a user