codeboosterstech commited on
Commit
55feae3
·
verified ·
1 Parent(s): a486fa6

Create docx_builder.py

Browse files
Files changed (1) hide show
  1. docx_builder.py +177 -0
docx_builder.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ DOCX document generation using python-docx
3
+ """
4
+
5
+ from docx import Document
6
+ from docx.shared import Inches
7
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
8
+ from typing import Dict, Any, List
9
+ import os
10
+
11
+ class DocxBuilder:
12
+ """Builds DOCX outputs for question papers, answers, and OBE reports"""
13
+
14
+ def __init__(self):
15
+ self.doc = None
16
+
17
+ def build_question_paper(self, data: Dict[str, Any], subject: str, stream: str) -> Document:
18
+ """Build question paper DOCX"""
19
+ self.doc = Document()
20
+
21
+ # Title section
22
+ title = self.doc.add_heading(f'{subject} - Question Paper', 0)
23
+ title.alignment = WD_ALIGN_PARAGRAPH.CENTER
24
+
25
+ # Metadata
26
+ self.doc.add_paragraph(f'Stream: {stream}')
27
+ self.doc.add_paragraph(f'Total Marks: {self._calculate_total_marks(data)}')
28
+ self.doc.add_paragraph('')
29
+
30
+ # Instructions
31
+ self._add_instructions(stream)
32
+
33
+ # Questions by part
34
+ self._add_part_questions(data, "A", "Part A - Answer ALL Questions")
35
+ self._add_part_questions(data, "B", "Part B - Answer ALL Questions (Either/Or)")
36
+ self._add_part_questions(data, "C", "Part C - Case Study (Answer ALL Questions)")
37
+
38
+ return self.doc
39
+
40
+ def build_answer_key(self, data: Dict[str, Any], subject: str) -> Document:
41
+ """Build answer key DOCX"""
42
+ self.doc = Document()
43
+
44
+ title = self.doc.add_heading(f'{subject} - Model Answer Key', 0)
45
+ title.alignment = WD_ALIGN_PARAGRAPH.CENTER
46
+
47
+ if "answers" in data:
48
+ for answer in data["answers"]:
49
+ self.doc.add_heading(f'Question {answer["question_ref"]}', level=2)
50
+ self.doc.add_paragraph(f'Model Answer: {answer.get("model_answer", "N/A")}')
51
+
52
+ if "marking_scheme" in answer:
53
+ self.doc.add_paragraph("Marking Scheme:")
54
+ for point in answer["marking_scheme"]:
55
+ self.doc.add_paragraph(point, style='List Bullet')
56
+
57
+ self.doc.add_paragraph('')
58
+
59
+ return self.doc
60
+
61
+ def build_obe_summary(self, data: Dict[str, Any], subject: str) -> Document:
62
+ """Build OBE summary report DOCX"""
63
+ self.doc = Document()
64
+
65
+ title = self.doc.add_heading(f'{subject} - OBE Summary Report', 0)
66
+ title.alignment = WD_ALIGN_PARAGRAPH.CENTER
67
+
68
+ if "obe" in data:
69
+ obe_data = data["obe"]
70
+
71
+ # Course Outcomes
72
+ self.doc.add_heading('Course Outcome Mapping', level=2)
73
+ if "course_outcomes" in obe_data:
74
+ for co, info in obe_data["course_outcomes"].items():
75
+ self.doc.add_paragraph(f'{co}: {info.get("coverage", "N/A")} coverage')
76
+ if "questions" in info:
77
+ self.doc.add_paragraph(f'Questions: {", ".join(info["questions"])}')
78
+
79
+ # Bloom's Distribution
80
+ self.doc.add_heading("Bloom's Taxonomy Distribution", level=2)
81
+ if "bloom_distribution" in obe_data:
82
+ for level, percentage in obe_data["bloom_distribution"].items():
83
+ self.doc.add_paragraph(f'{level}: {percentage}')
84
+
85
+ # Analysis
86
+ self.doc.add_heading('Quality Analysis', level=2)
87
+ self.doc.add_paragraph(f'Difficulty Index: {obe_data.get("difficulty_index", "N/A")}')
88
+ self.doc.add_paragraph(f'Unit Coverage: {obe_data.get("unit_coverage", "N/A")}')
89
+
90
+ if "recommendations" in obe_data:
91
+ self.doc.add_paragraph(f'Recommendations: {obe_data["recommendations"]}')
92
+
93
+ return self.doc
94
+
95
+ def _calculate_total_marks(self, data: Dict[str, Any]) -> int:
96
+ """Calculate total marks from questions"""
97
+ total = 0
98
+ if "final_qp" in data and "questions" in data["final_qp"]:
99
+ for question in data["final_qp"]["questions"]:
100
+ total += question.get("marks", 0)
101
+ return total
102
+
103
+ def _add_instructions(self, stream: str):
104
+ """Add stream-specific instructions"""
105
+ instructions = self.doc.add_paragraph('INSTRUCTIONS:')
106
+ instructions.bold = True
107
+
108
+ if stream == "CSE":
109
+ self.doc.add_paragraph('• Part A: Answer ALL questions (2 marks each)')
110
+ self.doc.add_paragraph('• Part B: Answer ALL questions (13 marks each) - Choose either (a) or (b)')
111
+ self.doc.add_paragraph('• Part C: Case study (14 marks) - Answer ALL sub-questions')
112
+ self.doc.add_paragraph('• Questions are tagged with relevant company standards (MAANGO BIG15)')
113
+ else:
114
+ self.doc.add_paragraph('• Part A: Answer ALL questions (2 marks each)')
115
+ self.doc.add_paragraph('• Part B: Answer ALL questions (13 marks each) - Choose either (a) or (b)')
116
+ self.doc.add_paragraph('• Part C: Case study (14 marks) - Answer ALL sub-questions')
117
+ self.doc.add_paragraph('• Questions follow GATE examination patterns')
118
+
119
+ self.doc.add_paragraph('')
120
+
121
+ def _add_part_questions(self, data: Dict[str, Any], part: str, heading: str):
122
+ """Add questions for specific part"""
123
+ self.doc.add_heading(heading, level=1)
124
+
125
+ if "final_qp" in data and "questions" in data["final_qp"]:
126
+ part_questions = [q for q in data["final_qp"]["questions"] if q.get("part") == part]
127
+
128
+ for question in part_questions:
129
+ # Question header
130
+ q_header = f'Q{question["question_no"]}'
131
+ if question.get("sub_no"):
132
+ q_header += f'({question["sub_no"]})'
133
+ q_header += f' [{question["marks"]} marks]'
134
+
135
+ self.doc.add_paragraph(q_header).bold = True
136
+
137
+ # Question text
138
+ self.doc.add_paragraph(question["question_text"])
139
+
140
+ # Metadata
141
+ meta = self.doc.add_paragraph()
142
+ meta.add_run(f'Unit: {question["unit"]} | ')
143
+ meta.add_run(f'Bloom: {question["bloom_level"]} | ')
144
+ meta.add_run(f'CO: {question["course_outcome"]} | ')
145
+ meta.add_run(f'Tags: {", ".join(question["tags"])}')
146
+ meta.style.font.italic = True
147
+
148
+ self.doc.add_paragraph('')
149
+
150
+ def save_document(self, doc: Document, filename: str):
151
+ """Save document to file"""
152
+ doc.save(filename)
153
+
154
+ def generate_all_documents(final_output: Dict[str, Any], subject: str, stream: str, output_dir: str = "."):
155
+ """Generate all three DOCX files"""
156
+ builder = DocxBuilder()
157
+
158
+ # Question Paper
159
+ qp_doc = builder.build_question_paper(final_output, subject, stream)
160
+ qp_filename = f"{output_dir}/{subject}_QuestionPaper.docx"
161
+ builder.save_document(qp_doc, qp_filename)
162
+
163
+ # Answer Key
164
+ ak_doc = builder.build_answer_key(final_output, subject)
165
+ ak_filename = f"{output_dir}/{subject}_KeyAnswers.docx"
166
+ builder.save_document(ak_doc, ak_filename)
167
+
168
+ # OBE Summary
169
+ obe_doc = builder.build_obe_summary(final_output, subject)
170
+ obe_filename = f"{output_dir}/{subject}_OBE_Summary.docx"
171
+ builder.save_document(obe_doc, obe_filename)
172
+
173
+ return {
174
+ "question_paper": qp_filename,
175
+ "answer_key": ak_filename,
176
+ "obe_summary": obe_filename
177
+ }