SNS / docx_builder.py
codeboosterstech's picture
Create docx_builder.py
55feae3 verified
raw
history blame
7.56 kB
"""
DOCX document generation using python-docx
"""
from docx import Document
from docx.shared import Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
from typing import Dict, Any, List
import os
class DocxBuilder:
"""Builds DOCX outputs for question papers, answers, and OBE reports"""
def __init__(self):
self.doc = None
def build_question_paper(self, data: Dict[str, Any], subject: str, stream: str) -> Document:
"""Build question paper DOCX"""
self.doc = Document()
# Title section
title = self.doc.add_heading(f'{subject} - Question Paper', 0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Metadata
self.doc.add_paragraph(f'Stream: {stream}')
self.doc.add_paragraph(f'Total Marks: {self._calculate_total_marks(data)}')
self.doc.add_paragraph('')
# Instructions
self._add_instructions(stream)
# Questions by part
self._add_part_questions(data, "A", "Part A - Answer ALL Questions")
self._add_part_questions(data, "B", "Part B - Answer ALL Questions (Either/Or)")
self._add_part_questions(data, "C", "Part C - Case Study (Answer ALL Questions)")
return self.doc
def build_answer_key(self, data: Dict[str, Any], subject: str) -> Document:
"""Build answer key DOCX"""
self.doc = Document()
title = self.doc.add_heading(f'{subject} - Model Answer Key', 0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
if "answers" in data:
for answer in data["answers"]:
self.doc.add_heading(f'Question {answer["question_ref"]}', level=2)
self.doc.add_paragraph(f'Model Answer: {answer.get("model_answer", "N/A")}')
if "marking_scheme" in answer:
self.doc.add_paragraph("Marking Scheme:")
for point in answer["marking_scheme"]:
self.doc.add_paragraph(point, style='List Bullet')
self.doc.add_paragraph('')
return self.doc
def build_obe_summary(self, data: Dict[str, Any], subject: str) -> Document:
"""Build OBE summary report DOCX"""
self.doc = Document()
title = self.doc.add_heading(f'{subject} - OBE Summary Report', 0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
if "obe" in data:
obe_data = data["obe"]
# Course Outcomes
self.doc.add_heading('Course Outcome Mapping', level=2)
if "course_outcomes" in obe_data:
for co, info in obe_data["course_outcomes"].items():
self.doc.add_paragraph(f'{co}: {info.get("coverage", "N/A")} coverage')
if "questions" in info:
self.doc.add_paragraph(f'Questions: {", ".join(info["questions"])}')
# Bloom's Distribution
self.doc.add_heading("Bloom's Taxonomy Distribution", level=2)
if "bloom_distribution" in obe_data:
for level, percentage in obe_data["bloom_distribution"].items():
self.doc.add_paragraph(f'{level}: {percentage}')
# Analysis
self.doc.add_heading('Quality Analysis', level=2)
self.doc.add_paragraph(f'Difficulty Index: {obe_data.get("difficulty_index", "N/A")}')
self.doc.add_paragraph(f'Unit Coverage: {obe_data.get("unit_coverage", "N/A")}')
if "recommendations" in obe_data:
self.doc.add_paragraph(f'Recommendations: {obe_data["recommendations"]}')
return self.doc
def _calculate_total_marks(self, data: Dict[str, Any]) -> int:
"""Calculate total marks from questions"""
total = 0
if "final_qp" in data and "questions" in data["final_qp"]:
for question in data["final_qp"]["questions"]:
total += question.get("marks", 0)
return total
def _add_instructions(self, stream: str):
"""Add stream-specific instructions"""
instructions = self.doc.add_paragraph('INSTRUCTIONS:')
instructions.bold = True
if stream == "CSE":
self.doc.add_paragraph('• Part A: Answer ALL questions (2 marks each)')
self.doc.add_paragraph('• Part B: Answer ALL questions (13 marks each) - Choose either (a) or (b)')
self.doc.add_paragraph('• Part C: Case study (14 marks) - Answer ALL sub-questions')
self.doc.add_paragraph('• Questions are tagged with relevant company standards (MAANGO BIG15)')
else:
self.doc.add_paragraph('• Part A: Answer ALL questions (2 marks each)')
self.doc.add_paragraph('• Part B: Answer ALL questions (13 marks each) - Choose either (a) or (b)')
self.doc.add_paragraph('• Part C: Case study (14 marks) - Answer ALL sub-questions')
self.doc.add_paragraph('• Questions follow GATE examination patterns')
self.doc.add_paragraph('')
def _add_part_questions(self, data: Dict[str, Any], part: str, heading: str):
"""Add questions for specific part"""
self.doc.add_heading(heading, level=1)
if "final_qp" in data and "questions" in data["final_qp"]:
part_questions = [q for q in data["final_qp"]["questions"] if q.get("part") == part]
for question in part_questions:
# Question header
q_header = f'Q{question["question_no"]}'
if question.get("sub_no"):
q_header += f'({question["sub_no"]})'
q_header += f' [{question["marks"]} marks]'
self.doc.add_paragraph(q_header).bold = True
# Question text
self.doc.add_paragraph(question["question_text"])
# Metadata
meta = self.doc.add_paragraph()
meta.add_run(f'Unit: {question["unit"]} | ')
meta.add_run(f'Bloom: {question["bloom_level"]} | ')
meta.add_run(f'CO: {question["course_outcome"]} | ')
meta.add_run(f'Tags: {", ".join(question["tags"])}')
meta.style.font.italic = True
self.doc.add_paragraph('')
def save_document(self, doc: Document, filename: str):
"""Save document to file"""
doc.save(filename)
def generate_all_documents(final_output: Dict[str, Any], subject: str, stream: str, output_dir: str = "."):
"""Generate all three DOCX files"""
builder = DocxBuilder()
# Question Paper
qp_doc = builder.build_question_paper(final_output, subject, stream)
qp_filename = f"{output_dir}/{subject}_QuestionPaper.docx"
builder.save_document(qp_doc, qp_filename)
# Answer Key
ak_doc = builder.build_answer_key(final_output, subject)
ak_filename = f"{output_dir}/{subject}_KeyAnswers.docx"
builder.save_document(ak_doc, ak_filename)
# OBE Summary
obe_doc = builder.build_obe_summary(final_output, subject)
obe_filename = f"{output_dir}/{subject}_OBE_Summary.docx"
builder.save_document(obe_doc, obe_filename)
return {
"question_paper": qp_filename,
"answer_key": ak_filename,
"obe_summary": obe_filename
}