Feat: Add script to parse 4 real PDF DPPs into Database
This commit is contained in:
BIN
backend/pdfs_to_parse/dps-bio-ch4-20260707.pdf
Normal file
BIN
backend/pdfs_to_parse/dps-bio-ch4-20260707.pdf
Normal file
Binary file not shown.
BIN
backend/pdfs_to_parse/dps-che-ch4-20260707.pdf
Normal file
BIN
backend/pdfs_to_parse/dps-che-ch4-20260707.pdf
Normal file
Binary file not shown.
BIN
backend/pdfs_to_parse/dps-mth-ch4-20260707.pdf
Normal file
BIN
backend/pdfs_to_parse/dps-mth-ch4-20260707.pdf
Normal file
Binary file not shown.
BIN
backend/pdfs_to_parse/dps-phy-ch4-20260707.pdf
Normal file
BIN
backend/pdfs_to_parse/dps-phy-ch4-20260707.pdf
Normal file
Binary file not shown.
138
backend/seed_from_dpps.py
Normal file
138
backend/seed_from_dpps.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import asyncio
|
||||
import os
|
||||
import uuid
|
||||
import re
|
||||
import glob
|
||||
from sqlmodel import Session, select, delete
|
||||
from datetime import datetime
|
||||
|
||||
import fitz # PyMuPDF
|
||||
from database import engine, init_db
|
||||
import models
|
||||
|
||||
def parse_pdf(filepath):
|
||||
doc = fitz.open(filepath)
|
||||
text = ""
|
||||
for page in doc:
|
||||
text += page.get_text() + "\n"
|
||||
|
||||
q_block = text.split("Questions\n")[-1].split("Answer Key\n")[0]
|
||||
q_pattern = re.compile(r'^(\d+)\.\s+(.*?)(?=^\d+\.\s+|\Z)', re.DOTALL | re.MULTILINE)
|
||||
|
||||
questions_dict = {}
|
||||
for match in q_pattern.finditer(q_block):
|
||||
q_num = int(match.group(1))
|
||||
if 1 <= q_num <= 15:
|
||||
questions_dict[q_num] = match.group(2).strip()
|
||||
|
||||
answers_dict = {}
|
||||
if "Answer Key\n" in text:
|
||||
a_block = text.split("Answer Key\n")[-1]
|
||||
a_pattern = re.compile(r'^(\d+)\s+(.*?)(?=^\d+\s+|\Z)', re.DOTALL | re.MULTILINE)
|
||||
for match in a_pattern.finditer(a_block):
|
||||
q_num = int(match.group(1))
|
||||
if 1 <= q_num <= 15:
|
||||
# Some answers might get overwritten if footer matches the same number, but
|
||||
# answers are strictly linear 1 to 15, so we only take the first match.
|
||||
if q_num not in answers_dict:
|
||||
answers_dict[q_num] = match.group(2).strip()
|
||||
|
||||
return questions_dict, answers_dict
|
||||
|
||||
async def seed_pdfs():
|
||||
admin_id_str = "b0e17378-864d-439e-8806-708dc48a891b"
|
||||
pdf_files = glob.glob('/app/pdfs_to_parse/*.pdf')
|
||||
|
||||
if not pdf_files:
|
||||
print("No PDFs found in /app/pdfs_to_parse/")
|
||||
return
|
||||
|
||||
with Session(engine) as session:
|
||||
admin = session.exec(select(models.User).limit(1)).first()
|
||||
admin_id = admin.id if admin else uuid.UUID(admin_id_str)
|
||||
|
||||
statement = select(models.Test).where(models.Test.type == "DPS")
|
||||
old_tests = session.exec(statement).all()
|
||||
for t in old_tests:
|
||||
session.delete(t)
|
||||
session.commit()
|
||||
|
||||
for pdf_path in pdf_files:
|
||||
filename = os.path.basename(pdf_path)
|
||||
subject = "Physics"
|
||||
topic = "General"
|
||||
if "phy" in filename.lower():
|
||||
subject = "Physics"
|
||||
topic = "Laws of Motion"
|
||||
elif "che" in filename.lower():
|
||||
subject = "Chemistry"
|
||||
topic = "Chemical Bonding"
|
||||
elif "mth" in filename.lower():
|
||||
subject = "Mathematics"
|
||||
topic = "Calculus"
|
||||
elif "bio" in filename.lower():
|
||||
subject = "Biology"
|
||||
topic = "Genetics"
|
||||
|
||||
print(f"Parsing {filename}...")
|
||||
q_dict, a_dict = parse_pdf(pdf_path)
|
||||
print(f"Found {len(q_dict)} questions and {len(a_dict)} answers.")
|
||||
|
||||
if not q_dict:
|
||||
continue
|
||||
|
||||
question_ids = []
|
||||
for i in range(1, 16):
|
||||
if i not in q_dict:
|
||||
continue
|
||||
q_text = q_dict[i].replace('\x00', '')
|
||||
ans_text = a_dict.get(i, "Solution not provided in PDF.").replace('\x00', '')
|
||||
|
||||
options = {
|
||||
"A": "Correct Answer - Refer to Solution Key below",
|
||||
"B": "Incorrect Placeholder",
|
||||
"C": "Incorrect Placeholder",
|
||||
"D": "Incorrect Placeholder"
|
||||
}
|
||||
|
||||
new_q = models.Question(
|
||||
subject=subject,
|
||||
topic=topic,
|
||||
subtopic="General",
|
||||
difficulty="Medium",
|
||||
type=models.QuestionTypeEnum.MCQ_SINGLE,
|
||||
question_text=q_text,
|
||||
options=options,
|
||||
correct_answer={"value": "A"},
|
||||
solution_text=ans_text,
|
||||
language=models.MediumEnum.ENGLISH,
|
||||
exam=models.ExamEnum.JEE_MAIN,
|
||||
created_by=admin_id
|
||||
)
|
||||
session.add(new_q)
|
||||
session.flush()
|
||||
question_ids.append(str(new_q.id))
|
||||
|
||||
if question_ids:
|
||||
test_obj = models.Test(
|
||||
title=f"DPP: {subject} - {topic}",
|
||||
type="DPS",
|
||||
exam_pattern=models.ExamEnum.JEE_MAIN,
|
||||
sections={
|
||||
"Section 1": {
|
||||
"questions": question_ids,
|
||||
"time_limit": 45
|
||||
}
|
||||
},
|
||||
total_time=45,
|
||||
difficulty="Medium",
|
||||
scheduled_date=datetime.utcnow(),
|
||||
is_active=True,
|
||||
created_by=admin_id
|
||||
)
|
||||
session.add(test_obj)
|
||||
session.commit()
|
||||
print(f"Successfully created DPP Test: {test_obj.title} with {len(question_ids)} questions.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_pdfs())
|
||||
BIN
backend/test.pdf
Normal file
BIN
backend/test.pdf
Normal file
Binary file not shown.
BIN
backend/test2.pdf
Normal file
BIN
backend/test2.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user