Feat: Seed accurate complex LaTeX questions and fix Play button hover state in dark mode

This commit is contained in:
2026-07-07 16:34:25 +05:30
parent 15cb718196
commit e901108288
2 changed files with 100 additions and 62 deletions

View File

@@ -1,7 +1,7 @@
import asyncio import asyncio
import os import os
import uuid import uuid
from sqlmodel import Session, select from sqlmodel import Session, select, delete
from datetime import datetime from datetime import datetime
from database import engine, init_db from database import engine, init_db
@@ -10,74 +10,112 @@ import storage
from pdf_parser import parse_pdf_to_questions from pdf_parser import parse_pdf_to_questions
async def seed_pdfs(): async def seed_pdfs():
# Admin ID fallback admin_id_str = "b0e17378-864d-439e-8806-708dc48a891b"
admin_id_str = "b0e17378-864d-439e-8806-708dc48a891b" # Or fetch first user
with Session(engine) as session: with Session(engine) as session:
admin = session.exec(select(models.User).limit(1)).first() admin = session.exec(select(models.User).limit(1)).first()
if admin: admin_id = admin.id if admin else uuid.UUID(admin_id_str)
admin_id = admin.id
else:
admin_id = uuid.UUID(admin_id_str)
# Paths to the 4 PDFs # Delete previously seeded AI DPPs to prevent duplicates
base_dir = r"/app/pdfs" statement = select(models.Test).where(models.Test.title.like("AI DPP:%"))
pdf_files = [ old_tests = session.exec(statement).all()
"media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782971559253.pdf", for t in old_tests:
"media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1782979477911.pdf", session.delete(t)
"media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783273583537.pdf", session.commit()
"media_e3dd3511-9fc4-402b-8054-6a56f7ed8202_1783410386530.pdf"
]
subjects = ["Physics", "Chemistry", "Biology", "Mathematics"]
topics = ["Rotational Dynamics", "Chemical Kinetics", "Genetics", "Calculus"]
for idx, filename in enumerate(pdf_files): # Realistic Mock Data
file_path = os.path.join(base_dir, filename) mock_data = [
if not os.path.exists(file_path): {
print(f"File not found: {file_path}") "topic": "Rotational Dynamics",
continue "subject": "Physics",
"difficulty": "Hard",
print(f"Processing {filename}...") "questions": [
with open(file_path, "rb") as f:
pdf_bytes = f.read()
# 1. Upload to Minio
object_name = f"dpps/{uuid.uuid4()}_{filename}"
storage.upload_file(pdf_bytes, object_name, content_type="application/pdf")
# 2. Extract
questions_data = await parse_pdf_to_questions(pdf_bytes, "JEE_Main")
if not questions_data:
print(f"Warning: No questions extracted for {filename}. Using mock data fallback.")
questions_data = [
{ {
"subject": subjects[idx], "question_text": r"A solid sphere of mass $M$ and radius $R$ is rolling without slipping on a horizontal surface. Its center of mass is moving with velocity $v$. What is its total kinetic energy?",
"topic": topics[idx], "options": {"A": r"$\frac{1}{2}Mv^2$", "B": r"$\frac{7}{10}Mv^2$", "C": r"$\frac{5}{7}Mv^2$", "D": r"$\frac{3}{4}Mv^2$"},
"subtopic": "General", "correct_answer": {"value": "B"},
"difficulty": "Hard", "solution_text": r"Total KE = Translational KE + Rotational KE. $KE_{tot} = \frac{1}{2}Mv^2 + \frac{1}{2}I\omega^2$. For solid sphere, $I = \frac{2}{5}MR^2$, and since it rolls without slipping, $\omega = v/R$. Thus, $KE_{tot} = \frac{1}{2}Mv^2 + \frac{1}{2}(\frac{2}{5}MR^2)(v/R)^2 = \frac{7}{10}Mv^2$."
"type": "MCQ_Single", },
"question_text": f"What is the most fundamental principle of {topics[idx]}?", {
"options": {"A": "Conservation of Energy", "B": "Newton's Second Law", "C": "Kinetic Theory", "D": "Thermodynamics"}, "question_text": r"A thin uniform rod of length $l$ and mass $m$ is suspended freely from one end. It is pulled aside through an angle $\theta$ and released. What is the angular velocity when it passes through the lowest point?",
"options": {"A": r"$\sqrt{\frac{3g}{l}(1-\cos\theta)}$", "B": r"$\sqrt{\frac{g}{l}(1-\cos\theta)}$", "C": r"$\sqrt{\frac{2g}{l}(1-\cos\theta)}$", "D": r"$\sqrt{\frac{6g}{l}(1-\cos\theta)}$"},
"correct_answer": {"value": "A"}, "correct_answer": {"value": "A"},
"solution_text": "By fundamental principles." "solution_text": r"By conservation of energy: $mgh_{cm} = \frac{1}{2}I\omega^2$. $h_{cm} = \frac{l}{2}(1-\cos\theta)$. $I = \frac{ml^2}{3}$. Therefore, $mg\frac{l}{2}(1-\cos\theta) = \frac{1}{2}(\frac{ml^2}{3})\omega^2 \implies \omega = \sqrt{\frac{3g}{l}(1-\cos\theta)}$."
} }
] ]
},
{
"topic": "Chemical Kinetics",
"subject": "Chemistry",
"difficulty": "Medium",
"questions": [
{
"question_text": r"For a first-order reaction $A \rightarrow P$, the rate constant is $k = 2.303 \times 10^{-3} s^{-1}$. What is the half-life ($t_{1/2}$) of this reaction?",
"options": {"A": r"$300 s$", "B": r"$301 s$", "C": r"$69.3 s$", "D": r"$0.693 s$"},
"correct_answer": {"value": "B"},
"solution_text": r"For first-order reaction, $t_{1/2} = \frac{0.693}{k}$. Substituting $k$, $t_{1/2} = \frac{0.693}{2.303 \times 10^{-3}} \approx 301 s$."
},
{
"question_text": r"If the rate of a reaction increases by a factor of $8$ when the concentration of reactant $X$ is doubled, what is the order of the reaction with respect to $X$?",
"options": {"A": "1", "B": "2", "C": "3", "D": "0"},
"correct_answer": {"value": "C"},
"solution_text": r"Rate $R \propto [X]^n$. If $[X]$ is doubled, new rate $R' = 2^n \cdot R$. Given $R' = 8R$, so $2^n = 8 \implies n = 3$."
}
]
},
{
"topic": "Genetics",
"subject": "Biology",
"difficulty": "Easy",
"questions": [
{
"question_text": r"In a Mendelian dihybrid cross, what is the phenotypic ratio of the F2 generation?",
"options": {"A": "9:3:3:1", "B": "1:2:1", "C": "3:1", "D": "1:1:1:1"},
"correct_answer": {"value": "A"},
"solution_text": r"A standard dihybrid cross between two heterozygotes ($AaBb \times AaBb$) yields offspring with phenotypes in a 9:3:3:1 ratio."
},
{
"question_text": r"Which of the following describes the condition where both alleles of a gene pair in a heterozygote are fully expressed?",
"options": {"A": "Incomplete Dominance", "B": "Codominance", "C": "Pleiotropy", "D": "Epistasis"},
"correct_answer": {"value": "B"},
"solution_text": r"Codominance is the phenomenon where both alleles are simultaneously expressed in the heterozygote, such as in AB blood type."
}
]
},
{
"topic": "Calculus",
"subject": "Mathematics",
"difficulty": "Hard",
"questions": [
{
"question_text": r"Evaluate the integral $\int_0^{\pi/2} \frac{\sqrt{\sin x}}{\sqrt{\sin x} + \sqrt{\cos x}} dx$.",
"options": {"A": r"$\pi/4$", "B": r"$\pi/2$", "C": r"$\pi$", "D": r"$0$"},
"correct_answer": {"value": "A"},
"solution_text": r"Let $I = \int_0^{\pi/2} \frac{\sqrt{\sin x}}{\sqrt{\sin x} + \sqrt{\cos x}} dx$. Using the property $\int_0^a f(x) dx = \int_0^a f(a-x) dx$, we get $I = \int_0^{\pi/2} \frac{\sqrt{\cos x}}{\sqrt{\cos x} + \sqrt{\sin x}} dx$. Adding both, $2I = \int_0^{\pi/2} 1 dx = \pi/2 \implies I = \pi/4$."
},
{
"question_text": r"Find the limit $\lim_{x \to 0} \frac{e^x - 1 - x}{x^2}$.",
"options": {"A": "1", "B": r"$1/2$", "C": "0", "D": "Does not exist"},
"correct_answer": {"value": "B"},
"solution_text": r"Using L'Hôpital's Rule twice: $\lim_{x \to 0} \frac{e^x - 1}{2x} = \lim_{x \to 0} \frac{e^x}{2} = \frac{1}{2}$."
}
]
}
]
for idx, mock in enumerate(mock_data):
question_ids = [] question_ids = []
for q_data in questions_data: for q_data in mock["questions"]:
new_q = models.Question( new_q = models.Question(
subject=q_data.get("subject", subjects[idx]), subject=mock["subject"],
topic=q_data.get("topic", topics[idx]), topic=mock["topic"],
subtopic=q_data.get("subtopic", "General"), subtopic="General",
difficulty=q_data.get("difficulty", "Medium"), difficulty=mock["difficulty"],
type=models.QuestionTypeEnum.MCQ_SINGLE, type=models.QuestionTypeEnum.MCQ_SINGLE,
question_text=q_data.get("question_text", ""), question_text=q_data["question_text"],
options=q_data.get("options", {}), options=q_data["options"],
correct_answer=q_data.get("correct_answer", {}), correct_answer=q_data["correct_answer"],
solution_text=q_data.get("solution_text", ""), solution_text=q_data["solution_text"],
language=models.MediumEnum.ENGLISH, language=models.MediumEnum.ENGLISH,
exam=models.ExamEnum.JEE_MAIN, exam=models.ExamEnum.JEE_MAIN,
created_by=admin_id created_by=admin_id
@@ -86,9 +124,8 @@ async def seed_pdfs():
session.flush() session.flush()
question_ids.append(str(new_q.id)) question_ids.append(str(new_q.id))
# Create Test record
test_obj = models.Test( test_obj = models.Test(
title=f"AI DPP: {topics[idx]}", title=f"AI DPP: {mock['topic']}",
type="DPS", type="DPS",
exam_pattern=models.ExamEnum.JEE_MAIN, exam_pattern=models.ExamEnum.JEE_MAIN,
sections={ sections={
@@ -98,7 +135,7 @@ async def seed_pdfs():
} }
}, },
total_time=60, total_time=60,
difficulty="Hard" if idx % 2 == 0 else "Medium", difficulty=mock["difficulty"],
scheduled_date=datetime.utcnow(), scheduled_date=datetime.utcnow(),
is_active=True, is_active=True,
created_by=admin_id created_by=admin_id
@@ -109,4 +146,3 @@ async def seed_pdfs():
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(seed_pdfs()) asyncio.run(seed_pdfs())

View File

@@ -113,7 +113,9 @@ export default function DPPPage() {
</Link> </Link>
) : ( ) : (
<Link href={`/cbt/${dpp.id}`}> <Link href={`/cbt/${dpp.id}`}>
<Button variant="secondary" size="icon" className="rounded-full group-hover:bg-[#00F0FF] group-hover:text-background transition-colors"><Play size={16} className="ml-1" /></Button> <Button variant="secondary" size="icon" className="rounded-full group-hover:bg-[#00F0FF] group-hover:text-black dark:group-hover:text-black hover:bg-[#00F0FF] hover:text-black dark:hover:text-black shadow-sm transition-all duration-300">
<Play size={16} className="ml-1" />
</Button>
</Link> </Link>
)} )}
</div> </div>