from datetime import datetime, date from enum import Enum from typing import List, Optional, Dict, Any from uuid import UUID, uuid4 from sqlmodel import Field, SQLModel, Relationship, JSON # --- ENUMS --- class ClassEnum(str, Enum): ELEVENTH = "11" TWELFTH = "12" DROPPER = "Dropper" class ExamEnum(str, Enum): JEE_MAIN = "JEE_Main" JEE_ADVANCED = "JEE_Advanced" NEET = "NEET" class MediumEnum(str, Enum): ENGLISH = "English" HINDI = "Hindi" BILINGUAL = "Bilingual" class TierEnum(str, Enum): FREE = "Free" PRO = "Pro" AI_COACH = "Al_Coach" class QuestionTypeEnum(str, Enum): MCQ_SINGLE = "MCQ_Single" MCQ_MULTIPLE = "MCQ_Multiple" NUMERICAL = "Numerical" class SourceTypeEnum(str, Enum): DPS = "DPS" TEST = "Test" MICROTEST = "MicroTest" class ErrorTypeEnum(str, Enum): CONCEPTUAL = "Conceptual" CALCULATION = "Calculation" READING = "Reading" TIME_PRESSURE = "Time_Pressure" # --- CORE DB MODELS --- class User(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) email: Optional[str] = Field(default=None, unique=True) phone: Optional[str] = Field(default=None, unique=True) password_hash: str name: str student_class: ClassEnum = Field(alias="class") target_exam: ExamEnum medium: MediumEnum school: Optional[str] = None city: str state: str tier: TierEnum = Field(default=TierEnum.FREE) trial_end: Optional[datetime] = None subscription_end: Optional[datetime] = None streak_count: int = Field(default=0) longest_streak: int = Field(default=0) created_at: datetime = Field(default_factory=datetime.utcnow) last_active: datetime = Field(default_factory=datetime.utcnow) class Parent(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) email: str phone: str password_hash: str name: str language: str = Field(default="English") # e.g., Hindi, English linked_students: List[str] = Field(default=[], sa_type=JSON) created_at: datetime = Field(default_factory=datetime.utcnow) class Question(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) subject: str # Physics, Chemistry, Maths, Biology topic: str subtopic: str difficulty: str # Easy, Medium, Hard type: QuestionTypeEnum question_text: str question_text_hi: Optional[str] = None options: Optional[Dict[str, Any]] = Field(default=None, sa_type=JSON) options_hi: Optional[Dict[str, Any]] = Field(default=None, sa_type=JSON) correct_answer: Dict[str, Any] = Field(sa_type=JSON) solution_text: str solution_text_hi: Optional[str] = None language: MediumEnum year: Optional[int] = None exam: Optional[ExamEnum] = None tags: Optional[str] = None is_active: bool = Field(default=True) created_by: UUID verified_by: Optional[UUID] = None class DPS(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) date: date title: str questions: List[str] = Field(default=[], sa_type=JSON) # Array of question UUID strings difficulty: str time_limit: int is_active: bool = Field(default=True) created_by: UUID class Test(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) title: str type: str # Weekly, Mock, PYQ exam_pattern: ExamEnum sections: Dict[str, Any] = Field(sa_type=JSON) # Array of {name, questions[], time_limit} total_time: int difficulty: str scheduled_date: Optional[datetime] = None end_date: Optional[datetime] = None is_active: bool = Field(default=True) created_by: UUID class Attempt(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) user_id: UUID = Field(foreign_key="user.id") source_type: SourceTypeEnum source_id: UUID started_at: datetime submitted_at: Optional[datetime] = None time_taken: Optional[int] = None # in seconds answers: Optional[Dict[str, Any]] = Field(default=None, sa_type=JSON) # Array of {question_id, selected_answer, is_correct, time_spent} score: int = Field(default=0) total_questions: int accuracy: float = Field(default=0.0) status: str = Field(default="In_Progress") # In_Progress, Submitted, Timed_Out class Mistake(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) user_id: UUID = Field(foreign_key="user.id") question_id: UUID = Field(foreign_key="question.id") attempt_id: UUID = Field(foreign_key="attempt.id") error_type: ErrorTypeEnum topic: str subtopic: str next_review: datetime review_count: int = Field(default=0) consecutive_correct: int = Field(default=0) is_mastered: bool = Field(default=False) created_at: datetime = Field(default_factory=datetime.utcnow) class StudyPlan(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) user_id: UUID = Field(foreign_key="user.id") date: date tasks: Dict[str, Any] = Field(sa_type=JSON) # Array of {type, description, duration_minutes, status} is_completed: bool = Field(default=False) generated_at: datetime = Field(default_factory=datetime.utcnow) feedback: Optional[str] = None class Subscription(SQLModel, table=True): id: UUID = Field(default_factory=uuid4, primary_key=True) user_id: UUID = Field(foreign_key="user.id") tier: TierEnum billing_cycle: str # Monthly, Quarterly, Annual start_date: datetime end_date: datetime amount: int # Stored in Paise (INR) payment_id: str # Razorpay order ID / payment ID status: str # Active, Expired, Cancelled, Paused auto_renew: bool = Field(default=True) coupon_code: Optional[str] = None