github-actions[bot] commited on
Commit
3ec1cd7
ยท
1 Parent(s): 4f74e1c

๐Ÿš€ Auto-deploy backend from GitHub (5544829)

Browse files
Files changed (1) hide show
  1. main.py +284 -48
main.py CHANGED
@@ -290,6 +290,7 @@ def call_hf_chat(
290
  top_p: float = 0.9,
291
  repetition_penalty: float = 1.15,
292
  model: Optional[str] = None,
 
293
  ) -> str:
294
  """
295
  Call the HF Serverless Inference API (OpenAI-compatible chat endpoint)
@@ -314,7 +315,7 @@ def call_hf_chat(
314
  }
315
 
316
  for attempt in range(3):
317
- resp = http_requests.post(url, headers=headers, json=payload, timeout=60)
318
  if resp.status_code == 503 and attempt < 2:
319
  logger.warning(f"HF chat 503 (model loading), retry {attempt + 1}/3")
320
  time.sleep(3)
@@ -1342,48 +1343,70 @@ class CalculatorResponse(BaseModel):
1342
  latex: Optional[str] = None
1343
 
1344
 
1345
- # โ”€โ”€โ”€ Quiz Topics Database โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1346
 
1347
  MATH_TOPICS_BY_GRADE: Dict[str, Dict[str, List[str]]] = {
1348
- "Grade 7": {
1349
- "Number Sense": ["Integers", "Fractions & Decimals", "Ratios & Proportions", "Percentages"],
1350
- "Algebra": ["Variables & Expressions", "One-Step Equations", "Inequalities", "Patterns"],
1351
- "Geometry": ["Angles", "Triangles", "Area & Perimeter", "Volume"],
1352
- "Statistics": ["Mean, Median, Mode", "Bar & Line Graphs", "Probability Basics"],
1353
- },
1354
- "Grade 8": {
1355
- "Algebra": ["Linear Equations", "Systems of Equations (Introduction)", "Slope & Rate of Change", "Functions"],
1356
- "Geometry": ["Pythagorean Theorem", "Transformations", "Congruence & Similarity", "Surface Area & Volume"],
1357
- "Number Sense": ["Exponents & Powers", "Scientific Notation", "Square & Cube Roots", "Rational & Irrational Numbers"],
1358
- "Statistics": ["Scatter Plots", "Two-Way Tables", "Probability of Compound Events"],
1359
- },
1360
- "Grade 9": {
1361
- "Algebra": ["Linear Functions", "Quadratic Equations", "Polynomials", "Factoring"],
1362
- "Geometry": ["Coordinate Geometry", "Circles", "Trigonometric Ratios", "Proofs"],
1363
- "Statistics": ["Data Analysis", "Normal Distribution Basics", "Sampling Methods"],
1364
- "Number Sense": ["Radicals & Exponents", "Rational Expressions"],
1365
- },
1366
- "Grade 10": {
1367
- "Algebra": ["Quadratic Functions", "Systems of Equations", "Exponential Functions", "Radical Equations"],
1368
- "Geometry": ["Circle Theorems", "Solid Geometry", "Coordinate Proofs", "Trigonometry"],
1369
- "Statistics & Probability": ["Permutations & Combinations", "Conditional Probability", "Binomial Probability"],
1370
- "Pre-Calculus": ["Sequences & Series", "Polynomial Functions"],
1371
- },
1372
  "Grade 11": {
1373
- "Pre-Calculus": ["Functions & Graphs", "Polynomial Division", "Rational Functions", "Logarithmic Functions", "Trigonometric Functions"],
1374
- "Statistics": ["Regression Analysis", "Confidence Intervals", "Hypothesis Testing Basics"],
1375
- "Algebra": ["Complex Numbers", "Matrices (Introduction)", "Conic Sections"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1376
  },
1377
  "Grade 12": {
1378
- "Basic Calculus": ["Limits", "Derivatives", "Integration", "Applications of Derivatives", "Area Under a Curve"],
1379
- "Statistics": ["Normal Distribution", "Z-Scores", "Chi-Square Tests"],
1380
- "Advanced Algebra": ["Series & Convergence", "Parametric Equations", "Polar Coordinates"],
1381
- },
1382
- "College": {
1383
- "Calculus": ["Multivariable Calculus", "Differential Equations", "Vector Calculus", "Infinite Series"],
1384
- "Linear Algebra": ["Matrices & Determinants", "Eigenvalues & Eigenvectors", "Vector Spaces", "Linear Transformations"],
1385
- "Discrete Mathematics": ["Set Theory", "Graph Theory", "Combinatorics", "Number Theory"],
1386
- "Statistics": ["Probability Distributions", "Bayesian Statistics", "Statistical Inference", "ANOVA"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1387
  },
1388
  }
1389
 
@@ -1630,15 +1653,64 @@ Remember:
1630
 
1631
  logger.info(f"Generating quiz: {request.numQuestions} questions, topics={effective_topics}")
1632
 
1633
- raw_content = call_hf_chat(messages, max_tokens=4096, temperature=0.3, top_p=0.9)
1634
- logger.info(f"Raw quiz response length: {len(raw_content)} chars")
 
 
1635
 
1636
- parsed_questions = _parse_quiz_json(raw_content)
1637
- if not parsed_questions:
1638
- logger.error(f"Failed to parse quiz JSON. Raw content:\n{raw_content[:500]}")
1639
- raise HTTPException(
1640
- status_code=500,
1641
- detail="Failed to parse quiz questions from AI response. Please try again.",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1642
  )
1643
 
1644
  validated = _validate_quiz_questions(parsed_questions, distribution)
@@ -1712,7 +1784,8 @@ async def preview_quiz(request: QuizGenerationRequest):
1712
  @app.get("/api/quiz/topics")
1713
  async def get_quiz_topics(gradeLevel: Optional[str] = None):
1714
  """
