feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine
This commit is contained in:
168
backend/models.py
Normal file
168
backend/models.py
Normal file
@@ -0,0 +1,168 @@
|
||||
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
|
||||
Reference in New Issue
Block a user