from sqlmodel import Session, select from uuid import UUID import models def calculate_readiness_score(user_id: UUID, session: Session) -> dict: """ Calculates the Exam Readiness Score (0-100) based on PRD A-05 parameters. Weights: Accuracy (40%), Speed (20%), Syllabus Coverage (20%), Consistency (10%), Mistake Recovery (10%). """ user = session.get(models.User, user_id) if not user: return {} attempts = session.exec( select(models.Attempt).where(models.Attempt.user_id == user_id) ).all() if not attempts: return { "score": 0, "status": "Not enough data", "prediction": "Start practice to calculate", "target_type": "Unranked", "breakdown": { "accuracy": 0, "speed": 0, "consistency": 0, "recovery": 0, "coverage": 0, } } # 1. Accuracy (40% Weight) avg_accuracy = sum(a.accuracy for a in attempts) / len(attempts) w_accuracy = (avg_accuracy / 100) * 40 # 2. Speed (20% Weight) - Assuming benchmark is 120s per question time_per_q = [ (a.time_taken / a.total_questions) for a in attempts if a.total_questions > 0 and a.time_taken ] avg_time = sum(time_per_q) / len(time_per_q) if time_per_q else 120 speed_factor = min(1.0, 120 / max(avg_time, 1)) # Cap at 1.0 w_speed = speed_factor * 20 # 3. Consistency (10% Weight) streak = min(user.streak_count, 30) # Max consistency score hit at 30 days w_consistency = (streak / 30) * 10 # 4. Mistake Recovery (10% Weight) mistakes = session.exec(select(models.Mistake).where(models.Mistake.user_id == user_id)).all() mastered_count = sum(1 for m in mistakes if m.is_mastered) total_mistakes = max(len(mistakes), 1) w_recovery = (mastered_count / total_mistakes) * 10 # 5. Syllabus Coverage (20% Weight) # For local sandbox, we will estimate based on total questions attempted total_q_attempted = sum(a.total_questions for a in attempts) coverage_factor = min(total_q_attempted / 500, 1.0) # Assume 500 questions = decent baseline coverage w_coverage = coverage_factor * 20 # Total Calculation total_score = round(w_accuracy + w_speed + w_consistency + w_recovery + w_coverage) # Calculate College Prediction based on PRD A-07 prediction = "Keep Practicing" target_type = "State Level" if total_score >= 85: prediction = "IIT Bombay / IIT Delhi within reach!" target_type = "Reach" elif total_score >= 70: prediction = "NIT Trichy / NIT Surathkal on track." target_type = "Target" elif total_score >= 50: prediction = "Top GFTIs / State Engineering Colleges." target_type = "Safety" return { "score": total_score, "breakdown": { "accuracy": round(w_accuracy, 1), "speed": round(w_speed, 1), "consistency": round(w_consistency, 1), "recovery": round(w_recovery, 1), "coverage": round(w_coverage, 1) }, "college_prediction": prediction, "prediction_category": target_type }