522 lines
28 KiB
Python
522 lines
28 KiB
Python
from sqlmodel import Session, create_engine, select
|
|
from uuid import uuid4, UUID
|
|
from datetime import datetime, timedelta, date
|
|
import os
|
|
import json
|
|
import glob
|
|
|
|
from database import engine, init_db
|
|
import models
|
|
import bcrypt
|
|
|
|
DEFAULT_SEED_HASH = bcrypt.hashpw("Password123!".encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
|
|
|
def seed_local_database():
|
|
# Ensure tables are created
|
|
init_db()
|
|
|
|
has_data = False
|
|
with Session(engine) as session:
|
|
# Prevent double seeding in local testing environment
|
|
if session.exec(select(models.Question)).first() is not None:
|
|
has_data = True
|
|
|
|
if has_data:
|
|
print("Local environment database already contains data components. Dropping all and reseeding...")
|
|
import sqlmodel
|
|
sqlmodel.SQLModel.metadata.drop_all(engine)
|
|
init_db()
|
|
|
|
print("Seeding baseline content entities...")
|
|
|
|
with Session(engine) as session:
|
|
# 1. Super Admin User
|
|
admin_id = UUID("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
|
|
admin_user = models.User(
|
|
id=admin_id,
|
|
phone="9999999998",
|
|
email="admin@pragativeer.com",
|
|
password_hash=bcrypt.hashpw("admin123".encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
|
|
name="Super Admin",
|
|
student_class=models.ClassEnum.TWELFTH,
|
|
medium=models.MediumEnum.ENGLISH,
|
|
target_exam=models.ExamEnum.JEE_MAIN,
|
|
city="New Delhi",
|
|
state="Delhi",
|
|
tier=models.TierEnum.AI_COACH,
|
|
streak_count=0,
|
|
last_active=datetime.utcnow()
|
|
)
|
|
session.add(admin_user)
|
|
|
|
# 2. AI Coach Demo Student User
|
|
student_id = UUID("b0e17378-864d-439e-8806-708dc48a891b")
|
|
student_user = models.User(
|
|
id=student_id,
|
|
phone="9999999999",
|
|
email="demo@pragativeer.com",
|
|
password_hash=bcrypt.hashpw("Pv9#kL2$mQ8!vX4@".encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
|
|
name="Rahul Sharma (Demo AI Coach)",
|
|
student_class=models.ClassEnum.TWELFTH,
|
|
medium=models.MediumEnum.ENGLISH,
|
|
target_exam=models.ExamEnum.JEE_MAIN,
|
|
school="Delhi Public School, R.K. Puram",
|
|
city="New Delhi",
|
|
state="Delhi",
|
|
tier=models.TierEnum.AI_COACH,
|
|
trial_end=None,
|
|
subscription_end=datetime.utcnow() + timedelta(days=345),
|
|
streak_count=6,
|
|
longest_streak=6,
|
|
last_active=datetime.utcnow()
|
|
)
|
|
session.add(student_user)
|
|
|
|
# 3. Neha Verma (AI Coach demo student)
|
|
neha_id = UUID("33333333-3333-3333-3333-333333333333")
|
|
neha_user = models.User(
|
|
id=neha_id,
|
|
phone="9999999997",
|
|
email="neha.demo@pragativeer.com",
|
|
password_hash=bcrypt.hashpw("Pv9#kL2$mQ8!vX4@".encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
|
|
name="Neha Verma",
|
|
student_class=models.ClassEnum.TWELFTH,
|
|
medium=models.MediumEnum.ENGLISH,
|
|
target_exam=models.ExamEnum.JEE_MAIN,
|
|
school="Modern School, Barakhamba Road",
|
|
city="New Delhi",
|
|
state="Delhi",
|
|
tier=models.TierEnum.AI_COACH,
|
|
trial_end=None,
|
|
subscription_end=datetime.utcnow() + timedelta(days=345),
|
|
streak_count=15,
|
|
longest_streak=25,
|
|
last_active=datetime.utcnow()
|
|
)
|
|
session.add(neha_user)
|
|
|
|
# 4. Priya Singh (Pro Tier demo student)
|
|
priya_id = UUID("22222222-2222-2222-2222-222222222222")
|
|
priya_user = models.User(
|
|
id=priya_id,
|
|
phone="9999999996",
|
|
email="priya.demo@pragativeer.com",
|
|
password_hash=bcrypt.hashpw("Pv9#kL2$mQ8!vX4@".encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
|
|
name="Priya Singh",
|
|
student_class=models.ClassEnum.TWELFTH,
|
|
medium=models.MediumEnum.ENGLISH,
|
|
target_exam=models.ExamEnum.NEET,
|
|
school="City Montessori School",
|
|
city="Lucknow",
|
|
state="Uttar Pradesh",
|
|
tier=models.TierEnum.PRO,
|
|
trial_end=None,
|
|
subscription_end=datetime.utcnow() + timedelta(days=345),
|
|
streak_count=42,
|
|
longest_streak=42,
|
|
last_active=datetime.utcnow()
|
|
)
|
|
session.add(priya_user)
|
|
|
|
# 5. Amit Kumar (Free Tier demo student)
|
|
amit_id = UUID("11111111-1111-1111-1111-111111111111")
|
|
amit_user = models.User(
|
|
id=amit_id,
|
|
phone="9999999995",
|
|
email="amit.demo@pragativeer.com",
|
|
password_hash=bcrypt.hashpw("Pv9#kL2$mQ8!vX4@".encode('utf-8'), bcrypt.gensalt()).decode('utf-8'),
|
|
name="Amit Kumar",
|
|
student_class=models.ClassEnum.ELEVENTH,
|
|
medium=models.MediumEnum.ENGLISH,
|
|
target_exam=models.ExamEnum.JEE_MAIN,
|
|
school="Loyola High School",
|
|
city="Patna",
|
|
state="Bihar",
|
|
tier=models.TierEnum.FREE,
|
|
trial_end=None,
|
|
streak_count=38,
|
|
longest_streak=38,
|
|
last_active=datetime.utcnow()
|
|
)
|
|
session.add(amit_user)
|
|
|
|
# Flush users first so FK constraints for subscription/transaction are satisfied
|
|
session.flush()
|
|
|
|
# Seed Demo Student Subscription records
|
|
for uid, tier in [(student_id, models.TierEnum.AI_COACH), (neha_id, models.TierEnum.AI_COACH), (priya_id, models.TierEnum.PRO)]:
|
|
demo_subscription = models.Subscription(
|
|
id=uuid4(),
|
|
user_id=uid,
|
|
tier=tier,
|
|
billing_cycle="Annual",
|
|
start_date=datetime.utcnow() - timedelta(days=20),
|
|
end_date=datetime.utcnow() + timedelta(days=345),
|
|
amount=1199900,
|
|
payment_id=f"PAY_DEMO_{str(uid)[:6]}",
|
|
status="Active",
|
|
auto_renew=True,
|
|
)
|
|
session.add(demo_subscription)
|
|
|
|
# Seed Demo Student Transaction record
|
|
demo_txn = models.Transaction(
|
|
id=uuid4(),
|
|
user_id=student_id,
|
|
amount=11999.0,
|
|
tier="Al_Coach",
|
|
status="Success",
|
|
created_at=datetime.utcnow() - timedelta(days=20),
|
|
)
|
|
session.add(demo_txn)
|
|
|
|
|
|
# admin_id is already defined above as the fixed UUID for the admin user
|
|
jee_test_id = UUID("b6faa295-3bf5-4de2-a587-d72b889629c4")
|
|
neet_test_id = UUID("e7c80521-825b-4c07-889a-ee646c05d762")
|
|
|
|
def generate_dummy_questions(subject: str, count: int, exam: models.ExamEnum, admin_id: UUID, start_idx: int = 1):
|
|
qs = []
|
|
for i in range(start_idx, count + start_idx):
|
|
q = models.Question(
|
|
id=uuid4(),
|
|
subject=subject,
|
|
topic=f"Generic Topic {i}",
|
|
subtopic=f"Subtopic {i}",
|
|
difficulty="Medium",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text=f"This is a simulated {exam.value} {subject} question #{i} for mock exam scaling.",
|
|
question_text_hi=f"यह एक अनुरूपित {exam.value} {subject} प्रश्न #{i} है।",
|
|
options={"A": "Option A", "B": "Option B", "C": "Option C", "D": "Option D"},
|
|
options_hi={"A": "विकल्प A", "B": "विकल्प B", "C": "विकल्प C", "D": "विकल्प D"},
|
|
correct_answer={"correct": "A"},
|
|
solution_text="Simulated solution for the mock test.",
|
|
solution_text_hi="मॉक टेस्ट के लिए अनुरूपित समाधान।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2024,
|
|
exam=exam,
|
|
created_by=admin_id
|
|
)
|
|
qs.append(q)
|
|
return qs
|
|
|
|
# --- JEE QUESTIONS ---
|
|
|
|
# Physics 1
|
|
jq1 = models.Question(
|
|
id=uuid4(),
|
|
subject="Physics",
|
|
topic="Electrostatics",
|
|
subtopic="Coulomb's Law",
|
|
difficulty="Medium",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="Two point charges placed at a distance $r$ in air exert a force $F$ on each other. At what distance will they exert the same force if placed in a medium of dielectric constant $K=4$?",
|
|
question_text_hi="वायु में $r$ दूरी पर स्थित दो बिंदु आवेश एक दूसरे पर $F$ बल लगाते हैं। यदि उन्हें $K=4$ परावैद्युतांक वाले माध्यम में रखा जाए, तो वे किस दूरी पर समान बल लगाएंगे?",
|
|
options={"A": "$r$", "B": "$r/2$", "C": "$2r$", "D": "$r/4$"},
|
|
options_hi={"A": "$r$", "B": "$r/2$", "C": "$2r$", "D": "$r/4$"},
|
|
correct_answer={"correct": "B"},
|
|
solution_text="Force in medium $F' = \\frac{F}{K}$. To maintain same force, $r' = \\frac{r}{\\sqrt{K}} = \\frac{r}{\\sqrt{4}} = \\frac{r}{2}$.",
|
|
solution_text_hi="माध्यम में बल $F' = \\frac{F}{K}$। समान बल बनाए रखने के लिए, $r' = \\frac{r}{\\sqrt{K}} = \\frac{r}{\\sqrt{4}} = \\frac{r}{2}$।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2024,
|
|
exam=models.ExamEnum.JEE_MAIN,
|
|
created_by=admin_id
|
|
)
|
|
|
|
# Physics 2
|
|
jq2 = models.Question(
|
|
id=uuid4(),
|
|
subject="Physics",
|
|
topic="Mechanics",
|
|
subtopic="Rotational Motion",
|
|
difficulty="Hard",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="A solid cylinder of mass $M$ and radius $R$ is rolling without slipping down an inclined plane of angle $\\theta$. What is the minimum coefficient of static friction $\\mu_s$ required to prevent slipping?",
|
|
question_text_hi="द्रव्यमान $M$ और त्रिज्या $R$ का एक ठोस बेलन कोण $\\theta$ के एक झुके हुए तल पर बिना फिसले लुढ़क रहा है। फिसलने से रोकने के लिए आवश्यक न्यूनतम स्थैतिक घर्षण गुणांक $\\mu_s$ क्या है?",
|
|
options={"A": "$\\frac{1}{3} \\tan \\theta$", "B": "$\\frac{2}{3} \\tan \\theta$", "C": "$\\frac{1}{2} \\tan \\theta$", "D": "$\\tan \\theta$"},
|
|
options_hi={"A": "$\\frac{1}{3} \\tan \\theta$", "B": "$\\frac{2}{3} \\tan \\theta$", "C": "$\\frac{1}{2} \\tan \\theta$", "D": "$\\tan \\theta$"},
|
|
correct_answer={"correct": "A"},
|
|
solution_text="$a = \\frac{g \\sin \\theta}{1 + \\frac{k^2}{R^2}}$. For solid cylinder $k^2 = \\frac{R^2}{2}$. So $a = \\frac{2}{3} g \\sin \\theta$. Friction $f = Mg \\sin \\theta - Ma = \\frac{1}{3} Mg \\sin \\theta$. Since $f \\le \\mu_s Mg \\cos \\theta$, $\\mu_s \\ge \\frac{1}{3} \\tan \\theta$.",
|
|
solution_text_hi="$a = \\frac{g \\sin \\theta}{1 + \\frac{k^2}{R^2}}$। ठोस बेलन के लिए $k^2 = \\frac{R^2}{2}$। इसलिए $a = \\frac{2}{3} g \\sin \\theta$। घर्षण $f = Mg \\sin \\theta - Ma = \\frac{1}{3} Mg \\sin \\theta$। चूंकि $f \\le \\mu_s Mg \\cos \\theta$, $\\mu_s \\ge \\frac{1}{3} \\tan \\theta$ है।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2023,
|
|
exam=models.ExamEnum.JEE_MAIN,
|
|
created_by=admin_id
|
|
)
|
|
|
|
# Chemistry 1
|
|
jq3 = models.Question(
|
|
id=uuid4(),
|
|
subject="Chemistry",
|
|
topic="Physical Chemistry",
|
|
subtopic="Chemical Kinetics",
|
|
difficulty="Medium",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="For a first-order reaction, the time required for $99\\%$ completion is $x$ times the time required for $90\\%$ completion. The value of $x$ is:",
|
|
question_text_hi="एक प्रथम-कोटि की अभिक्रिया के लिए, $99\\%$ पूर्ण होने में लगा समय $90\\%$ पूर्ण होने में लगे समय का $x$ गुना है। $x$ का मान है:",
|
|
options={"A": "$2$", "B": "$10$", "C": "$3$", "D": "$4$"},
|
|
options_hi={"A": "$2$", "B": "$10$", "C": "$3$", "D": "$4$"},
|
|
correct_answer={"correct": "A"},
|
|
solution_text="$t = \\frac{2.303}{k} \\log \\frac{100}{100-x}$. $t_{99} = \\frac{2.303}{k} \\log 100 = 2 \\times \\frac{2.303}{k}$. $t_{90} = \\frac{2.303}{k} \\log 10 = 1 \\times \\frac{2.303}{k}$. Therefore $x = 2$.",
|
|
solution_text_hi="$t = \\frac{2.303}{k} \\log \\frac{100}{100-x}$। $t_{99} = \\frac{2.303}{k} \\log 100 = 2 \\times \\frac{2.303}{k}$। $t_{90} = \\frac{2.303}{k} \\log 10 = 1 \\times \\frac{2.303}{k}$। इसलिए $x = 2$ है।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2024,
|
|
exam=models.ExamEnum.JEE_MAIN,
|
|
created_by=admin_id
|
|
)
|
|
|
|
# Maths 1
|
|
jq4 = models.Question(
|
|
id=uuid4(),
|
|
subject="Mathematics",
|
|
topic="Calculus",
|
|
subtopic="Definite Integration",
|
|
difficulty="Hard",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="The value of the integral $\\int_{0}^{\\pi/2} \\frac{\\sin^3 x}{\\sin^3 x + \\cos^3 x} dx$ is:",
|
|
question_text_hi="समाकल $\\int_{0}^{\\pi/2} \\frac{\\sin^3 x}{\\sin^3 x + \\cos^3 x} dx$ का मान है:",
|
|
options={"A": "$\\pi/2$", "B": "$\\pi/4$", "C": "$0$", "D": "$\\pi/8$"},
|
|
options_hi={"A": "$\\pi/2$", "B": "$\\pi/4$", "C": "$0$", "D": "$\\pi/8$"},
|
|
correct_answer={"correct": "B"},
|
|
solution_text="Let $I = \\int_{0}^{\\pi/2} \\frac{\\sin^3 x}{\\sin^3 x + \\cos^3 x} dx$. Using $\\int_0^a f(x)dx = \\int_0^a f(a-x)dx$, we get $I = \\int_{0}^{\\pi/2} \\frac{\\cos^3 x}{\\cos^3 x + \\sin^3 x} dx$. Adding both, $2I = \\int_{0}^{\\pi/2} 1 dx = \\pi/2$. Thus $I = \\pi/4$.",
|
|
solution_text_hi="माना $I = \\int_{0}^{\\pi/2} \\frac{\\sin^3 x}{\\sin^3 x + \\cos^3 x} dx$। गुणधर्म $\\int_0^a f(x)dx = \\int_0^a f(a-x)dx$ का उपयोग करने पर, $I = \\int_{0}^{\\pi/2} \\frac{\\cos^3 x}{\\cos^3 x + \\sin^3 x} dx$। दोनों को जोड़ने पर, $2I = \\int_{0}^{\\pi/2} 1 dx = \\pi/2$। इसलिए $I = \\pi/4$ है।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2023,
|
|
exam=models.ExamEnum.JEE_MAIN,
|
|
created_by=admin_id
|
|
)
|
|
|
|
jee_physics_dummies = generate_dummy_questions("Physics", 28, models.ExamEnum.JEE_MAIN, admin_id, start_idx=3)
|
|
jee_chem_dummies = generate_dummy_questions("Chemistry", 29, models.ExamEnum.JEE_MAIN, admin_id, start_idx=2)
|
|
jee_math_dummies = generate_dummy_questions("Mathematics", 29, models.ExamEnum.JEE_MAIN, admin_id, start_idx=2)
|
|
|
|
session.add_all([jq1, jq2, jq3, jq4])
|
|
session.add_all(jee_physics_dummies)
|
|
session.add_all(jee_chem_dummies)
|
|
session.add_all(jee_math_dummies)
|
|
|
|
# --- NEET QUESTIONS ---
|
|
|
|
# Biology 1
|
|
nq1 = models.Question(
|
|
id=uuid4(),
|
|
subject="Botany",
|
|
topic="Genetics",
|
|
subtopic="Translation",
|
|
difficulty="Easy",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="Which of the following codons codes for Methionine and also acts as an initiator codon?",
|
|
question_text_hi="निम्नलिखित में से कौन सा कोडॉन मेथियोनिन के लिए कोड करता है और एक प्रारंभक कोडॉन के रूप में भी कार्य करता है?",
|
|
options={"A": "AUG", "B": "GUG", "C": "UAA", "D": "UAG"},
|
|
options_hi={"A": "AUG", "B": "GUG", "C": "UAA", "D": "UAG"},
|
|
correct_answer={"correct": "A"},
|
|
solution_text="AUG is the starting codon (initiator codon) in eukaryotes and prokaryotes and also codes for the amino acid methionine.",
|
|
solution_text_hi="AUG यूकेरियोट्स और प्रोकैरियोट्स में प्रारंभिक कोडॉन (प्रारंभक कोडॉन) है और अमीनो एसिड मेथियोनिन के लिए भी कोड करता है।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2024,
|
|
exam=models.ExamEnum.NEET,
|
|
created_by=admin_id
|
|
)
|
|
|
|
# Physics 1 (NEET)
|
|
nq2 = models.Question(
|
|
id=uuid4(),
|
|
subject="Physics",
|
|
topic="Kinematics",
|
|
subtopic="Motion under Gravity",
|
|
difficulty="Medium",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="A ball is thrown vertically upward with a velocity of $20\\text{ m/s}$ from the top of a tower. If the height of the tower is $25\\text{ m}$, the time taken by the ball to reach the ground is (take $g = 10\\text{ m/s}^2$):",
|
|
question_text_hi="एक मीनार के शीर्ष से एक गेंद को $20\\text{ मी/से}$ के वेग से लंबवत ऊपर की ओर फेंका जाता है। यदि मीनार की ऊंचाई $25\\text{ मीटर}$ है, तो गेंद को जमीन पर पहुंचने में लगा समय है ($g = 10\\text{ मी/से}^2$ लें):",
|
|
options={"A": "$5\\text{ s}$", "B": "$3\\text{ s}$", "C": "$4\\text{ s}$", "D": "$2\\text{ s}$"},
|
|
options_hi={"A": "$5\\text{ सेकंड}$", "B": "$3\\text{ सेकंड}$", "C": "$4\\text{ सेकंड}$", "D": "$2\\text{ सेकंड}$"},
|
|
correct_answer={"correct": "A"},
|
|
solution_text="Using $s = ut + \\frac{1}{2}at^2$. Taking upward direction positive: $-25 = 20t - \\frac{1}{2}(10)t^2 \\implies 5t^2 - 20t - 25 = 0 \\implies t^2 - 4t - 5 = 0 \\implies (t-5)(t+1) = 0$. Since time cannot be negative, $t = 5\\text{ s}$.",
|
|
solution_text_hi="$s = ut + \\frac{1}{2}at^2$ का उपयोग करने पर। ऊपर की दिशा को धनात्मक मानने पर: $-25 = 20t - \\frac{1}{2}(10)t^2 \\implies 5t^2 - 20t - 25 = 0 \\implies t^2 - 4t - 5 = 0 \\implies (t-5)(t+1) = 0$। चूंकि समय ऋणात्मक नहीं हो सकता, $t = 5$ सेकंड है।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2023,
|
|
exam=models.ExamEnum.NEET,
|
|
created_by=admin_id
|
|
)
|
|
|
|
# Chemistry 1 (NEET)
|
|
nq3 = models.Question(
|
|
id=uuid4(),
|
|
subject="Chemistry",
|
|
topic="Periodic Table",
|
|
subtopic="Ionic Radii",
|
|
difficulty="Medium",
|
|
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
|
question_text="Which of the following represents the correct order of increasing ionic radii for isoelectronic species?",
|
|
question_text_hi="समइलेक्ट्रॉनिक प्रजातियों के लिए बढ़ती हुई आयनिक त्रिज्या का सही क्रम निम्नलिखित में से कौन सा दर्शाता है?",
|
|
options={"A": "$\\text{O}^{2-} < \\text{F}^- < \\text{Na}^+$", "B": "$\\text{Na}^+ < \\text{F}^- < \\text{O}^{2-}$", "C": "$\\text{F}^- < \\text{Na}^+ < \\text{O}^{2-}$", "D": "$\\text{O}^{2-} < \\text{Na}^+ < \\text{F}^-$"},
|
|
options_hi={"A": "$\\text{O}^{2-} < \\text{F}^- < \\text{Na}^+$", "B": "$\\text{Na}^+ < \\text{F}^- < \\text{O}^{2-}$", "C": "$\\text{F}^- < \\text{Na}^+ < \\text{O}^{2-}$", "D": "$\\text{O}^{2-} < \\text{Na}^+ < \\text{F}^-$"},
|
|
correct_answer={"correct": "B"},
|
|
solution_text="For isoelectronic species, ionic radii decrease with increase in atomic number (or nuclear charge). Nuclear charge: $\\text{Na}^+ (11) > \\text{F}^- (9) > \\text{O}^{2-} (8)$. Therefore, the correct order is $\\text{Na}^+ < \\text{F}^- < \\text{O}^{2-}$.",
|
|
solution_text_hi="समइलेक्ट्रॉनिक प्रजातियों के लिए, परमाणु संख्या (या नाभिकीय आवेश) में वृद्धि के साथ आयनिक त्रिज्या कम होती जाती है। नाभिकीय आवेश: $\\text{Na}^+ (11) > \\text{F}^- (9) > \\text{O}^{2-} (8)$। इसलिए, सही क्रम $\\text{Na}^+ < \\text{F}^- < \\text{O}^{2-}$ है।",
|
|
language=models.MediumEnum.BILINGUAL,
|
|
year=2024,
|
|
exam=models.ExamEnum.NEET,
|
|
created_by=admin_id
|
|
)
|
|
|
|
neet_physics_dummies = generate_dummy_questions("Physics", 49, models.ExamEnum.NEET, admin_id, start_idx=2)
|
|
neet_chem_dummies = generate_dummy_questions("Chemistry", 49, models.ExamEnum.NEET, admin_id, start_idx=2)
|
|
neet_botany_dummies = generate_dummy_questions("Botany", 49, models.ExamEnum.NEET, admin_id, start_idx=2)
|
|
neet_zoology_dummies = generate_dummy_questions("Zoology", 50, models.ExamEnum.NEET, admin_id, start_idx=1)
|
|
|
|
session.add_all([nq1, nq2, nq3])
|
|
session.add_all(neet_physics_dummies)
|
|
session.add_all(neet_chem_dummies)
|
|
session.add_all(neet_botany_dummies)
|
|
session.add_all(neet_zoology_dummies)
|
|
|
|
# --- TESTS CREATION ---
|
|
|
|
# 1. JEE Mock Test
|
|
jee_test = models.Test(
|
|
id=jee_test_id,
|
|
title="JEE Main Full Mock Test #1 (2025)",
|
|
type="Mock",
|
|
exam_pattern=models.ExamEnum.JEE_MAIN,
|
|
sections={
|
|
"Physics": {
|
|
"name": "Physics",
|
|
"questions": [str(jq1.id), str(jq2.id)] + [str(q.id) for q in jee_physics_dummies],
|
|
"time_limit": 60
|
|
},
|
|
"Chemistry": {
|
|
"name": "Chemistry",
|
|
"questions": [str(jq3.id)] + [str(q.id) for q in jee_chem_dummies],
|
|
"time_limit": 60
|
|
},
|
|
"Mathematics": {
|
|
"name": "Mathematics",
|
|
"questions": [str(jq4.id)] + [str(q.id) for q in jee_math_dummies],
|
|
"time_limit": 60
|
|
}
|
|
},
|
|
total_time=180,
|
|
difficulty="Mixed",
|
|
scheduled_date=datetime.utcnow(),
|
|
created_by=admin_id
|
|
)
|
|
session.add(jee_test)
|
|
|
|
# 2. NEET Mock Test
|
|
neet_test = models.Test(
|
|
id=neet_test_id,
|
|
title="NEET UG Full Mock Test #1 (2025)",
|
|
type="Mock",
|
|
exam_pattern=models.ExamEnum.NEET,
|
|
sections={
|
|
"Physics": {
|
|
"name": "Physics",
|
|
"questions": [str(nq2.id)] + [str(q.id) for q in neet_physics_dummies],
|
|
"time_limit": 45
|
|
},
|
|
"Chemistry": {
|
|
"name": "Chemistry",
|
|
"questions": [str(nq3.id)] + [str(q.id) for q in neet_chem_dummies],
|
|
"time_limit": 45
|
|
},
|
|
"Botany": {
|
|
"name": "Botany",
|
|
"questions": [str(nq1.id)] + [str(q.id) for q in neet_botany_dummies],
|
|
"time_limit": 45
|
|
},
|
|
"Zoology": {
|
|
"name": "Zoology",
|
|
"questions": [str(q.id) for q in neet_zoology_dummies],
|
|
"time_limit": 45
|
|
}
|
|
},
|
|
total_time=180,
|
|
difficulty="Mixed",
|
|
scheduled_date=datetime.utcnow(),
|
|
created_by=admin_id
|
|
)
|
|
session.add(neet_test)
|
|
|
|
session.commit()
|
|
|
|
# --- DPS (Daily Practice Sheets) from admin_uploads JSON files ---
|
|
print("Seeding DPS tests from admin_uploads...")
|
|
# Resolve path: works inside Docker (/app/admin_uploads) and locally (../../admin_uploads)
|
|
_script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
_docker_path = os.path.join(_script_dir, "admin_uploads")
|
|
_local_path = os.path.join(_script_dir, "..", "admin_uploads")
|
|
dps_json_dir = _docker_path if os.path.isdir(_docker_path) else _local_path
|
|
|
|
dps_json_files = sorted(glob.glob(os.path.join(dps_json_dir, "dps_*.json")))
|
|
|
|
with Session(engine) as dps_session:
|
|
for i, json_path in enumerate(dps_json_files):
|
|
with open(json_path, "r", encoding="utf-8") as f:
|
|
raw_questions = json.load(f)
|
|
|
|
subject = raw_questions[0].get("subject", "General") if raw_questions else "General"
|
|
topic = raw_questions[0].get("topic", "Chapter 4") if raw_questions else "Chapter 4"
|
|
sheet_title = f"{subject} — {topic} DPS"
|
|
sheet_date = date.today() - timedelta(days=i)
|
|
|
|
# Create Question records from the JSON
|
|
q_ids = []
|
|
for raw_q in raw_questions:
|
|
q = models.Question(
|
|
id=uuid4(),
|
|
subject=raw_q.get("subject", subject),
|
|
topic=raw_q.get("topic", topic),
|
|
subtopic=raw_q.get("subtopic", "Mixed"),
|
|
difficulty=raw_q.get("difficulty", "Medium"),
|
|
type=models.QuestionTypeEnum(raw_q.get("type", "MCQ_Single")),
|
|
question_text=raw_q.get("question_text", "Sample question"),
|
|
options=raw_q.get("options"),
|
|
correct_answer=raw_q.get("correct_answer", {"value": "A"}),
|
|
solution_text=raw_q.get("solution_text", "Refer to standard solution."),
|
|
language=models.MediumEnum.ENGLISH,
|
|
exam=models.ExamEnum.JEE_MAIN,
|
|
created_by=admin_id,
|
|
)
|
|
dps_session.add(q)
|
|
q_ids.append(str(q.id))
|
|
|
|
dps_id = uuid4()
|
|
dps = models.DPS(
|
|
id=dps_id,
|
|
date=sheet_date,
|
|
title=sheet_title,
|
|
questions=q_ids,
|
|
difficulty="Medium",
|
|
time_limit=30,
|
|
is_active=True,
|
|
created_by=admin_id,
|
|
)
|
|
dps_session.add(dps)
|
|
|
|
# Also create as Test(type='DPS') so /api/tests?type=DPS and CBT engine can run it
|
|
dps_test = models.Test(
|
|
id=dps_id,
|
|
title=sheet_title,
|
|
type="DPS",
|
|
exam_pattern=models.ExamEnum.JEE_MAIN,
|
|
sections={"Practice": {"questions": q_ids, "time_limit": 30}},
|
|
total_time=30,
|
|
difficulty="Medium",
|
|
scheduled_date=datetime.combine(sheet_date, datetime.min.time()),
|
|
is_active=True,
|
|
created_by=admin_id,
|
|
)
|
|
dps_session.add(dps_test)
|
|
print(f" ✓ DPS & CBT Test seeded: {sheet_title} ({len(q_ids)} questions)")
|
|
|
|
dps_session.commit()
|
|
|
|
print("\n=== LOCAL SEED COMPLETED SUCCESSFULLY ===")
|
|
print(f"JEE Test ID: {jee_test.id}")
|
|
print(f"NEET Test ID: {neet_test.id}")
|
|
print(f"DPS Sheets: {len(dps_json_files)} seeded from admin_uploads/\n")
|
|
|
|
if __name__ == "__main__":
|
|
seed_local_database()
|