1715
- Return structured list of math topics organised by grade level.
 
1716
  If gradeLevel is provided, return topics for that grade only.
1717
  """
1718
  if gradeLevel:
@@ -1729,7 +1802,11 @@ async def get_quiz_topics(gradeLevel: Optional[str] = None):
1729
  detail=f"Grade level '{gradeLevel}' not found. Available: {list(MATH_TOPICS_BY_GRADE.keys())}",
1730
  )
1731
 
1732
- return {"allTopics": MATH_TOPICS_BY_GRADE}
 
 
 
 
1733
 
1734
 
1735
  # โ”€โ”€โ”€ Student Competency Assessment โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -2195,6 +2272,165 @@ async def get_analytics_config():
2195
  }
2196
 
2197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2198
  # โ”€โ”€โ”€ Automation Engine Endpoints โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
2199
 
2200
 
 
290
  top_p: float = 0.9,
291
  repetition_penalty: float = 1.15,
292
  model: Optional[str] = None,
293
+ timeout: int = 90,
294
  ) -> str:
295
  """
296
  Call the HF Serverless Inference API (OpenAI-compatible chat endpoint)
 
315
  }
316
 
317
  for attempt in range(3):
318
+ resp = http_requests.post(url, headers=headers, json=payload, timeout=timeout)
319
  if resp.status_code == 503 and attempt < 2:
320
  logger.warning(f"HF chat 503 (model loading), retry {attempt + 1}/3")
321
  time.sleep(3)
 
1343
  latex: Optional[str] = None
1344
 
1345
 
1346
+ # โ”€โ”€โ”€ Quiz Topics Database (SHS Grade 11-12 Only) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1347
 
