Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# app.py - Enhanced
|
| 2 |
# Aligned with Pakistani National Curriculum (2006)
|
| 3 |
# Developer: Najaf Ali Sharqi
|
| 4 |
|
|
@@ -9,6 +9,8 @@ from datetime import datetime
|
|
| 9 |
from docx import Document
|
| 10 |
from docx.shared import Pt, RGBColor, Inches
|
| 11 |
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
|
|
|
|
|
| 12 |
import re
|
| 13 |
import json
|
| 14 |
|
|
@@ -23,8 +25,7 @@ client = Groq(api_key=GROQ_API_KEY)
|
|
| 23 |
# ---------------------------
|
| 24 |
# Configuration Constants
|
| 25 |
# ---------------------------
|
| 26 |
-
APP_VERSION = "2.
|
| 27 |
-
MAX_MCQS = 20
|
| 28 |
MODEL_NAME = "llama-3.3-70b-versatile"
|
| 29 |
|
| 30 |
# ---------------------------
|
|
@@ -51,6 +52,228 @@ subjects_by_grade = {
|
|
| 51 |
# Chapter mapping (National Curriculum 2006)
|
| 52 |
# ---------------------------
|
| 53 |
chapters_by_subject_and_grade = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
"Grade 1": {
|
| 55 |
"Mathematics": ["Counting & Numbers", "Basic Addition", "Basic Subtraction", "Shapes", "Measurements"],
|
| 56 |
"English": ["Alphabet & Sounds", "Basic Words", "Simple Sentences", "Listening & Speaking", "Reading Short Texts"],
|
|
@@ -319,61 +542,51 @@ chapters_by_subject_and_grade = {
|
|
| 319 |
}
|
| 320 |
|
| 321 |
# ---------------------------
|
| 322 |
-
#
|
| 323 |
-
# ---------------------------
|
| 324 |
-
QUESTION_TYPES = {
|
| 325 |
-
"Multiple Choice Questions (MCQs)": {
|
| 326 |
-
"code": "mcq",
|
| 327 |
-
"description": "Four-option questions testing recall and application",
|
| 328 |
-
"format": "Question with options A, B, C, D"
|
| 329 |
-
},
|
| 330 |
-
"Fill in the Blanks": {
|
| 331 |
-
"code": "fill_blank",
|
| 332 |
-
"description": "Complete sentences with missing key terms",
|
| 333 |
-
"format": "Sentences with underlined blanks"
|
| 334 |
-
},
|
| 335 |
-
"Match the Column": {
|
| 336 |
-
"code": "match_column",
|
| 337 |
-
"description": "Connect related items from two columns",
|
| 338 |
-
"format": "Two columns with items to match"
|
| 339 |
-
},
|
| 340 |
-
"Short Response Questions": {
|
| 341 |
-
"code": "short_response",
|
| 342 |
-
"description": "Brief written answers (2-3 sentences)",
|
| 343 |
-
"format": "Questions requiring concise explanations"
|
| 344 |
-
},
|
| 345 |
-
"Essay Type Questions": {
|
| 346 |
-
"code": "essay",
|
| 347 |
-
"description": "Extended written responses with detailed analysis",
|
| 348 |
-
"format": "Open-ended questions requiring paragraphs"
|
| 349 |
-
}
|
| 350 |
-
}
|
| 351 |
-
|
| 352 |
-
# ---------------------------
|
| 353 |
-
# Cognitive levels for item writing (Bloom's Taxonomy adapted)
|
| 354 |
# ---------------------------
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
|
| 378 |
# ---------------------------
|
| 379 |
# Enhanced question generation with proper item writing principles
|
|
@@ -642,8 +855,9 @@ def parse_match_column(text):
|
|
| 642 |
return questions, answers
|
| 643 |
|
| 644 |
def parse_short_response(text, expected_count):
|
| 645 |
-
"""Parse Short Response questions"""
|
| 646 |
-
|
|
|
|
| 647 |
|
| 648 |
questions = []
|
| 649 |
answers = []
|
|
@@ -656,18 +870,19 @@ def parse_short_response(text, expected_count):
|
|
| 656 |
|
| 657 |
if len(parts) >= 2:
|
| 658 |
a_text = parts[1]
|
| 659 |
-
# Extract
|
| 660 |
schemes = re.findall(r'\d+[\.\)]\s*(.+?)(?=\d+[\.\)]|$)', a_text, re.DOTALL)
|
| 661 |
answers = [s.strip() for s in schemes[:expected_count]]
|
| 662 |
|
| 663 |
if len(answers) < len(questions):
|
| 664 |
-
answers.extend(['Key points
|
| 665 |
|
| 666 |
return questions, answers
|
| 667 |
|
| 668 |
def parse_essay(text, expected_count):
|
| 669 |
-
"""Parse Essay questions"""
|
| 670 |
-
|
|
|
|
| 671 |
|
| 672 |
questions = []
|
| 673 |
answers = []
|
|
@@ -680,15 +895,31 @@ def parse_essay(text, expected_count):
|
|
| 680 |
|
| 681 |
if len(parts) >= 2:
|
| 682 |
a_text = parts[1]
|
| 683 |
-
# Extract criteria
|
| 684 |
criteria = re.findall(r'\d+[\.\)]\s*(.+?)(?=\d+[\.\)]|$)', a_text, re.DOTALL)
|
| 685 |
answers = [c.strip() for c in criteria[:expected_count]]
|
| 686 |
|
| 687 |
if len(answers) < len(questions):
|
| 688 |
-
answers.extend(['
|
| 689 |
|
| 690 |
return questions, answers
|
| 691 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
# ---------------------------
|
| 693 |
# Enhanced DOCX export supporting all question types
|
| 694 |
# ---------------------------
|
|
@@ -785,9 +1016,9 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 785 |
if question_type_code == "mcq":
|
| 786 |
for i, q in enumerate(questions, start=1):
|
| 787 |
q_para = doc.add_paragraph()
|
| 788 |
-
q_para.add_run(f"
|
| 789 |
q_para.add_run(q)
|
| 790 |
-
q_para.space_after = Pt(
|
| 791 |
doc.add_paragraph()
|
| 792 |
|
| 793 |
elif question_type_code == "fill_blank":
|
|
@@ -796,6 +1027,7 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 796 |
q_para.add_run(f"{i}. ").bold = True
|
| 797 |
q_para.add_run(q)
|
| 798 |
q_para.space_after = Pt(8)
|
|
|
|
| 799 |
|
| 800 |
elif question_type_code == "match_column":
|
| 801 |
for q in questions:
|
|
@@ -811,13 +1043,13 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 811 |
doc.add_paragraph()
|
| 812 |
# Add space for answer
|
| 813 |
for _ in range(3 if question_type_code == "short_response" else 8):
|
| 814 |
-
doc.add_paragraph("_" *
|
| 815 |
doc.add_paragraph()
|
| 816 |
|
| 817 |
# Answer key (on separate page)
|
| 818 |
doc.add_page_break()
|
| 819 |
|
| 820 |
-
ans_heading = doc.add_heading("ANSWER KEY
|
| 821 |
ans_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 822 |
for run in ans_heading.runs:
|
| 823 |
run.font.color.rgb = RGBColor(153, 0, 0)
|
|
@@ -828,9 +1060,7 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 828 |
# Add answers based on type
|
| 829 |
if question_type_code == "mcq":
|
| 830 |
# Answer table for MCQs
|
| 831 |
-
|
| 832 |
-
ans_table = doc.add_table(rows=num_rows, cols=5)
|
| 833 |
-
ans_table.style = 'Medium Grid 1 Accent 1'
|
| 834 |
|
| 835 |
headers = ["Q#", "A", "B", "C", "D"]
|
| 836 |
for idx, header in enumerate(headers):
|
|
@@ -853,7 +1083,33 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 853 |
else:
|
| 854 |
cell.text = ""
|
| 855 |
cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 856 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 857 |
else:
|
| 858 |
# Text-based answers for other types
|
| 859 |
for i, ans in enumerate(answers, start=1):
|
|
@@ -865,7 +1121,7 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 865 |
# Footer
|
| 866 |
doc.add_paragraph()
|
| 867 |
footer = doc.add_paragraph()
|
| 868 |
-
footer.add_run(f"Generated by
|
| 869 |
footer.runs[0].font.size = Pt(8)
|
| 870 |
footer.runs[0].font.color.rgb = RGBColor(128, 128, 128)
|
| 871 |
footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
@@ -883,31 +1139,61 @@ def export_to_word(questions, answers, grade, subject, chapter, difficulty, test
|
|
| 883 |
return None, f"Error creating document: {str(e)}"
|
| 884 |
|
| 885 |
# ---------------------------
|
| 886 |
-
# Main generation handler
|
| 887 |
# ---------------------------
|
| 888 |
-
def on_generate(grade, subject, chapter,
|
| 889 |
-
|
|
|
|
|
|
|
| 890 |
|
| 891 |
# Validation
|
| 892 |
-
if not all([grade, subject, chapter,
|
| 893 |
return "⚠️ Please fill all required fields", None, None
|
| 894 |
|
| 895 |
if not school_name or not school_name.strip():
|
| 896 |
school_name = "Educational Institution"
|
| 897 |
|
| 898 |
try:
|
| 899 |
-
#
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
-
|
| 910 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 911 |
|
| 912 |
if error:
|
| 913 |
return f"❌ Error: {error}", None, None
|
|
@@ -934,7 +1220,8 @@ def on_generate(grade, subject, chapter, difficulty, num_questions, test_type, s
|
|
| 934 |
# Export to Word
|
| 935 |
file_path, doc_error = export_to_word(
|
| 936 |
questions, answers, grade, subject, chapter,
|
| 937 |
-
|
|
|
|
| 938 |
)
|
| 939 |
|
| 940 |
if doc_error:
|
|
@@ -942,15 +1229,20 @@ def on_generate(grade, subject, chapter, difficulty, num_questions, test_type, s
|
|
| 942 |
|
| 943 |
# Success message
|
| 944 |
success_msg = f"✅ Test generated successfully!\n\n"
|
| 945 |
-
success_msg += f"📊 Statistics:\n"
|
| 946 |
success_msg += f"• Question Type: {question_type}\n"
|
| 947 |
success_msg += f"• Total Questions: {len(questions)}\n"
|
| 948 |
success_msg += f"• Total Marks: {total_marks}\n"
|
| 949 |
-
success_msg += f"• Grade
|
| 950 |
-
success_msg += f"• Subject: {subject}\n"
|
| 951 |
success_msg += f"• Chapter: {chapter}\n"
|
| 952 |
-
|
| 953 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 954 |
|
| 955 |
# Preview
|
| 956 |
preview_count = min(3, len(questions))
|
|
@@ -958,7 +1250,7 @@ def on_generate(grade, subject, chapter, difficulty, num_questions, test_type, s
|
|
| 958 |
|
| 959 |
for i, q in enumerate(questions[:preview_count], 1):
|
| 960 |
if q_type_code == "mcq":
|
| 961 |
-
preview += f"
|
| 962 |
else:
|
| 963 |
preview += f"{i}. {q[:200]}...\n\n" if len(q) > 200 else f"{i}. {q}\n\n"
|
| 964 |
|
|
@@ -989,16 +1281,18 @@ def create_ui():
|
|
| 989 |
# Header
|
| 990 |
gr.Markdown(
|
| 991 |
"""
|
| 992 |
-
# 📚
|
| 993 |
-
###
|
|
|
|
|
|
|
| 994 |
""",
|
| 995 |
elem_classes="header"
|
| 996 |
)
|
| 997 |
|
| 998 |
gr.Markdown(
|
| 999 |
"""
|
| 1000 |
-
Generate high-quality
|
| 1001 |
-
|
| 1002 |
""",
|
| 1003 |
elem_classes="description"
|
| 1004 |
)
|
|
@@ -1073,20 +1367,66 @@ def create_ui():
|
|
| 1073 |
info="Select specific chapter or unit"
|
| 1074 |
)
|
| 1075 |
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
info="
|
| 1081 |
)
|
| 1082 |
|
| 1083 |
-
|
| 1084 |
-
|
| 1085 |
-
|
| 1086 |
-
|
|
|
|
| 1087 |
|
| 1088 |
-
|
| 1089 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1090 |
visible=True
|
| 1091 |
)
|
| 1092 |
|
|
@@ -1114,8 +1454,16 @@ def create_ui():
|
|
| 1114 |
- **Match the Column**: Assesses relationships and connections
|
| 1115 |
- **Short Response**: Evaluates understanding and explanation
|
| 1116 |
- **Essay Questions**: Measures critical thinking and synthesis
|
| 1117 |
-
-
|
| 1118 |
- Review generated questions before final use
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1119 |
"""
|
| 1120 |
)
|
| 1121 |
|
|
@@ -1158,12 +1506,12 @@ def create_ui():
|
|
| 1158 |
"""
|
| 1159 |
---
|
| 1160 |
<div class="footer">
|
| 1161 |
-
<strong>
|
| 1162 |
-
Comprehensive Assessment Tool with 5 Question Types<br>
|
| 1163 |
Aligned with Pakistani National Curriculum (2006)<br>
|
| 1164 |
Developer: <strong>Najaf Ali Sharqi</strong><br>
|
| 1165 |
-
Based on
|
| 1166 |
-
📧 For
|
| 1167 |
</div>
|
| 1168 |
""",
|
| 1169 |
elem_classes="footer"
|
|
@@ -1184,19 +1532,6 @@ def create_ui():
|
|
| 1184 |
return gr.update(choices=chapters, value=None)
|
| 1185 |
return gr.update(choices=[], value=None)
|
| 1186 |
|
| 1187 |
-
def update_difficulty_info(difficulty):
|
| 1188 |
-
"""Update difficulty level description"""
|
| 1189 |
-
if difficulty in DIFFICULTY_LEVELS:
|
| 1190 |
-
info = DIFFICULTY_LEVELS[difficulty]
|
| 1191 |
-
desc = info.get('description', '')
|
| 1192 |
-
keywords = ', '.join(info.get('keywords', []))
|
| 1193 |
-
return f"""
|
| 1194 |
-
**Level Description:** {desc}
|
| 1195 |
-
|
| 1196 |
-
**Keywords:** {keywords}
|
| 1197 |
-
"""
|
| 1198 |
-
return ""
|
| 1199 |
-
|
| 1200 |
def update_question_type_info(q_type):
|
| 1201 |
"""Update question type description"""
|
| 1202 |
if q_type in QUESTION_TYPES:
|
|
@@ -1210,6 +1545,16 @@ def create_ui():
|
|
| 1210 |
"""
|
| 1211 |
return ""
|
| 1212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1213 |
# Connect event handlers
|
| 1214 |
grade_dropdown.change(
|
| 1215 |
update_subjects,
|
|
@@ -1229,29 +1574,43 @@ def create_ui():
|
|
| 1229 |
outputs=[chapter_dropdown]
|
| 1230 |
)
|
| 1231 |
|
| 1232 |
-
difficulty_dropdown.change(
|
| 1233 |
-
update_difficulty_info,
|
| 1234 |
-
inputs=[difficulty_dropdown],
|
| 1235 |
-
outputs=[difficulty_info]
|
| 1236 |
-
)
|
| 1237 |
-
|
| 1238 |
question_type.change(
|
| 1239 |
update_question_type_info,
|
| 1240 |
inputs=[question_type],
|
| 1241 |
outputs=[question_type_info]
|
| 1242 |
)
|
| 1243 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1244 |
generate_btn.click(
|
| 1245 |
on_generate,
|
| 1246 |
inputs=[
|
| 1247 |
grade_dropdown,
|
| 1248 |
subject_dropdown,
|
| 1249 |
chapter_dropdown,
|
| 1250 |
-
difficulty_dropdown,
|
| 1251 |
-
num_questions,
|
| 1252 |
test_type,
|
| 1253 |
school_name,
|
| 1254 |
-
question_type
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1255 |
],
|
| 1256 |
outputs=[output_text, output_file, file_status]
|
| 1257 |
)
|
|
|
|
| 1 |
+
# app.py - Enhanced Test Generator for Hugging Face
|
| 2 |
# Aligned with Pakistani National Curriculum (2006)
|
| 3 |
# Developer: Najaf Ali Sharqi
|
| 4 |
|
|
|
|
| 9 |
from docx import Document
|
| 10 |
from docx.shared import Pt, RGBColor, Inches
|
| 11 |
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
| 12 |
+
from docx.oxml.ns import qn
|
| 13 |
+
from docx.oxml import OxmlElement
|
| 14 |
import re
|
| 15 |
import json
|
| 16 |
|
|
|
|
| 25 |
# ---------------------------
|
| 26 |
# Configuration Constants
|
| 27 |
# ---------------------------
|
| 28 |
+
APP_VERSION = "2.5"
|
|
|
|
| 29 |
MODEL_NAME = "llama-3.3-70b-versatile"
|
| 30 |
|
| 31 |
# ---------------------------
|
|
|
|
| 52 |
# Chapter mapping (National Curriculum 2006)
|
| 53 |
# ---------------------------
|
| 54 |
chapters_by_subject_and_grade = {
|
| 55 |
+
"Grade 1": {
|
| 56 |
+
"Mathematics": ["Counting & Numbers", "Basic Addition", "Basic Subtraction", "Shapes", "Measurements"],
|
| 57 |
+
"English": ["Alphabet & Sounds", "Basic Words", "Simple Sentences", "Listening & Speaking", "Reading Short Texts"],
|
| 58 |
+
"Urdu": ["حروف", "الفاظ", "سادہ جملے", "پڑھنا", "لکھنا"],
|
| 59 |
+
"Islamiyat": ["Basic Beliefs", "Prophets Stories", "Good Manners", "Prayers & Worship"],
|
| 60 |
+
"General Knowledge": ["My Body", "My Home", "My School", "Seasons"]
|
| 61 |
+
},
|
| 62 |
+
"Grade 2": {
|
| 63 |
+
"Mathematics": ["Place Value", "Addition & Subtraction", "Money", "Time", "Shapes"],
|
| 64 |
+
"English": ["Parts of Speech Basics", "Simple Grammar", "Short Comprehension", "Vocabulary Building"],
|
| 65 |
+
"Urdu": ["الفاظ و جملے", "مختصر کہانیاں", "حروفِ صحیح"],
|
| 66 |
+
"Islamiyat": ["Prophets", "Good Deeds", "Mosque Etiquette"],
|
| 67 |
+
"General Knowledge": ["Plants & Animals", "Local Community", "Transport"]
|
| 68 |
+
},
|
| 69 |
+
"Grade 3": {
|
| 70 |
+
"Mathematics": ["Multiplication", "Division", "Fractions", "Geometry Basics", "Measurement"],
|
| 71 |
+
"English": ["Tenses (simple)", "Comprehension Passages", "Vocabulary", "Sentence Formation"],
|
| 72 |
+
"Urdu": ["کالم و مضامین", "قواعدِ اردو", "کہانیاں"],
|
| 73 |
+
"Science": ["Plants", "Animals", "Human Body", "Materials"],
|
| 74 |
+
"Islamiyat": ["Basics of Iman", "Seerat", "Manners"],
|
| 75 |
+
"General Knowledge": ["Environment", "Community Helpers", "Safety"]
|
| 76 |
+
},
|
| 77 |
+
"Grade 4": {
|
| 78 |
+
"Mathematics": ["Fractions & Decimals", "Geometry", "Data Handling", "Word Problems"],
|
| 79 |
+
"English": ["Grammar (intermediate)", "Comprehension", "Writing Short Paragraphs"],
|
| 80 |
+
"Urdu": ["نظم و نثر", "قواعد", "مفردات"],
|
| 81 |
+
"Science": ["Ecosystems", "Forces", "Heat & Energy"],
|
| 82 |
+
"Islamiyat": ["Pillars of Islam", "Seerah Stories"],
|
| 83 |
+
"General Knowledge": ["Maps & Directions", "Cultural Heritage", "Technology"]
|
| 84 |
+
},
|
| 85 |
+
"Grade 5": {
|
| 86 |
+
"Mathematics": ["Numbers & Operations", "Geometry", "Statistics", "Ratio & Proportion"],
|
| 87 |
+
"English": ["Advanced Grammar Basics", "Paragraph Writing", "Comprehension"],
|
| 88 |
+
"Urdu": ["ادبی مطالعہ", "تعبیر و تشریح"],
|
| 89 |
+
"Science": ["Cells & Life", "Matter", "Energy"],
|
| 90 |
+
"Islamiyat": ["Quranic Stories", "Akhlaq"],
|
| 91 |
+
"Social Studies": ["Pakistan Geography", "History Basics", "Civics Introduction"]
|
| 92 |
+
},
|
| 93 |
+
"Grade 6": {
|
| 94 |
+
"Mathematics": ["Number System", "Algebra Introduction", "Geometry", "Data Handling"],
|
| 95 |
+
"English": ["Grammar (complex)", "Essay Writing", "Comprehension Skills"],
|
| 96 |
+
"Urdu": ["ادب و نحو", "تحریر"],
|
| 97 |
+
"Science": ["Atoms & Molecules", "Human Body", "Plants"],
|
| 98 |
+
"Islamiyat": ["Seerah & Ahkam"],
|
| 99 |
+
"Social Studies": ["World Geography", "Historical Events", "Government"]
|
| 100 |
+
},
|
| 101 |
+
"Grade 7": {
|
| 102 |
+
"Mathematics": ["Algebra", "Geometry", "Mensuration", "Statistics"],
|
| 103 |
+
"English": ["Poetry & Prose", "Grammar", "Comprehension"],
|
| 104 |
+
"Urdu": ["نثر و شاعری", "قواعد"],
|
| 105 |
+
"Science": ["Cell Biology", "Human Systems", "Electricity Basics"],
|
| 106 |
+
"Islamiyat": ["Islamic History", "Moral Teachings"],
|
| 107 |
+
"Social Studies": ["Geography Basics", "History", "Civics"],
|
| 108 |
+
"Computer": ["Basic ICT", "Computer Hardware", "Introduction to Coding"]
|
| 109 |
+
},
|
| 110 |
+
"Grade 8": {
|
| 111 |
+
"Mathematics": ["Linear Equations", "Geometry", "Pythagoras", "Graphs"],
|
| 112 |
+
"English": ["Advanced Comprehension", "Composition", "Grammar"],
|
| 113 |
+
"Urdu": ["تجزیہ ادب", "تحقیقِ مختصر"],
|
| 114 |
+
"Science": ["Forces & Motion", "Heat", "Waves Basics"],
|
| 115 |
+
"Islamiyat": ["Islamic Law Basics", "Seerah"],
|
| 116 |
+
"Social Studies": ["Regional Geography", "Historical Events"],
|
| 117 |
+
"Computer": ["Algorithms", "Basic Programming"]
|
| 118 |
+
},
|
| 119 |
+
"Grade 9": {
|
| 120 |
+
"Mathematics": ["Number Systems & Algebra", "Coordinate Geometry", "Trigonometry", "Graphs"],
|
| 121 |
+
"English": ["Advanced Grammar", "Comprehension", "Writing Skills"],
|
| 122 |
+
"Urdu": ["ادب اور نثر", "نظم", "مضامین"],
|
| 123 |
+
"Biology": [
|
| 124 |
+
"Introduction to Biology", "Solving a Biological Problem", "Biodiversity",
|
| 125 |
+
"Cells and Tissues", "Cell Cycle", "Enzymes", "Bioenergetics", "Nutrition", "Transport"
|
| 126 |
+
],
|
| 127 |
+
"Chemistry": [
|
| 128 |
+
"Fundamentals of Chemistry", "Structure of Atoms", "Periodic Table and Periodicity of Properties",
|
| 129 |
+
"Structure of Molecules", "Physical States of Matter", "Solutions", "Electrochemistry", "Chemical Reactivity"
|
| 130 |
+
],
|
| 131 |
+
"Physics": [
|
| 132 |
+
"Physical Quantities and Measurement", "Kinematics", "Dynamics", "Turning Effect of Forces",
|
| 133 |
+
"Gravitation", "Work and Energy", "Properties of Matter", "Thermal Properties of Matter", "Transfer of Heat"
|
| 134 |
+
],
|
| 135 |
+
"Islamiyat": ["Seerat of Prophet", "Faith & Beliefs", "Worship Practices"],
|
| 136 |
+
"Pakistan Studies": ["Ideological Basis of Pakistan", "Making of Pakistan", "Land and Environment", "History of Pakistan"]
|
| 137 |
+
},
|
| 138 |
+
"Grade 10": {
|
| 139 |
+
"Mathematics": ["Advanced Algebra", "Trigonometry", "Geometry", "Probability"],
|
| 140 |
+
"English": ["Literature", "Advanced Composition", "Comprehension"],
|
| 141 |
+
"Urdu": ["ادبِ جدید", "تحقیقی مضمون"],
|
| 142 |
+
"Biology": [
|
| 143 |
+
"Gaseous Exchange", "Homeostasis", "Coordination", "Support and Movement",
|
| 144 |
+
"Reproduction", "Inheritance", "Man and His Environment", "Biotechnology", "Pharmacology"
|
| 145 |
+
],
|
| 146 |
+
"Chemistry": [
|
| 147 |
+
"Chemical Equilibrium", "Acids, Bases and Salts", "Organic Chemistry", "Hydrocarbons",
|
| 148 |
+
"Biochemistry", "Environmental Chemistry I: The Atmosphere", "Environmental Chemistry II: Water", "Chemical Industries"
|
| 149 |
+
],
|
| 150 |
+
"Physics": [
|
| 151 |
+
"Simple Harmonic Motion and Waves", "Sound", "Geometrical Optics", "Electrostatics",
|
| 152 |
+
"Current Electricity", "Electromagnetism", "Introductory Electronics",
|
| 153 |
+
"Information and Communication Technology", "Radioactivity"
|
| 154 |
+
],
|
| 155 |
+
"Islamiyat": ["Islamic Studies II", "Ethics & Society", "Fiqh Basics"],
|
| 156 |
+
"Pakistan Studies": [
|
| 157 |
+
"History of Pakistan-II", "Pakistan in World Affairs", "Economic Developments",
|
| 158 |
+
"Population, Society and Culture of Pakistan"
|
| 159 |
+
]
|
| 160 |
+
},
|
| 161 |
+
"Grade 11": {
|
| 162 |
+
"Mathematics": ["Advanced Algebra & Calculus Intro", "Coordinate Geometry", "Trigonometry", "Vectors"],
|
| 163 |
+
"English": ["Academic Writing", "Literature Studies", "Critical Reading"],
|
| 164 |
+
"Urdu": ["ادبی مطالعہ", "نظم و نثر کی تشریح"],
|
| 165 |
+
"Biology": [
|
| 166 |
+
"Cell Structure and Functions", "Biological Molecules", "Enzymes", "Bioenergetics",
|
| 167 |
+
"Acellular Life", "Prokaryotes", "Protists and Fungi", "Diversity among Plants",
|
| 168 |
+
"Diversity among Animals", "Form and Functions in Plants", "Digestion", "Circulation", "Immunity"
|
| 169 |
+
],
|
| 170 |
+
"Chemistry": [
|
| 171 |
+
"Stoichiometry", "Atomic Structure", "Theories of Covalent Bonding and Shapes of Molecules",
|
| 172 |
+
"States of Matter I: Gases", "States of Matter II: Liquids", "States of Matter III: Solids",
|
| 173 |
+
"Chemical Equilibrium", "Acids, Bases and Salts", "Chemical Kinetics",
|
| 174 |
+
"Solutions and Colloids", "Thermochemistry", "Oxidation, Reduction and Electrochemistry"
|
| 175 |
+
],
|
| 176 |
+
"Physics": [
|
| 177 |
+
"Measurement", "Vectors and Equilibrium", "Forces and Motion", "Work and Energy",
|
| 178 |
+
"Rotational and Circular Motion", "Fluid Dynamics", "Oscillations", "Waves",
|
| 179 |
+
"Physical Optics", "Thermodynamics", "Electrostatics", "Current Electricity"
|
| 180 |
+
],
|
| 181 |
+
"Computer": ["Programming Fundamentals", "Data Structures Intro", "Databases"],
|
| 182 |
+
"Islamiyat": ["Advanced Seerah", "Comparative Religion"],
|
| 183 |
+
"Pakistan Studies": [
|
| 184 |
+
"Pakistan in Geographical Perspective", "Pakistan Resources", "Historical Perspective (Ancient to Modern)",
|
| 185 |
+
"Islam and Pakistan", "Administrative System", "Political and Constitutional Developments", "Human Rights"
|
| 186 |
+
]
|
| 187 |
+
},
|
| 188 |
+
"Grade 12": {
|
| 189 |
+
"Mathematics": ["Calculus", "Advanced Algebra", "Statistics", "Analytical Geometry"],
|
| 190 |
+
"English": ["Research Writing", "Advanced Literature", "Critical Analysis"],
|
| 191 |
+
"Urdu": ["تحقیق و تنقید", "ادبی شخصیات"],
|
| 192 |
+
"Biology": [
|
| 193 |
+
"Respiration", "Homeostasis", "Support and Movement", "Nervous Coordination",
|
| 194 |
+
"Chemical Coordination", "Behavior", "Reproduction", "Development and Aging",
|
| 195 |
+
"Inheritance", "Chromosome and DNA", "Evolution", "Man and His Environment",
|
| 196 |
+
"Biotechnology", "Biology and Human Welfare"
|
| 197 |
+
],
|
| 198 |
+
"Chemistry": [
|
| 199 |
+
"s and p Block Elements", "d-Block Elements", "Organic Compounds", "Hydrocarbons",
|
| 200 |
+
"Alkyl Halides and Amines", "Alcohols and Phenols", "Aldehydes and Ketones",
|
| 201 |
+
"Carboxylic Acids and Functional Derivatives", "Biochemistry", "Industrial Chemistry",
|
| 202 |
+
"Environmental Chemistry", "Analytical Chemistry"
|
| 203 |
+
],
|
| 204 |
+
"Physics": [
|
| 205 |
+
"Electromagnetism", "Electromagnetic Induction", "Alternating Current", "Physics of Solids",
|
| 206 |
+
"Electronics", "Dawn of Modern Physics", "Atomic Spectra", "Nuclear Physics"
|
| 207 |
+
],
|
| 208 |
+
"Computer": ["Web Technologies", "Advanced Programming Concepts"],
|
| 209 |
+
"Islamiyat": ["Advanced Islamic Thought", "Islamic Jurisprudence"],
|
| 210 |
+
"Pakistan Studies": [
|
| 211 |
+
"Society and Culture", "Foreign Relations of Pakistan", "Economic Development",
|
| 212 |
+
"Sports, Tourism and National Identity", "Contemporary Challenges and Policies"
|
| 213 |
+
]
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
# ---------------------------
|
| 218 |
+
# Question types for comprehensive assessment
|
| 219 |
+
# ---------------------------
|
| 220 |
+
QUESTION_TYPES = {
|
| 221 |
+
"Multiple Choice Questions (MCQs)": {
|
| 222 |
+
"code": "mcq",
|
| 223 |
+
"description": "Four-option questions testing recall and application",
|
| 224 |
+
"format": "Question with options A, B, C, D"
|
| 225 |
+
},
|
| 226 |
+
"Fill in the Blanks": {
|
| 227 |
+
"code": "fill_blank",
|
| 228 |
+
"description": "Complete sentences with missing key terms",
|
| 229 |
+
"format": "Sentences with underlined blanks"
|
| 230 |
+
},
|
| 231 |
+
"Match the Column": {
|
| 232 |
+
"code": "match_column",
|
| 233 |
+
"description": "Connect related items from two columns",
|
| 234 |
+
"format": "Two columns with items to match"
|
| 235 |
+
},
|
| 236 |
+
"Short Response Questions": {
|
| 237 |
+
"code": "short_response",
|
| 238 |
+
"description": "Brief written answers (2-3 sentences)",
|
| 239 |
+
"format": "Questions requiring concise explanations"
|
| 240 |
+
},
|
| 241 |
+
"Essay Type Questions": {
|
| 242 |
+
"code": "essay",
|
| 243 |
+
"description": "Extended written responses with detailed analysis",
|
| 244 |
+
"format": "Open-ended questions requiring paragraphs"
|
| 245 |
+
}
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
# ---------------------------
|
| 249 |
+
# Cognitive levels for item writing (Bloom's Taxonomy)
|
| 250 |
+
# ---------------------------
|
| 251 |
+
DIFFICULTY_LEVELS = {
|
| 252 |
+
"Remembering": {
|
| 253 |
+
"description": "Recall facts, terms, basic concepts",
|
| 254 |
+
"keywords": ["define", "list", "identify", "name", "state", "recall"]
|
| 255 |
+
},
|
| 256 |
+
"Understanding": {
|
| 257 |
+
"description": "Explain ideas or concepts",
|
| 258 |
+
"keywords": ["explain", "describe", "summarize", "interpret", "paraphrase"]
|
| 259 |
+
},
|
| 260 |
+
"Applying": {
|
| 261 |
+
"description": "Use information in new situations",
|
| 262 |
+
"keywords": ["apply", "demonstrate", "solve", "use", "calculate", "implement"]
|
| 263 |
+
},
|
| 264 |
+
"Analyzing": {
|
| 265 |
+
"description": "Draw connections, examine relationships",
|
| 266 |
+
"keywords": ["analyze", "compare", "contrast", "differentiate", "examine"]
|
| 267 |
+
},
|
| 268 |
+
"Evaluating": {
|
| 269 |
+
"description": "Justify decisions, make judgments",
|
| 270 |
+
"keywords": ["evaluate", "justify", "critique", "assess", "judge"]
|
| 271 |
+
},
|
| 272 |
+
"Creating": {
|
| 273 |
+
"description": "Produce new or original work",
|
| 274 |
+
"keywords": ["create", "design", "construct", "develop", "formulate"]
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
"Grade 1": {
|
| 278 |
"Mathematics": ["Counting & Numbers", "Basic Addition", "Basic Subtraction", "Shapes", "Measurements"],
|
| 279 |
"English": ["Alphabet & Sounds", "Basic Words", "Simple Sentences", "Listening & Speaking", "Reading Short Texts"],
|
|
|
|
| 542 |
}
|
| 543 |
|
| 544 |
# ---------------------------
|
| 545 |
+
# Enhanced question generation with Bloom's Taxonomy distribution
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
# ---------------------------
|
| 547 |
+
def generate_questions_multi_level(grade, subject, chapter, test_type, school_name, question_type,
|
| 548 |
+
bloom_distribution, total_questions):
|
| 549 |
+
"""Generate questions with mixed cognitive levels based on user distribution"""
|
| 550 |
+
|
| 551 |
+
all_questions = []
|
| 552 |
+
all_answers = []
|
| 553 |
+
|
| 554 |
+
try:
|
| 555 |
+
# Calculate number of questions per level
|
| 556 |
+
for level, percentage in bloom_distribution.items():
|
| 557 |
+
if percentage > 0:
|
| 558 |
+
num_q = int((percentage / 100) * total_questions)
|
| 559 |
+
if num_q > 0:
|
| 560 |
+
questions, answers, error, q_type_code = generate_questions(
|
| 561 |
+
grade, subject, chapter, level,
|
| 562 |
+
num_q, test_type, school_name, question_type
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
+
if error:
|
| 566 |
+
return [], [], error, "mcq"
|
| 567 |
+
|
| 568 |
+
all_questions.extend(questions)
|
| 569 |
+
all_answers.extend(answers)
|
| 570 |
+
|
| 571 |
+
# Ensure we have the right number of questions
|
| 572 |
+
if len(all_questions) < total_questions:
|
| 573 |
+
# Generate additional questions at first non-zero level
|
| 574 |
+
shortage = total_questions - len(all_questions)
|
| 575 |
+
first_level = next((k for k, v in bloom_distribution.items() if v > 0), "Understanding")
|
| 576 |
+
|
| 577 |
+
extra_q, extra_a, error, q_type_code = generate_questions(
|
| 578 |
+
grade, subject, chapter, first_level,
|
| 579 |
+
shortage, test_type, school_name, question_type
|
| 580 |
+
)
|
| 581 |
+
|
| 582 |
+
if not error:
|
| 583 |
+
all_questions.extend(extra_q)
|
| 584 |
+
all_answers.extend(extra_a)
|
| 585 |
+
|
| 586 |
+
return all_questions[:total_questions], all_answers[:total_questions], None, q_type_code
|
| 587 |
+
|
| 588 |
+
except Exception as e:
|
| 589 |
+
return [], [], f"Error in multi-level generation: {str(e)}", "mcq"
|
| 590 |
|
| 591 |
# ---------------------------
|
| 592 |
# Enhanced question generation with proper item writing principles
|
|
|
|
| 855 |
return questions, answers
|
| 856 |
|
| 857 |
def parse_short_response(text, expected_count):
|
| 858 |
+
"""Parse Short Response questions - separating questions from expected answers"""
|
| 859 |
+
# Split by expected answers section
|
| 860 |
+
parts = re.split(r'EXPECTED\s+ANSWERS?:?|MARKING\s+SCHEME:?', text, flags=re.IGNORECASE)
|
| 861 |
|
| 862 |
questions = []
|
| 863 |
answers = []
|
|
|
|
| 870 |
|
| 871 |
if len(parts) >= 2:
|
| 872 |
a_text = parts[1]
|
| 873 |
+
# Extract expected answers/key points
|
| 874 |
schemes = re.findall(r'\d+[\.\)]\s*(.+?)(?=\d+[\.\)]|$)', a_text, re.DOTALL)
|
| 875 |
answers = [s.strip() for s in schemes[:expected_count]]
|
| 876 |
|
| 877 |
if len(answers) < len(questions):
|
| 878 |
+
answers.extend(['Key points: Provide comprehensive explanation with examples'] * (len(questions) - len(answers)))
|
| 879 |
|
| 880 |
return questions, answers
|
| 881 |
|
| 882 |
def parse_essay(text, expected_count):
|
| 883 |
+
"""Parse Essay questions - separating questions from expected answers"""
|
| 884 |
+
# Split by expected answers section
|
| 885 |
+
parts = re.split(r'EXPECTED\s+ANSWERS?:?|MARKING\s+CRITERIA:?', text, flags=re.IGNORECASE)
|
| 886 |
|
| 887 |
questions = []
|
| 888 |
answers = []
|
|
|
|
| 895 |
|
| 896 |
if len(parts) >= 2:
|
| 897 |
a_text = parts[1]
|
| 898 |
+
# Extract criteria/expected coverage
|
| 899 |
criteria = re.findall(r'\d+[\.\)]\s*(.+?)(?=\d+[\.\)]|$)', a_text, re.DOTALL)
|
| 900 |
answers = [c.strip() for c in criteria[:expected_count]]
|
| 901 |
|
| 902 |
if len(answers) < len(questions):
|
| 903 |
+
answers.extend(['Main points: Detailed analysis with evidence, examples, and logical argumentation'] * (len(questions) - len(answers)))
|
| 904 |
|
| 905 |
return questions, answers
|
| 906 |
|
| 907 |
+
def create_answer_table(doc, answers, num_cols=5):
|
| 908 |
+
"""Helper function to create formatted answer table"""
|
| 909 |
+
num_rows = len(answers) + 1
|
| 910 |
+
ans_table = doc.add_table(rows=num_rows, cols=num_cols)
|
| 911 |
+
ans_table.style = 'Medium Grid 1 Accent 1'
|
| 912 |
+
|
| 913 |
+
# Set column widths
|
| 914 |
+
for row in ans_table.rows:
|
| 915 |
+
for idx, cell in enumerate(row.cells):
|
| 916 |
+
if idx == 0:
|
| 917 |
+
cell.width = Inches(0.6)
|
| 918 |
+
else:
|
| 919 |
+
cell.width = Inches(1.2)
|
| 920 |
+
|
| 921 |
+
return ans_table
|
| 922 |
+
|
| 923 |
# ---------------------------
|
| 924 |
# Enhanced DOCX export supporting all question types
|
| 925 |
# ---------------------------
|
|
|
|
| 1016 |
if question_type_code == "mcq":
|
| 1017 |
for i, q in enumerate(questions, start=1):
|
| 1018 |
q_para = doc.add_paragraph()
|
| 1019 |
+
q_para.add_run(f"{i}. ").bold = True
|
| 1020 |
q_para.add_run(q)
|
| 1021 |
+
q_para.space_after = Pt(8)
|
| 1022 |
doc.add_paragraph()
|
| 1023 |
|
| 1024 |
elif question_type_code == "fill_blank":
|
|
|
|
| 1027 |
q_para.add_run(f"{i}. ").bold = True
|
| 1028 |
q_para.add_run(q)
|
| 1029 |
q_para.space_after = Pt(8)
|
| 1030 |
+
doc.add_paragraph()
|
| 1031 |
|
| 1032 |
elif question_type_code == "match_column":
|
| 1033 |
for q in questions:
|
|
|
|
| 1043 |
doc.add_paragraph()
|
| 1044 |
# Add space for answer
|
| 1045 |
for _ in range(3 if question_type_code == "short_response" else 8):
|
| 1046 |
+
doc.add_paragraph("_" * 85)
|
| 1047 |
doc.add_paragraph()
|
| 1048 |
|
| 1049 |
# Answer key (on separate page)
|
| 1050 |
doc.add_page_break()
|
| 1051 |
|
| 1052 |
+
ans_heading = doc.add_heading("ANSWER KEY", level=2)
|
| 1053 |
ans_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 1054 |
for run in ans_heading.runs:
|
| 1055 |
run.font.color.rgb = RGBColor(153, 0, 0)
|
|
|
|
| 1060 |
# Add answers based on type
|
| 1061 |
if question_type_code == "mcq":
|
| 1062 |
# Answer table for MCQs
|
| 1063 |
+
ans_table = create_answer_table(doc, answers, 5)
|
|
|
|
|
|
|
| 1064 |
|
| 1065 |
headers = ["Q#", "A", "B", "C", "D"]
|
| 1066 |
for idx, header in enumerate(headers):
|
|
|
|
| 1083 |
else:
|
| 1084 |
cell.text = ""
|
| 1085 |
cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 1086 |
+
|
| 1087 |
+
elif question_type_code == "fill_blank":
|
| 1088 |
+
# Tabular answer key for Fill in the Blanks
|
| 1089 |
+
ans_table = create_answer_table(doc, answers, 2)
|
| 1090 |
+
|
| 1091 |
+
# Headers
|
| 1092 |
+
headers = ["Q#", "Correct Answer"]
|
| 1093 |
+
for idx, header in enumerate(headers):
|
| 1094 |
+
cell = ans_table.cell(0, idx)
|
| 1095 |
+
cell.text = header
|
| 1096 |
+
cell.paragraphs[0].runs[0].bold = True
|
| 1097 |
+
cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 1098 |
+
|
| 1099 |
+
# Fill answers
|
| 1100 |
+
for r, ans in enumerate(answers, start=1):
|
| 1101 |
+
ans_table.cell(r, 0).text = str(r)
|
| 1102 |
+
ans_table.cell(r, 0).paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 1103 |
+
ans_table.cell(r, 1).text = str(ans)
|
| 1104 |
+
|
| 1105 |
+
elif question_type_code in ["short_response", "essay"]:
|
| 1106 |
+
# Expected answers for subjective questions
|
| 1107 |
+
for i, ans in enumerate(answers, start=1):
|
| 1108 |
+
ans_para = doc.add_paragraph()
|
| 1109 |
+
ans_para.add_run(f"Q{i}. Expected Answer:").bold = True
|
| 1110 |
+
doc.add_paragraph(str(ans))
|
| 1111 |
+
doc.add_paragraph()
|
| 1112 |
+
|
| 1113 |
else:
|
| 1114 |
# Text-based answers for other types
|
| 1115 |
for i, ans in enumerate(answers, start=1):
|
|
|
|
| 1121 |
# Footer
|
| 1122 |
doc.add_paragraph()
|
| 1123 |
footer = doc.add_paragraph()
|
| 1124 |
+
footer.add_run(f"Generated by Test Generator Pro v{APP_VERSION} | Developer: Najaf Ali Sharqi | {datetime.now().strftime('%d-%m-%Y %H:%M')}").italic = True
|
| 1125 |
footer.runs[0].font.size = Pt(8)
|
| 1126 |
footer.runs[0].font.color.rgb = RGBColor(128, 128, 128)
|
| 1127 |
footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
|
| 1139 |
return None, f"Error creating document: {str(e)}"
|
| 1140 |
|
| 1141 |
# ---------------------------
|
| 1142 |
+
# Main generation handler with Bloom's Taxonomy distribution
|
| 1143 |
# ---------------------------
|
| 1144 |
+
def on_generate(grade, subject, chapter, test_type, school_name, question_type,
|
| 1145 |
+
use_bloom_mix, bloom_remember, bloom_understand, bloom_apply,
|
| 1146 |
+
bloom_analyze, bloom_evaluate, bloom_create, num_questions):
|
| 1147 |
+
"""Handle question generation request with optional Bloom's distribution"""
|
| 1148 |
|
| 1149 |
# Validation
|
| 1150 |
+
if not all([grade, subject, chapter, question_type]):
|
| 1151 |
return "⚠️ Please fill all required fields", None, None
|
| 1152 |
|
| 1153 |
if not school_name or not school_name.strip():
|
| 1154 |
school_name = "Educational Institution"
|
| 1155 |
|
| 1156 |
try:
|
| 1157 |
+
# Check if using Bloom's mix
|
| 1158 |
+
if use_bloom_mix:
|
| 1159 |
+
# Validate percentages
|
| 1160 |
+
total_percent = bloom_remember + bloom_understand + bloom_apply + bloom_analyze + bloom_evaluate + bloom_create
|
| 1161 |
+
if total_percent != 100:
|
| 1162 |
+
return f"❌ Bloom's Taxonomy percentages must total 100% (currently {total_percent}%)", None, None
|
| 1163 |
+
|
| 1164 |
+
bloom_distribution = {
|
| 1165 |
+
"Remembering": bloom_remember,
|
| 1166 |
+
"Understanding": bloom_understand,
|
| 1167 |
+
"Applying": bloom_apply,
|
| 1168 |
+
"Analyzing": bloom_analyze,
|
| 1169 |
+
"Evaluating": bloom_evaluate,
|
| 1170 |
+
"Creating": bloom_create
|
| 1171 |
+
}
|
| 1172 |
+
|
| 1173 |
+
# Show progress
|
| 1174 |
+
status_msg = f"🔄 Generating {num_questions} {question_type} with mixed cognitive levels...\n"
|
| 1175 |
+
status_msg += f"📊 Bloom's Distribution:\n"
|
| 1176 |
+
for level, pct in bloom_distribution.items():
|
| 1177 |
+
if pct > 0:
|
| 1178 |
+
status_msg += f" • {level}: {pct}% ({int(pct * num_questions / 100)} questions)\n"
|
| 1179 |
+
|
| 1180 |
+
# Generate with mixed levels
|
| 1181 |
+
questions, answers, error, q_type_code = generate_questions_multi_level(
|
| 1182 |
+
grade, subject, chapter, test_type, school_name, question_type,
|
| 1183 |
+
bloom_distribution, num_questions
|
| 1184 |
+
)
|
| 1185 |
+
else:
|
| 1186 |
+
# Single level generation (default to Understanding)
|
| 1187 |
+
status_msg = f"🔄 Generating {num_questions} {question_type}...\n"
|
| 1188 |
+
status_msg += f"📖 Grade: {grade} | Subject: {subject}\n"
|
| 1189 |
+
status_msg += f"📚 Chapter: {chapter}\n"
|
| 1190 |
+
status_msg += f"🎯 Cognitive Level: Understanding\n"
|
| 1191 |
+
status_msg += "⏳ Please wait..."
|
| 1192 |
+
|
| 1193 |
+
questions, answers, error, q_type_code = generate_questions(
|
| 1194 |
+
grade, subject, chapter, "Understanding",
|
| 1195 |
+
num_questions, test_type, school_name, question_type
|
| 1196 |
+
)
|
| 1197 |
|
| 1198 |
if error:
|
| 1199 |
return f"❌ Error: {error}", None, None
|
|
|
|
| 1220 |
# Export to Word
|
| 1221 |
file_path, doc_error = export_to_word(
|
| 1222 |
questions, answers, grade, subject, chapter,
|
| 1223 |
+
"Mixed Cognitive Levels" if use_bloom_mix else "Understanding",
|
| 1224 |
+
test_type, school_name, total_marks, q_type_code
|
| 1225 |
)
|
| 1226 |
|
| 1227 |
if doc_error:
|
|
|
|
| 1229 |
|
| 1230 |
# Success message
|
| 1231 |
success_msg = f"✅ Test generated successfully!\n\n"
|
| 1232 |
+
success_msg += f"📊 Test Statistics:\n"
|
| 1233 |
success_msg += f"• Question Type: {question_type}\n"
|
| 1234 |
success_msg += f"• Total Questions: {len(questions)}\n"
|
| 1235 |
success_msg += f"• Total Marks: {total_marks}\n"
|
| 1236 |
+
success_msg += f"• Grade: {grade} | Subject: {subject}\n"
|
|
|
|
| 1237 |
success_msg += f"• Chapter: {chapter}\n"
|
| 1238 |
+
|
| 1239 |
+
if use_bloom_mix:
|
| 1240 |
+
success_msg += f"\n🎯 Bloom's Taxonomy Distribution:\n"
|
| 1241 |
+
for level, pct in bloom_distribution.items():
|
| 1242 |
+
if pct > 0:
|
| 1243 |
+
success_msg += f" • {level}: {pct}%\n"
|
| 1244 |
+
|
| 1245 |
+
success_msg += f"\n📥 Download your test document below"
|
| 1246 |
|
| 1247 |
# Preview
|
| 1248 |
preview_count = min(3, len(questions))
|
|
|
|
| 1250 |
|
| 1251 |
for i, q in enumerate(questions[:preview_count], 1):
|
| 1252 |
if q_type_code == "mcq":
|
| 1253 |
+
preview += f"{i}. {q[:200]}...\n\n" if len(q) > 200 else f"{i}. {q}\n\n"
|
| 1254 |
else:
|
| 1255 |
preview += f"{i}. {q[:200]}...\n\n" if len(q) > 200 else f"{i}. {q}\n\n"
|
| 1256 |
|
|
|
|
| 1281 |
# Header
|
| 1282 |
gr.Markdown(
|
| 1283 |
"""
|
| 1284 |
+
# 📚 Test Generator
|
| 1285 |
+
### **Developer: Najaf Ali Sharqi**
|
| 1286 |
+
|
| 1287 |
+
Professional Assessment Tool Aligned with Pakistani National Curriculum (2006)
|
| 1288 |
""",
|
| 1289 |
elem_classes="header"
|
| 1290 |
)
|
| 1291 |
|
| 1292 |
gr.Markdown(
|
| 1293 |
"""
|
| 1294 |
+
Generate high-quality assessment questions with multiple formats and Bloom's Taxonomy integration.
|
| 1295 |
+
Perfect for educators creating comprehensive tests for Grades 1-12.
|
| 1296 |
""",
|
| 1297 |
elem_classes="description"
|
| 1298 |
)
|
|
|
|
| 1367 |
info="Select specific chapter or unit"
|
| 1368 |
)
|
| 1369 |
|
| 1370 |
+
# Bloom's Taxonomy Distribution Section
|
| 1371 |
+
use_bloom_mix = gr.Checkbox(
|
| 1372 |
+
label="📊 Use Mixed Bloom's Taxonomy Levels",
|
| 1373 |
+
value=False,
|
| 1374 |
+
info="Create tests with questions from multiple cognitive levels"
|
| 1375 |
)
|
| 1376 |
|
| 1377 |
+
bloom_percentages = gr.Column(visible=False)
|
| 1378 |
+
|
| 1379 |
+
with bloom_percentages:
|
| 1380 |
+
gr.Markdown("### Set Percentage for Each Cognitive Level")
|
| 1381 |
+
gr.Markdown("*(Total must equal 100%)*")
|
| 1382 |
|
| 1383 |
+
with gr.Row():
|
| 1384 |
+
bloom_remember = gr.Slider(
|
| 1385 |
+
minimum=0, maximum=100, value=30, step=5,
|
| 1386 |
+
label="Remembering %",
|
| 1387 |
+
info="Recall facts and basic concepts"
|
| 1388 |
+
)
|
| 1389 |
+
bloom_understand = gr.Slider(
|
| 1390 |
+
minimum=0, maximum=100, value=30, step=5,
|
| 1391 |
+
label="Understanding %",
|
| 1392 |
+
info="Explain ideas and concepts"
|
| 1393 |
+
)
|
| 1394 |
+
|
| 1395 |
+
with gr.Row():
|
| 1396 |
+
bloom_apply = gr.Slider(
|
| 1397 |
+
minimum=0, maximum=100, value=20, step=5,
|
| 1398 |
+
label="Applying %",
|
| 1399 |
+
info="Use information in new situations"
|
| 1400 |
+
)
|
| 1401 |
+
bloom_analyze = gr.Slider(
|
| 1402 |
+
minimum=0, maximum=100, value=10, step=5,
|
| 1403 |
+
label="Analyzing %",
|
| 1404 |
+
info="Draw connections and relationships"
|
| 1405 |
+
)
|
| 1406 |
+
|
| 1407 |
+
with gr.Row():
|
| 1408 |
+
bloom_evaluate = gr.Slider(
|
| 1409 |
+
minimum=0, maximum=100, value=10, step=5,
|
| 1410 |
+
label="Evaluating %",
|
| 1411 |
+
info="Justify decisions and make judgments"
|
| 1412 |
+
)
|
| 1413 |
+
bloom_create = gr.Slider(
|
| 1414 |
+
minimum=0, maximum=100, value=0, step=5,
|
| 1415 |
+
label="Creating %",
|
| 1416 |
+
info="Produce new or original work"
|
| 1417 |
+
)
|
| 1418 |
+
|
| 1419 |
+
bloom_total = gr.Textbox(
|
| 1420 |
+
label="Total Percentage",
|
| 1421 |
+
value="100%",
|
| 1422 |
+
interactive=False
|
| 1423 |
+
)
|
| 1424 |
+
|
| 1425 |
+
difficulty_dropdown = gr.Dropdown(
|
| 1426 |
+
choices=list(DIFFICULTY_LEVELS.keys()),
|
| 1427 |
+
value="Understanding",
|
| 1428 |
+
label="Single Cognitive Level (if not using mix)",
|
| 1429 |
+
info="Select one level for entire test",
|
| 1430 |
visible=True
|
| 1431 |
)
|
| 1432 |
|
|
|
|
| 1454 |
- **Match the Column**: Assesses relationships and connections
|
| 1455 |
- **Short Response**: Evaluates understanding and explanation
|
| 1456 |
- **Essay Questions**: Measures critical thinking and synthesis
|
| 1457 |
+
- **Bloom's Mix**: Use for comprehensive assessment across cognitive levels
|
| 1458 |
- Review generated questions before final use
|
| 1459 |
+
|
| 1460 |
+
### 📧 Custom App Development
|
| 1461 |
+
**Need a customized assessment tool?**
|
| 1462 |
+
Contact developer **Najaf Ali Sharqi** for:
|
| 1463 |
+
- Custom question banks
|
| 1464 |
+
- Institution-specific features
|
| 1465 |
+
- Advanced analytics
|
| 1466 |
+
- Integration with LMS platforms
|
| 1467 |
"""
|
| 1468 |
)
|
| 1469 |
|
|
|
|
| 1506 |
"""
|
| 1507 |
---
|
| 1508 |
<div class="footer">
|
| 1509 |
+
<strong>Test Generator Pro v2.5</strong><br>
|
| 1510 |
+
Comprehensive Assessment Tool with 5 Question Types & Bloom's Taxonomy Integration<br>
|
| 1511 |
Aligned with Pakistani National Curriculum (2006)<br>
|
| 1512 |
Developer: <strong>Najaf Ali Sharqi</strong><br>
|
| 1513 |
+
Based on Professional Item Writing Standards<br><br>
|
| 1514 |
+
📧 For custom app development or support: Contact Najaf Ali Sharqi
|
| 1515 |
</div>
|
| 1516 |
""",
|
| 1517 |
elem_classes="footer"
|
|
|
|
| 1532 |
return gr.update(choices=chapters, value=None)
|
| 1533 |
return gr.update(choices=[], value=None)
|
| 1534 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1535 |
def update_question_type_info(q_type):
|
| 1536 |
"""Update question type description"""
|
| 1537 |
if q_type in QUESTION_TYPES:
|
|
|
|
| 1545 |
"""
|
| 1546 |
return ""
|
| 1547 |
|
| 1548 |
+
def toggle_bloom_sliders(use_mix):
|
| 1549 |
+
"""Show/hide Bloom's taxonomy sliders"""
|
| 1550 |
+
return gr.update(visible=use_mix), gr.update(visible=not use_mix)
|
| 1551 |
+
|
| 1552 |
+
def calculate_bloom_total(r, u, ap, an, e, c):
|
| 1553 |
+
"""Calculate total percentage"""
|
| 1554 |
+
total = r + u + ap + an + e + c
|
| 1555 |
+
color = "green" if total == 100 else "red"
|
| 1556 |
+
return f'<span style="color:{color}; font-weight:bold;">{total}%</span>'
|
| 1557 |
+
|
| 1558 |
# Connect event handlers
|
| 1559 |
grade_dropdown.change(
|
| 1560 |
update_subjects,
|
|
|
|
| 1574 |
outputs=[chapter_dropdown]
|
| 1575 |
)
|
| 1576 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1577 |
question_type.change(
|
| 1578 |
update_question_type_info,
|
| 1579 |
inputs=[question_type],
|
| 1580 |
outputs=[question_type_info]
|
| 1581 |
)
|
| 1582 |
|
| 1583 |
+
use_bloom_mix.change(
|
| 1584 |
+
toggle_bloom_sliders,
|
| 1585 |
+
inputs=[use_bloom_mix],
|
| 1586 |
+
outputs=[bloom_percentages, difficulty_dropdown]
|
| 1587 |
+
)
|
| 1588 |
+
|
| 1589 |
+
# Update total when sliders change
|
| 1590 |
+
for slider in [bloom_remember, bloom_understand, bloom_apply, bloom_analyze, bloom_evaluate, bloom_create]:
|
| 1591 |
+
slider.change(
|
| 1592 |
+
calculate_bloom_total,
|
| 1593 |
+
inputs=[bloom_remember, bloom_understand, bloom_apply, bloom_analyze, bloom_evaluate, bloom_create],
|
| 1594 |
+
outputs=[bloom_total]
|
| 1595 |
+
)
|
| 1596 |
+
|
| 1597 |
generate_btn.click(
|
| 1598 |
on_generate,
|
| 1599 |
inputs=[
|
| 1600 |
grade_dropdown,
|
| 1601 |
subject_dropdown,
|
| 1602 |
chapter_dropdown,
|
|
|
|
|
|
|
| 1603 |
test_type,
|
| 1604 |
school_name,
|
| 1605 |
+
question_type,
|
| 1606 |
+
use_bloom_mix,
|
| 1607 |
+
bloom_remember,
|
| 1608 |
+
bloom_understand,
|
| 1609 |
+
bloom_apply,
|
| 1610 |
+
bloom_analyze,
|
| 1611 |
+
bloom_evaluate,
|
| 1612 |
+
bloom_create,
|
| 1613 |
+
num_questions
|
| 1614 |
],
|
| 1615 |
outputs=[output_text, output_file, file_status]
|
| 1616 |
)
|