169 lines
5.7 KiB
Python
169 lines
5.7 KiB
Python
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
|
|
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
|