1348
  MATH_TOPICS_BY_GRADE: Dict[str, Dict[str, List[str]]] = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1349
  "Grade 11": {
1350
+ "General Mathematics - Functions and Their Graphs": [
1351
+ "Functions and Relations", "Evaluating Functions", "Operations on Functions",
1352
+ "Composite Functions", "Inverse Functions", "Rational Functions",
1353
+ "Exponential Functions", "Logarithmic Functions",
1354
+ ],
1355
+ "General Mathematics - Business Mathematics": [
1356
+ "Simple Interest", "Compound Interest", "Annuities",
1357
+ "Loans and Amortization", "Stocks and Bonds",
1358
+ ],
1359
+ "General Mathematics - Logic": [
1360
+ "Propositions and Connectives", "Truth Tables",
1361
+ "Logical Equivalence", "Valid Arguments and Fallacies",
1362
+ ],
1363
+ "Statistics and Probability - Random Variables": [
1364
+ "Random Variables", "Discrete Probability Distributions",
1365
+ "Mean and Variance of Discrete RV",
1366
+ ],
1367
+ "Statistics and Probability - Normal Distribution": [
1368
+ "Normal Distribution", "Standard Normal Distribution and Z-scores",
1369
+ "Areas Under the Normal Curve",
1370
+ ],
1371
+ "Statistics and Probability - Sampling and Estimation": [
1372
+ "Sampling Distributions", "Central Limit Theorem",
1373
+ "Point Estimation", "Confidence Intervals",
1374
+ ],
1375
+ "Statistics and Probability - Hypothesis Testing": [
1376
+ "Hypothesis Testing Concepts", "T-test", "Z-test",
1377
+ "Correlation and Regression",
1378
+ ],
1379
  },
1380
  "Grade 12": {
1381
+ "Pre-Calculus - Analytic Geometry": [
1382
+ "Conic Sections - Parabola", "Conic Sections - Ellipse",
1383
+ "Conic Sections - Hyperbola", "Conic Sections - Circle",
1384
+ "Systems of Nonlinear Equations",
1385
+ ],
1386
+ "Pre-Calculus - Series and Induction": [
1387
+ "Sequences and Series", "Arithmetic Sequences", "Geometric Sequences",
1388
+ "Mathematical Induction", "Binomial Theorem",
1389
+ ],
1390
+ "Pre-Calculus - Trigonometry": [
1391
+ "Angles and Unit Circle", "Trigonometric Functions",
1392
+ "Trigonometric Identities", "Sum and Difference Formulas",
1393
+ "Inverse Trigonometric Functions", "Polar Coordinates",
1394
+ ],
1395
+ "Basic Calculus - Limits": [
1396
+ "Limits of Functions", "Limit Theorems", "One-Sided Limits",
1397
+ "Infinite Limits and Limits at Infinity", "Continuity of Functions",
1398
+ ],
1399
+ "Basic Calculus - Derivatives": [
1400
+ "Definition of the Derivative", "Differentiation Rules", "Chain Rule",
1401
+ "Implicit Differentiation", "Higher-Order Derivatives", "Related Rates",
1402
+ "Extrema and the First Derivative Test",
1403
+ "Concavity and the Second Derivative Test", "Optimization Problems",
1404
+ ],
1405
+ "Basic Calculus - Integration": [
1406
+ "Antiderivatives and Indefinite Integrals",
1407
+ "Definite Integrals and the FTC",
1408
+ "Integration by Substitution", "Area Under a Curve",
1409
+ ],
1410
  },
1411
  }
1412
 
 
1653
 
1654
  logger.info(f"Generating quiz: {request.numQuestions} questions, topics={effective_topics}")
1655
 
1656
+ # Scale max_tokens based on requested questions โ€” each question needs ~400-600 tokens
1657
+ max_tokens = min(16384, max(4096, request.numQuestions * 600))
1658
+ # Use longer HTTP timeout for quiz generation (scales with question count)
1659
+ http_timeout = max(90, request.numQuestions * 12)
1660
 
1661
+ parsed_questions: List[Dict[str, Any]] = []
1662
+ max_attempts = 2 # Retry once if LLM generates too few questions
1663
+
1664
+ for attempt in range(max_attempts):
1665
+ raw_content = call_hf_chat(
1666
+ messages, max_tokens=max_tokens, temperature=0.3, top_p=0.9,
1667
+ timeout=http_timeout,
1668
+ )
1669
+ logger.info(f"Raw quiz response length: {len(raw_content)} chars (attempt {attempt + 1})")
1670
+
1671
+ parsed_questions = _parse_quiz_json(raw_content)
1672
+
1673
+ if not parsed_questions:
1674
+ logger.error(f"Failed to parse quiz JSON (attempt {attempt + 1}). Raw content:\n{raw_content[:500]}")
1675
+ if attempt < max_attempts - 1:
1676
+ logger.info("Retrying quiz generation...")
1677
+ continue
1678
+ raise HTTPException(
1679
+ status_code=500,
1680
+ detail="Failed to parse quiz questions from AI response. Please try again.",
1681
+ )
1682
+
1683
+ # If we got at least 70% of requested questions, accept the result
1684
+ if len(parsed_questions) >= request.numQuestions * 0.7:
1685
+ break
1686
+
1687
+ # Otherwise retry with a stronger nudge
1688
+ if attempt < max_attempts - 1:
1689
+ logger.warning(
1690
+ f"LLM generated only {len(parsed_questions)}/{request.numQuestions} questions "
1691
+ f"(attempt {attempt + 1}). Retrying with reinforced prompt..."
1692
+ )
1693
+ # Add an assistant + user turn to push the LLM harder
1694
+ messages = [
1695
+ {"role": "system", "content": QUIZ_GENERATION_SYSTEM_PROMPT},
1696
+ {"role": "user", "content": prompt},
1697
+ {"role": "assistant", "content": raw_content},
1698
+ {
1699
+ "role": "user",
1700
+ "content": (
1701
+ f"You only generated {len(parsed_questions)} questions but I need "
1702
+ f"exactly {request.numQuestions}. Please generate ALL "
1703
+ f"{request.numQuestions} questions in a single JSON array. "
1704
+ f"Do not stop early."
1705
+ ),
1706
+ },
1707
+ ]
1708
+
1709
+ # Warn if the LLM still generated fewer questions than requested
1710
+ if len(parsed_questions) < request.numQuestions:
1711
+ logger.warning(
1712
+ f"LLM generated {len(parsed_questions)}/{request.numQuestions} questions "
1713
+ f"after {max_attempts} attempts (raw length={len(raw_content)} chars)."
1714
  )
1715
 
1716
  validated = _validate_quiz_questions(parsed_questions, distribution)
 
1784
  @app.get("/api/quiz/topics")
1785
  async def get_quiz_topics(gradeLevel: Optional[str] = None):
1786
  """
1787
+ Return structured list of SHS math topics organised by grade level.
1788
+ Only Grade 11 and Grade 12 are supported.
1789
  If gradeLevel is provided, return topics for that grade only.
1790
  """
1791
  if gradeLevel:
 
1802
  detail=f"Grade level '{gradeLevel}' not found. Available: {list(MATH_TOPICS_BY_GRADE.keys())}",
1803
  )
1804
 
1805
+ # Return all SHS topics organized by grade
1806
+ return {
1807
+ "gradeLevels": list(MATH_TOPICS_BY_GRADE.keys()),
1808
+ "allTopics": MATH_TOPICS_BY_GRADE,
1809
+ }
1810
 
1811
 
1812
  # โ”€โ”€โ”€ Student Competency Assessment โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
2272
  }
2273
 
2274
 
2275
+ # โ”€โ”€โ”€ Topic Mastery Analytics โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
2276
+
2277
+ # SHS topic data for fallback/mock generation
2278
+ _SHS_TOPICS = {
2279
+ "gen-math": {
2280
+ "name": "General Mathematics",
2281
+ "topics": [
2282
+ ("Functions and Relations", "Functions and Their Graphs"),
2283
+ ("Evaluating Functions", "Functions and Their Graphs"),
2284
+ ("Operations on Functions", "Functions and Their Graphs"),
2285
+ ("Composite Functions", "Functions and Their Graphs"),
2286
+ ("Inverse Functions", "Functions and Their Graphs"),
2287
+ ("Rational Functions", "Functions and Their Graphs"),
2288
+ ("Exponential Functions", "Functions and Their Graphs"),
2289
+ ("Logarithmic Functions", "Functions and Their Graphs"),
2290
+ ("Simple Interest", "Business Mathematics"),
2291
+ ("Compound Interest", "Business Mathematics"),
2292
+ ("Annuities", "Business Mathematics"),
2293
+ ("Loans and Amortization", "Business Mathematics"),
2294
+ ("Stocks and Bonds", "Business Mathematics"),
2295
+ ("Propositions and Connectives", "Logic"),
2296
+ ("Truth Tables", "Logic"),
2297
+ ("Logical Equivalence", "Logic"),
2298
+ ("Valid Arguments and Fallacies", "Logic"),
2299
+ ],
2300
+ },
2301
+ "stats-prob": {
2302
+ "name": "Statistics and Probability",
2303
+ "topics": [
2304
+ ("Random Variables", "Random Variables"),
2305
+ ("Discrete Probability Distributions", "Random Variables"),
2306
+ ("Mean and Variance of Discrete RV", "Random Variables"),
2307
+ ("Normal Distribution", "Normal Distribution"),
2308
+ ("Standard Normal Distribution and Z-scores", "Normal Distribution"),
2309
+ ("Areas Under the Normal Curve", "Normal Distribution"),
2310
+ ("Sampling Distributions", "Sampling and Estimation"),
2311
+ ("Central Limit Theorem", "Sampling and Estimation"),
2312
+ ("Point Estimation", "Sampling and Estimation"),
2313
+ ("Confidence Intervals", "Sampling and Estimation"),
2314
+ ("Hypothesis Testing Concepts", "Hypothesis Testing"),
2315
+ ("T-test", "Hypothesis Testing"),
2316
+ ("Z-test", "Hypothesis Testing"),
2317
+ ("Correlation and Regression", "Correlation and Regression"),
2318
+ ],
2319
+ },
2320
+ "pre-calc": {
2321
+ "name": "Pre-Calculus",
2322
+ "topics": [
2323
+ ("Conic Sections - Parabola", "Analytic Geometry"),
2324
+ ("Conic Sections - Ellipse", "Analytic Geometry"),
2325
+ ("Conic Sections - Hyperbola", "Analytic Geometry"),
2326
+ ("Conic Sections - Circle", "Analytic Geometry"),
2327
+ ("Systems of Nonlinear Equations", "Analytic Geometry"),
2328
+ ("Sequences and Series", "Series and Induction"),
2329
+ ("Arithmetic Sequences", "Series and Induction"),
2330
+ ("Geometric Sequences", "Series and Induction"),
2331
+ ("Mathematical Induction", "Series and Induction"),
2332
+ ("Binomial Theorem", "Series and Induction"),
2333
+ ("Angles and Unit Circle", "Trigonometry"),
2334
+ ("Trigonometric Functions", "Trigonometry"),
2335
+ ("Trigonometric Identities", "Trigonometry"),
2336
+ ("Sum and Difference Formulas", "Trigonometry"),
2337
+ ("Inverse Trigonometric Functions", "Trigonometry"),
2338
+ ("Polar Coordinates", "Trigonometry"),
2339
+ ],
2340
+ },
2341
+ "basic-calc": {
2342
+ "name": "Basic Calculus",
2343
+ "topics": [
2344
+ ("Limits of Functions", "Limits"),
2345
+ ("Limit Theorems", "Limits"),
2346
+ ("One-Sided Limits", "Limits"),
2347
+ ("Infinite Limits and Limits at Infinity", "Limits"),
2348
+ ("Continuity of Functions", "Limits"),
2349
+ ("Definition of the Derivative", "Derivatives"),
2350
+ ("Differentiation Rules", "Derivatives"),
2351
+ ("Chain Rule", "Derivatives"),
2352
+ ("Implicit Differentiation", "Derivatives"),
2353
+ ("Higher-Order Derivatives", "Derivatives"),
2354
+ ("Related Rates", "Derivatives"),
2355
+ ("Extrema and the First Derivative Test", "Derivatives"),
2356
+ ("Concavity and the Second Derivative Test", "Derivatives"),
2357
+ ("Optimization Problems", "Derivatives"),
2358
+ ("Antiderivatives and Indefinite Integrals", "Integration"),
2359
+ ("Definite Integrals and the FTC", "Integration"),
2360
+ ("Integration by Substitution", "Integration"),
2361
+ ("Area Under a Curve", "Integration"),
2362
+ ],
2363
+ },
2364
+ }
2365
+
2366
+
2367
+ @app.get("/api/analytics/topic-mastery")
2368
+ async def topic_mastery_analytics(
2369
+ teacherId: str = Query(..., description="Teacher UID"),
2370
+ classId: Optional[str] = Query(None, description="Optional class ID filter"),
2371
+ ):
2372
+ """
2373
+ Aggregate per-topic mastery statistics for a teacher's class.
2374
+ Returns topic-level averages, attempt counts, and mastery status.
2375
+ """
2376
+ import random
2377
+
2378
+ try:
2379
+ # In production, this would query Firestore quiz submissions.
2380
+ # For now, generate realistic mock data seeded by teacherId.
2381
+ rng = random.Random(hash(teacherId))
2382
+ total_students = 30
2383
+
2384
+ topics_out = []
2385
+ mastered_count = 0
2386
+ needs_attention_count = 0
2387
+ excluded_count = 0
2388
+
2389
+ for subj_id, subj_data in _SHS_TOPICS.items():
2390
+ for topic_name, unit in subj_data["topics"]:
2391
+ attempted = rng.randint(3, total_students)
2392
+ class_avg = round(rng.uniform(35, 95), 1)
2393
+ above_85 = int(attempted * (rng.uniform(0.6, 0.95) if class_avg >= 85 else rng.uniform(0.05, 0.35)))
2394
+ mastery_pct = round((above_85 / total_students) * 100, 1)
2395
+
2396
+ if attempted < 3:
2397
+ status = "no_data"
2398
+ elif mastery_pct >= 75:
2399
+ status = "mastered"
2400
+ mastered_count += 1
2401
+ elif class_avg >= 60:
2402
+ status = "on_track"
2403
+ else:
2404
+ status = "needs_attention"
2405
+ needs_attention_count += 1
2406
+
2407
+ topics_out.append({
2408
+ "topicName": topic_name,
2409
+ "subjectId": subj_id,
2410
+ "unit": unit,
2411
+ "classAverage": class_avg,
2412
+ "studentsAttempted": attempted,
2413
+ "totalStudents": total_students,
2414
+ "studentsAbove85": above_85,
2415
+ "masteryPercentage": mastery_pct,
2416
+ "masteryStatus": status,
2417
+ "isExcluded": False,
2418
+ })
2419
+
2420
+ return {
2421
+ "topics": topics_out,
2422
+ "summary": {
2423
+ "totalTopicsTracked": len(topics_out),
2424
+ "masteredCount": mastered_count,
2425
+ "needsAttentionCount": needs_attention_count,
2426
+ "excludedCount": excluded_count,
2427
+ },
2428
+ }
2429
+ except Exception as e:
2430
+ logger.error(f"Topic mastery analytics error: {e}")
2431
+ raise HTTPException(status_code=500, detail=f"Topic mastery error: {str(e)}")
2432
+
2433
+
2434
  # โ”€โ”€โ”€ Automation Engine Endpoints โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
2435
 
2436