Turbiling commited on
Commit
d46a278
·
verified ·
1 Parent(s): 912f6a4

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -1581
app.py DELETED
@@ -1,1581 +0,0 @@
1
-
2
-
3
- # app.py - Enhanced Test Generator for Hugging Face
4
- # Aligned with Pakistani National Curriculum (2006)
5
- # Developer: Najaf Ali Sharqi
6
- import os
7
- import gradio as gr
8
- from groq import Groq
9
- from datetime import datetime
10
- from docx import Document
11
- from docx.shared import Pt, RGBColor, Inches
12
- from docx.enum.text import WD_ALIGN_PARAGRAPH
13
- from docx.oxml.ns import qn
14
- from docx.oxml import OxmlElement
15
- import re
16
- import json
17
-
18
- # ---------------------------
19
- # Initialize GROQ client with error handling
20
- # ---------------------------
21
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
22
- if not GROQ_API_KEY:
23
- raise ValueError("GROQ_API_KEY environment variable not set")
24
- client = Groq(api_key=GROQ_API_KEY)
25
-
26
- # ---------------------------
27
- # Configuration Constants
28
- # ---------------------------
29
- APP_VERSION = "2.6"
30
- MODEL_NAME = "llama-3.3-70b-versatile"
31
-
32
- # ---------------------------
33
- # Grades and subjects aligned with Pakistan National Curriculum
34
- # ---------------------------
35
- grades = [f"Grade {i}" for i in range(1, 13)]
36
-
37
- subjects_by_grade = {
38
- "Grade 1": ["Mathematics", "English", "Urdu", "Islamiyat", "General Knowledge"],
39
- "Grade 2": ["Mathematics", "English", "Urdu", "Islamiyat", "General Knowledge"],
40
- "Grade 3": ["Mathematics", "English", "Urdu", "Science", "Islamiyat", "General Knowledge"],
41
- "Grade 4": ["Mathematics", "English", "Urdu", "Science", "Islamiyat", "General Knowledge"],
42
- "Grade 5": ["Mathematics", "English", "Urdu", "Science", "Islamiyat", "Social Studies"],
43
- "Grade 6": ["Mathematics", "English", "Urdu", "Science", "Islamiyat", "Social Studies"],
44
- "Grade 7": ["Mathematics", "English", "Urdu", "Science", "Islamiyat", "Social Studies", "Computer"],
45
- "Grade 8": ["Mathematics", "English", "Urdu", "Science", "Islamiyat", "Social Studies", "Computer"],
46
- "Grade 9": ["Mathematics", "English", "Urdu", "Biology", "Chemistry", "Physics", "Islamiyat", "Pakistan Studies"],
47
- "Grade 10": ["Mathematics", "English", "Urdu", "Biology", "Chemistry", "Physics", "Islamiyat", "Pakistan Studies"],
48
- "Grade 11": ["Mathematics", "English", "Urdu", "Biology", "Chemistry", "Physics", "Computer", "Islamiyat", "Pakistan Studies"],
49
- "Grade 12": ["Mathematics", "English", "Urdu", "Biology", "Chemistry", "Physics", "Computer", "Islamiyat", "Pakistan Studies"]
50
- }
51
-
52
- # ---------------------------
53
- # Chapter mapping (National Curriculum 2006)
54
- # ---------------------------
55
- chapters_by_subject_and_grade = {
56
- "Grade 1": {
57
- "Mathematics": ["Counting & Numbers", "Basic Addition", "Basic Subtraction", "Shapes", "Measurements"],
58
- "English": ["Alphabet & Sounds", "Basic Words", "Simple Sentences", "Listening & Speaking", "Reading Short Texts"],
59
- "Urdu": ["حروف", "الفاظ", "سادہ جملے", "پڑھنا", "لکھنا"],
60
- "Islamiyat": ["Basic Beliefs", "Prophets Stories", "Good Manners", "Prayers & Worship"],
61
- "General Knowledge": ["My Body", "My Home", "My School", "Seasons"]
62
- },
63
- "Grade 2": {
64
- "Mathematics": ["Place Value", "Addition & Subtraction", "Money", "Time", "Shapes"],
65
- "English": ["Parts of Speech Basics", "Simple Grammar", "Short Comprehension", "Vocabulary Building"],
66
- "Urdu": ["الفاظ و جملے", "مختصر کہانیاں", "حروفِ صحیح"],
67
- "Islamiyat": ["Prophets", "Good Deeds", "Mosque Etiquette"],
68
- "General Knowledge": ["Plants & Animals", "Local Community", "Transport"]
69
- },
70
- "Grade 3": {
71
- "Mathematics": ["Multiplication", "Division", "Fractions", "Geometry Basics", "Measurement"],
72
- "English": ["Tenses (simple)", "Comprehension Passages", "Vocabulary", "Sentence Formation"],
73
- "Urdu": ["کالم و مضامین", "قواعدِ اردو", "کہانیاں"],
74
- "Science": ["Plants", "Animals", "Human Body", "Materials"],
75
- "Islamiyat": ["Basics of Iman", "Seerat", "Manners"],
76
- "General Knowledge": ["Environment", "Community Helpers", "Safety"]
77
- },
78
- "Grade 4": {
79
- "Mathematics": ["Fractions & Decimals", "Geometry", "Data Handling", "Word Problems"],
80
- "English": ["Grammar (intermediate)", "Comprehension", "Writing Short Paragraphs"],
81
- "Urdu": ["نظم و نثر", "قواعد", "مفردات"],
82
- "Science": ["Ecosystems", "Forces", "Heat & Energy"],
83
- "Islamiyat": ["Pillars of Islam", "Seerah Stories"],
84
- "General Knowledge": ["Maps & Directions", "Cultural Heritage", "Technology"]
85
- },
86
- "Grade 5": {
87
- "Mathematics": ["Numbers & Operations", "Geometry", "Statistics", "Ratio & Proportion"],
88
- "English": ["Advanced Grammar Basics", "Paragraph Writing", "Comprehension"],
89
- "Urdu": ["ادبی مطالعہ", "تعبیر و تشریح"],
90
- "Science": ["Cells & Life", "Matter", "Energy"],
91
- "Islamiyat": ["Quranic Stories", "Akhlaq"],
92
- "Social Studies": ["Pakistan Geography", "History Basics", "Civics Introduction"]
93
- },
94
- "Grade 6": {
95
- "Mathematics": ["Number System", "Algebra Introduction", "Geometry", "Data Handling"],
96
- "English": ["Grammar (complex)", "Essay Writing", "Comprehension Skills"],
97
- "Urdu": ["ادب و نحو", "تحریر"],
98
- "Science": ["Atoms & Molecules", "Human Body", "Plants"],
99
- "Islamiyat": ["Seerah & Ahkam"],
100
- "Social Studies": ["World Geography", "Historical Events", "Government"]
101
- },
102
- "Grade 7": {
103
- "Mathematics": ["Algebra", "Geometry", "Mensuration", "Statistics"],
104
- "English": ["Poetry & Prose", "Grammar", "Comprehension"],
105
- "Urdu": ["نثر و شاعری", "قواعد"],
106
- "Science": ["Cell Biology", "Human Systems", "Electricity Basics"],
107
- "Islamiyat": ["Islamic History", "Moral Teachings"],
108
- "Social Studies": ["Geography Basics", "History", "Civics"],
109
- "Computer": ["Basic ICT", "Computer Hardware", "Introduction to Coding"]
110
- },
111
- "Grade 8": {
112
- "Mathematics": ["Linear Equations", "Geometry", "Pythagoras", "Graphs"],
113
- "English": ["Advanced Comprehension", "Composition", "Grammar"],
114
- "Urdu": ["تجزیہ ادب", "تحقیقِ مختصر"],
115
- "Science": ["Forces & Motion", "Heat", "Waves Basics"],
116
- "Islamiyat": ["Islamic Law Basics", "Seerah"],
117
- "Social Studies": ["Regional Geography", "Historical Events"],
118
- "Computer": ["Algorithms", "Basic Programming"]
119
- },
120
- "Grade 9": {
121
- "Mathematics": ["Number Systems & Algebra", "Coordinate Geometry", "Trigonometry", "Graphs"],
122
- "English": ["Advanced Grammar", "Comprehension", "Writing Skills"],
123
- "Urdu": ["ادب اور نثر", "نظم", "مضامین"],
124
- "Biology": [
125
- "Introduction to Biology", "Solving a Biological Problem", "Biodiversity",
126
- "Cells and Tissues", "Cell Cycle", "Enzymes", "Bioenergetics", "Nutrition", "Transport"
127
- ],
128
- "Chemistry": [
129
- "Fundamentals of Chemistry", "Structure of Atoms", "Periodic Table and Periodicity of Properties",
130
- "Structure of Molecules", "Physical States of Matter", "Solutions", "Electrochemistry", "Chemical Reactivity"
131
- ],
132
- "Physics": [
133
- "Physical Quantities and Measurement", "Kinematics", "Dynamics", "Turning Effect of Forces",
134
- "Gravitation", "Work and Energy", "Properties of Matter", "Thermal Properties of Matter", "Transfer of Heat"
135
- ],
136
- "Islamiyat": ["Seerat of Prophet", "Faith & Beliefs", "Worship Practices"],
137
- "Pakistan Studies": ["Ideological Basis of Pakistan", "Making of Pakistan", "Land and Environment", "History of Pakistan"]
138
- },
139
- "Grade 10": {
140
- "Mathematics": ["Advanced Algebra", "Trigonometry", "Geometry", "Probability"],
141
- "English": ["Literature", "Advanced Composition", "Comprehension"],
142
- "Urdu": ["ادبِ جدید", "تحقیقی مضمون"],
143
- "Biology": [
144
- "Gaseous Exchange", "Homeostasis", "Coordination", "Support and Movement",
145
- "Reproduction", "Inheritance", "Man and His Environment", "Biotechnology", "Pharmacology"
146
- ],
147
- "Chemistry": [
148
- "Chemical Equilibrium", "Acids, Bases and Salts", "Organic Chemistry", "Hydrocarbons",
149
- "Biochemistry", "Environmental Chemistry I: The Atmosphere", "Environmental Chemistry II: Water", "Chemical Industries"
150
- ],
151
- "Physics": [
152
- "Simple Harmonic Motion and Waves", "Sound", "Geometrical Optics", "Electrostatics",
153
- "Current Electricity", "Electromagnetism", "Introductory Electronics",
154
- "Information and Communication Technology", "Radioactivity"
155
- ],
156
- "Islamiyat": ["Islamic Studies II", "Ethics & Society", "Fiqh Basics"],
157
- "Pakistan Studies": [
158
- "History of Pakistan-II", "Pakistan in World Affairs", "Economic Developments",
159
- "Population, Society and Culture of Pakistan"
160
- ]
161
- },
162
- "Grade 11": {
163
- "Mathematics": ["Advanced Algebra & Calculus Intro", "Coordinate Geometry", "Trigonometry", "Vectors"],
164
- "English": ["Academic Writing", "Literature Studies", "Critical Reading"],
165
- "Urdu": ["ادبی مطالعہ", "نظم و نثر کی تشریح"],
166
- "Biology": [
167
- "Cell Structure and Functions", "Biological Molecules", "Enzymes", "Bioenergetics",
168
- "Acellular Life", "Prokaryotes", "Protists and Fungi", "Diversity among Plants",
169
- "Diversity among Animals", "Form and Functions in Plants", "Digestion", "Circulation", "Immunity"
170
- ],
171
- "Chemistry": [
172
- "Stoichiometry", "Atomic Structure", "Theories of Covalent Bonding and Shapes of Molecules",
173
- "States of Matter I: Gases", "States of Matter II: Liquids", "States of Matter III: Solids",
174
- "Chemical Equilibrium", "Acids, Bases and Salts", "Chemical Kinetics",
175
- "Solutions and Colloids", "Thermochemistry", "Oxidation, Reduction and Electrochemistry"
176
- ],
177
- "Physics": [
178
- "Measurement", "Vectors and Equilibrium", "Forces and Motion", "Work and Energy",
179
- "Rotational and Circular Motion", "Fluid Dynamics", "Oscillations", "Waves",
180
- "Physical Optics", "Thermodynamics", "Electrostatics", "Current Electricity"
181
- ],
182
- "Computer": ["Programming Fundamentals", "Data Structures Intro", "Databases"],
183
- "Islamiyat": ["Advanced Seerah", "Comparative Religion"],
184
- "Pakistan Studies": [
185
- "Pakistan in Geographical Perspective", "Pakistan Resources", "Historical Perspective (Ancient to Modern)",
186
- "Islam and Pakistan", "Administrative System", "Political and Constitutional Developments", "Human Rights"
187
- ]
188
- },
189
- "Grade 12": {
190
- "Mathematics": ["Calculus", "Advanced Algebra", "Statistics", "Analytical Geometry"],
191
- "English": ["Research Writing", "Advanced Literature", "Critical Analysis"],
192
- "Urdu": ["تحقیق و تنقید", "ادبی شخصیات"],
193
- "Biology": [
194
- "Respiration", "Homeostasis", "Support and Movement", "Nervous Coordination",
195
- "Chemical Coordination", "Behavior", "Reproduction", "Development and Aging",
196
- "Inheritance", "Chromosome and DNA", "Evolution", "Man and His Environment",
197
- "Biotechnology", "Biology and Human Welfare"
198
- ],
199
- "Chemistry": [
200
- "s and p Block Elements", "d-Block Elements", "Organic Compounds", "Hydrocarbons",
201
- "Alkyl Halides and Amines", "Alcohols and Phenols", "Aldehydes and Ketones",
202
- "Carboxylic Acids and Functional Derivatives", "Biochemistry", "Industrial Chemistry",
203
- "Environmental Chemistry", "Analytical Chemistry"
204
- ],
205
- "Physics": [
206
- "Electromagnetism", "Electromagnetic Induction", "Alternating Current", "Physics of Solids",
207
- "Electronics", "Dawn of Modern Physics", "Atomic Spectra", "Nuclear Physics"
208
- ],
209
- "Computer": ["Web Technologies", "Advanced Programming Concepts"],
210
- "Islamiyat": ["Advanced Islamic Thought", "Islamic Jurisprudence"],
211
- "Pakistan Studies": [
212
- "Society and Culture", "Foreign Relations of Pakistan", "Economic Development",
213
- "Sports, Tourism and National Identity", "Contemporary Challenges and Policies"
214
- ]
215
- }
216
- }
217
-
218
- # ---------------------------
219
- # Question types for comprehensive assessment
220
- # ---------------------------
221
- QUESTION_TYPES = {
222
- "Multiple Choice Questions (MCQs)": {
223
- "code": "mcq",
224
- "description": "Four-option questions testing recall and application",
225
- "format": "Question with options A, B, C, D"
226
- },
227
- "Fill in the Blanks": {
228
- "code": "fill_blank",
229
- "description": "Complete sentences with missing key terms",
230
- "format": "Sentences with underlined blanks"
231
- },
232
- "Match the Column": {
233
- "code": "match_column",
234
- "description": "Connect related items from two columns",
235
- "format": "Two columns with items to match"
236
- },
237
- "Short Response Questions": {
238
- "code": "short_response",
239
- "description": "Brief written answers (2-3 sentences)",
240
- "format": "Questions requiring concise explanations"
241
- },
242
- "Essay Type Questions": {
243
- "code": "essay",
244
- "description": "Extended written responses with detailed analysis",
245
- "format": "Open-ended questions requiring paragraphs"
246
- }
247
- }
248
-
249
- # ---------------------------
250
- # Cognitive levels for item writing (Bloom's Taxonomy)
251
- # ---------------------------
252
- DIFFICULTY_LEVELS = {
253
- "Remembering": {
254
- "description": "Recall facts, terms, basic concepts",
255
- "keywords": ["define", "list", "identify", "name", "state", "recall"]
256
- },
257
- "Understanding": {
258
- "description": "Explain ideas or concepts",
259
- "keywords": ["explain", "describe", "summarize", "interpret", "paraphrase"]
260
- },
261
- "Applying": {
262
- "description": "Use information in new situations",
263
- "keywords": ["apply", "demonstrate", "solve", "use", "calculate", "implement"]
264
- },
265
- "Analyzing": {
266
- "description": "Draw connections, examine relationships",
267
- "keywords": ["analyze", "compare", "contrast", "differentiate", "examine"]
268
- },
269
- "Evaluating": {
270
- "description": "Justify decisions, make judgments",
271
- "keywords": ["evaluate", "justify", "critique", "assess", "judge"]
272
- },
273
- "Creating": {
274
- "description": "Produce new or original work",
275
- "keywords": ["create", "design", "construct", "develop", "formulate"]
276
- }
277
- }
278
-
279
- # ---------------------------
280
- # Enhanced question generation with Bloom's Taxonomy distribution
281
- # ---------------------------
282
- def generate_questions_multi_level(grade, subject, chapter, test_type, school_name, question_type,
283
- bloom_distribution, total_questions):
284
- """Generate questions with mixed cognitive levels based on user distribution"""
285
-
286
- all_questions = []
287
- all_answers = []
288
-
289
- try:
290
- # Calculate number of questions per level
291
- for level, percentage in bloom_distribution.items():
292
- if percentage > 0:
293
- num_q = int((percentage / 100) * total_questions)
294
- if num_q > 0:
295
- questions, answers, error, q_type_code = generate_questions(
296
- grade, subject, chapter, level,
297
- num_q, test_type, school_name, question_type
298
- )
299
-
300
- if error:
301
- return [], [], error, "mcq"
302
-
303
- all_questions.extend(questions)
304
- all_answers.extend(answers)
305
-
306
- # Ensure we have the right number of questions
307
- if len(all_questions) < total_questions:
308
- # Generate additional questions at first non-zero level
309
- shortage = total_questions - len(all_questions)
310
- first_level = next((k for k, v in bloom_distribution.items() if v > 0), "Understanding")
311
-
312
- extra_q, extra_a, error, q_type_code = generate_questions(
313
- grade, subject, chapter, first_level,
314
- shortage, test_type, school_name, question_type
315
- )
316
-
317
- if not error:
318
- all_questions.extend(extra_q)
319
- all_answers.extend(extra_a)
320
-
321
- return all_questions[:total_questions], all_answers[:total_questions], None, q_type_code
322
-
323
- except Exception as e:
324
- return [], [], f"Error in multi-level generation: {str(e)}", "mcq"
325
-
326
- # ---------------------------
327
- # Enhanced question generation with proper item writing principles
328
- # ---------------------------
329
- def generate_questions(grade, subject, chapter, difficulty, num_questions, test_type, school_name, question_type):
330
- """Generate questions using AI with enhanced item writing guidelines"""
331
-
332
- try:
333
- q_type_code = QUESTION_TYPES.get(question_type, {}).get("code", "mcq")
334
- is_mathematics = subject.lower() == "mathematics"
335
-
336
- # Extract grade number for level-specific prompts
337
- grade_num = int(grade.split()[-1]) if grade.startswith("Grade") else 6
338
-
339
- # Build type-specific prompts
340
- if q_type_code == "mcq":
341
- system_msg = """You are an expert educational item writer specializing in Multiple Choice Questions aligned with the Pakistani National Curriculum (2006).
342
- CRITICAL FORMATTING RULES:
343
- - Start DIRECTLY with question 1
344
- - DO NOT write introductory text like "Here are the questions" or similar
345
- - DO NOT number your output with "Q1." - the system will add numbering
346
- - Each question must be on its own, clean format
347
- Professional MCQ Principles:
348
- 1. STEM CLARITY: Write clear, concise question stems
349
- 2. PLAUSIBLE DISTRACTORS: Wrong options based on common misconceptions
350
- 3. GRAMMATICAL CONSISTENCY: All options match the stem
351
- 4. NO CUEING: Avoid "all/none of the above"
352
- 5. EQUAL LENGTH: Similar option lengths
353
- 6. AGE-APPROPRIATE: Vocabulary for grade level
354
- Format EXACTLY as:
355
- [Question stem]
356
- A) [Option 1]
357
- B) [Option 2]
358
- C) [Option 3]
359
- D) [Option 4]
360
- [Next question stem]
361
- A) [Option 1]
362
- B) [Option 2]
363
- C) [Option 3]
364
- D) [Option 4]
365
- After all questions, provide:
366
- ANSWER KEY:
367
- 1. B
368
- 2. A"""
369
-
370
- user_msg = f"""Create {num_questions} MCQs for {grade} {subject}, Chapter: {chapter}
371
- Cognitive Level: {difficulty}
372
- START DIRECTLY WITH THE FIRST QUESTION - NO INTRODUCTION TEXT.
373
- Align with Pakistani National Curriculum (2006).
374
- Use {difficulty} skills: {DIFFICULTY_LEVELS.get(difficulty, {}).get('description', '')}"""
375
-
376
- elif q_type_code == "fill_blank":
377
- system_msg = """You are an expert at creating Fill in the Blanks questions for Pakistani curriculum.
378
- CRITICAL: Start DIRECTLY with question 1. NO introductory text.
379
- Format EXACTLY as:
380
- [Sentence with __________ for the blank]
381
- [Next sentence with __________ for the blank]
382
- After all questions:
383
- ANSWERS:
384
- 1. [correct word/phrase]
385
- 2. [correct word/phrase]"""
386
-
387
- user_msg = f"""Create {num_questions} Fill in the Blanks for {grade} {subject}, Chapter: {chapter}
388
- Cognitive Level: {difficulty}
389
- Rules:
390
- - One blank per sentence marked with __________
391
- - Blank should test key concepts
392
- - Provide clear context clues
393
- - NO introduction - start with question 1 directly"""
394
-
395
- elif q_type_code == "match_column":
396
- system_msg = """You are an expert at creating Match the Column questions for Pakistani curriculum.
397
- CRITICAL: Start DIRECTLY with the matching sets. NO introductory text.
398
- Format EXACTLY as:
399
- Column A | Column B
400
- 1. [Item A1] | a. [Item B1]
401
- 2. [Item A2] | b. [Item B2]
402
- 3. [Item A3] | c. [Item B3]
403
- ANSWERS:
404
- 1-[letter], 2-[letter], 3-[letter]"""
405
-
406
- user_msg = f"""Create {num_questions//2} matching pairs for {grade} {subject}, Chapter: {chapter}
407
- Cognitive Level: {difficulty}
408
- Create two columns with related items (terms-definitions, concepts-examples, etc.)
409
- NO introduction - start directly with the table"""
410
-
411
- elif q_type_code == "short_response":
412
- if is_mathematics:
413
- # MATHEMATICS-SPECIFIC SHORT QUESTIONS
414
- system_msg = f"""You are an expert Mathematics teacher creating SHORT NUMERICAL/COMPUTATIONAL questions for {grade} aligned with Pakistani National Curriculum.
415
-
416
- CRITICAL RULES FOR MATHEMATICS SHORT QUESTIONS:
417
- 1. Create ACTUAL COMPUTATIONAL PROBLEMS - NOT descriptive/theoretical questions
418
- 2. Each question must require CALCULATIONS, SOLVING, or MATHEMATICAL WORKING
419
- 3. Questions must be SOLVABLE with specific numerical answers
420
- 4. NO questions like "Describe...", "Explain the significance...", "What is the importance..."
421
- 5. START DIRECTLY - NO introductory text
422
-
423
- QUESTION TYPES TO USE (based on grade level):
424
- """
425
- # Add grade-specific guidance
426
- if grade_num <= 5:
427
- system_msg += """
428
- - Simple arithmetic calculations (addition, subtraction, multiplication, division)
429
- - Word problems with numbers
430
- - Basic fraction operations
431
- - Simple geometry (find perimeter, area)
432
- - Money calculations
433
- """
434
- elif grade_num <= 8:
435
- system_msg += """
436
- - Algebraic expressions and simplification
437
- - Solving linear equations (1-2 steps)
438
- - Geometry problems (angles, areas, perimeters)
439
- - Ratio and proportion calculations
440
- - Percentage problems
441
- - Statistics (mean, median, mode)
442
- """
443
- else: # Grade 9-12
444
- system_msg += """
445
- - Solving algebraic equations and inequalities
446
- - Trigonometric calculations (find angles, sides)
447
- - Coordinate geometry problems
448
- - Calculus problems (differentiation, integration for Grade 11-12)
449
- - Matrix operations
450
- - Sequence and series
451
- - Probability calculations
452
- - Vector operations (for higher grades)
453
- """
454
-
455
- system_msg += """
456
- FORMAT EXACTLY AS:
457
- Solve: 3x + 5 = 20
458
- Find the area of a triangle with base 8 cm and height 6 cm.
459
- Calculate: (2/3) + (5/6)
460
-
461
- After all questions provide:
462
- SOLUTIONS WITH WORKING:
463
- 1. 3x + 5 = 20
464
- 3x = 20 - 5
465
- 3x = 15
466
- x = 5
467
-
468
- 2. Area = (1/2) × base × height
469
- Area = (1/2) × 8 × 6
470
- Area = 24 cm²
471
- """
472
-
473
- user_msg = f"""Create {num_questions} SHORT MATHEMATICAL PROBLEMS for {grade}, Chapter: {chapter}
474
- Cognitive Level: {difficulty}
475
-
476
- CRITICAL REQUIREMENTS:
477
- - Each question must be a SOLVABLE mathematical problem
478
- - Include specific numbers and require calculations
479
- - Mix of: equations, word problems, geometry, calculations
480
- - Difficulty appropriate for {grade}
481
- - Related to chapter: {chapter}
482
- - NO descriptive/theoretical questions
483
-
484
- START DIRECTLY WITH FIRST PROBLEM - NO INTRODUCTION"""
485
-
486
- else:
487
- # Non-mathematics subjects (original behavior)
488
- system_msg = """You are an expert at creating Short Response questions for Pakistani curriculum.
489
- CRITICAL: Start DIRECTLY with question 1. NO introductory text.
490
- Format EXACTLY as:
491
- [Question requiring 2-3 sentence answer]
492
- [Next question]
493
- After all questions:
494
- MARKING SCHEME:
495
- 1. Key points: [point 1], [point 2], [point 3]
496
- 2. Key points: [point 1], [point 2]"""
497
-
498
- user_msg = f"""Create {num_questions} Short Response questions for {grade} {subject}, Chapter: {chapter}
499
- Cognitive Level: {difficulty}
500
- Each answer should be 2-3 sentences (3-5 marks each)
501
- NO introduction - start with question 1 directly"""
502
-
503
- elif q_type_code == "essay":
504
- if is_mathematics:
505
- # MATHEMATICS-SPECIFIC LONG QUESTIONS
506
- system_msg = f"""You are an expert Mathematics teacher creating LONG NUMERICAL/PROOF questions for {grade} aligned with Pakistani National Curriculum.
507
-
508
- CRITICAL RULES FOR MATHEMATICS LONG QUESTIONS:
509
- 1. Create MULTI-STEP MATHEMATICAL PROBLEMS requiring detailed working
510
- 2. Each question must involve CALCULATIONS, PROOFS, DERIVATIONS, or COMPLEX PROBLEM-SOLVING
511
- 3. Questions must require showing complete mathematical working
512
- 4. NO descriptive essays like "Write an essay on...", "Discuss the history of..."
513
- 5. START DIRECTLY - NO introductory text
514
-
515
- QUESTION TYPES TO USE (based on grade level):
516
- """
517
- # Add grade-specific guidance
518
- if grade_num <= 5:
519
- system_msg += """
520
- - Multi-step word problems
521
- - Combined operations problems
522
- - Detailed geometry problems (multiple shapes)
523
- - Problems requiring multiple calculations
524
- """
525
- elif grade_num <= 8:
526
- system_msg += """
527
- - Solve and verify equations
528
- - Word problems with multiple steps
529
- - Geometric proofs (basic)
530
- - Complete problems with parts (a), (b), (c)
531
- - Data handling with multiple calculations
532
- - Graph drawing and interpretation
533
- """
534
- else: # Grade 9-12
535
- system_msg += """
536
- - Multi-part algebraic problems with proof
537
- - Trigonometric identities to prove
538
- - Calculus problems (derivatives, integrals for Grade 11-12)
539
- - Coordinate geometry with complete derivation
540
- - Vector problems with multiple parts
541
- - Matrix operations and determinants
542
- - Complex word problems requiring modeling
543
- - Geometric constructions and proofs
544
- - Sequence and series with proofs
545
- - Probability with multiple scenarios
546
- """
547
-
548
- system_msg += """
549
- FORMAT FOR MULTI-PART QUESTIONS:
550
- Question 1:
551
- (a) Solve the equation: 2x² - 5x - 3 = 0
552
- (b) Verify your solutions by substituting back
553
- (c) Graph the quadratic function
554
-
555
- Question 2:
556
- Prove that: sin²θ + cos²θ = 1
557
- [Show complete algebraic proof]
558
-
559
- After all questions provide:
560
- DETAILED SOLUTIONS:
561
- 1. (a) Using quadratic formula:
562
- x = [-b ± √(b² - 4ac)] / 2a
563
- [complete working shown step by step]
564
-
565
- (b) Verification:
566
- [substitution and checking]
567
-
568
- (c) [Graph description with key points]
569
-
570
- MARKING SCHEME:
571
- - Method: X marks
572
- - Calculations: Y marks
573
- - Final answer: Z marks
574
- """
575
-
576
- user_msg = f"""Create {num_questions} LONG MATHEMATICAL PROBLEMS for {grade}, Chapter: {chapter}
577
- Cognitive Level: {difficulty}
578
-
579
- CRITICAL REQUIREMENTS:
580
- - Each question must be a COMPLEX SOLVABLE mathematical problem
581
- - Include multi-step calculations, proofs, or derivations
582
- - Can have multiple parts (a), (b), (c)
583
- - Appropriate difficulty for {grade}
584
- - Related to chapter: {chapter}
585
- - Show complete working in solutions
586
- - NO essay-type descriptive questions
587
-
588
- Types to include:
589
- - Numerical problems with multiple steps
590
- - Proofs of identities/theorems (for higher grades)
591
- - Word problems requiring mathematical modeling
592
- - Problems combining multiple concepts
593
-
594
- START DIRECTLY WITH FIRST PROBLEM - NO INTRODUCTION"""
595
-
596
- else:
597
- # Non-mathematics subjects (original behavior)
598
- system_msg = """You are an expert at creating Essay questions for Pakistani curriculum.
599
- CRITICAL: Start DIRECTLY with question 1. NO introductory text.
600
- Format EXACTLY as:
601
- [Essay question requiring detailed analysis]
602
- [Next essay question]
603
- After all questions:
604
- MARKING CRITERIA:
605
- 1. Expected coverage: [main points to cover]
606
- 2. Expected coverage: [main points to cover]"""
607
-
608
- user_msg = f"""Create {num_questions} Essay questions for {grade} {subject}, Chapter: {chapter}
609
- Cognitive Level: {difficulty}
610
- Each answer should be 1-2 paragraphs (8-10 marks each)
611
- Require analysis, evaluation, or synthesis
612
- NO introduction - start with question 1 directly"""
613
-
614
- # Call Groq API
615
- response = client.chat.completions.create(
616
- messages=[
617
- {"role": "system", "content": system_msg},
618
- {"role": "user", "content": user_msg}
619
- ],
620
- model=MODEL_NAME,
621
- temperature=0.7,
622
- max_tokens=4000
623
- )
624
-
625
- text = response.choices[0].message.content
626
-
627
- # Clean up any introductory text
628
- text = clean_ai_response(text)
629
-
630
- # Parse based on question type
631
- if q_type_code == "mcq":
632
- questions, answers = parse_mcqs_and_answers(text, num_questions)
633
- elif q_type_code == "fill_blank":
634
- questions, answers = parse_fill_blanks(text, num_questions)
635
- elif q_type_code == "match_column":
636
- questions, answers = parse_match_column(text)
637
- elif q_type_code == "short_response":
638
- questions, answers = parse_short_response(text, num_questions)
639
- elif q_type_code == "essay":
640
- questions, answers = parse_essay(text, num_questions)
641
- else:
642
- questions, answers = [], []
643
-
644
- return questions, answers, None, q_type_code
645
-
646
- except Exception as e:
647
- error_msg = f"Error generating questions: {str(e)}"
648
- return [], [], error_msg, "mcq"
649
-
650
- def clean_ai_response(text):
651
- """Remove common AI introductory phrases"""
652
- # Patterns to remove
653
- patterns = [
654
- r'^Here\s+(are|is)\s+the\s+\d+\s+.*?:?\s*\n*',
655
- r'^Below\s+(are|is)\s+.*?:?\s*\n*',
656
- r'^I\'ve\s+created\s+.*?:?\s*\n*',
657
- r'^The\s+following\s+(are|is)\s+.*?:?\s*\n*',
658
- r'^Q1\.\s+Here.*?:\s*\n*',
659
- ]
660
-
661
- for pattern in patterns:
662
- text = re.sub(pattern, '', text, flags=re.IGNORECASE | re.MULTILINE)
663
-
664
- return text.strip()
665
-
666
- def parse_mcqs_and_answers(text, expected_count):
667
- """Parse AI response to extract MCQs and answer key"""
668
-
669
- # Split by answer key section
670
- parts = re.split(r'ANSWER\s+KEY:?', text, flags=re.IGNORECASE)
671
-
672
- mcqs = []
673
- answer_key = []
674
-
675
- if len(parts) >= 1:
676
- # Extract MCQs
677
- mcq_text = parts[0]
678
-
679
- # Remove any "Q#." prefixes and split by question patterns
680
- mcq_text = re.sub(r'^\s*Q\d+\.\s*', '', mcq_text, flags=re.MULTILINE)
681
-
682
- # Split by numbered questions (1., 2., etc.) but keep the content
683
- questions = re.split(r'\n\s*\d+\.\s+', mcq_text)
684
- questions = [q.strip() for q in questions if q.strip() and len(q.strip()) > 20]
685
- mcqs = questions[:expected_count]
686
-
687
- if len(parts) >= 2:
688
- # Extract answer key
689
- answer_text = parts[1]
690
- # Find patterns like "1. B" or "1) B" or "1: B"
691
- answers = re.findall(r'\d+[\.\):\s]+([A-D])', answer_text, re.IGNORECASE)
692
- answer_key = [ans.upper() for ans in answers[:expected_count]]
693
-
694
- # If parsing failed, create placeholder
695
- if len(answer_key) < len(mcqs):
696
- answer_key.extend(['A'] * (len(mcqs) - len(answer_key)))
697
-
698
- return mcqs, answer_key
699
-
700
- def parse_fill_blanks(text, expected_count):
701
- """Parse Fill in the Blanks questions"""
702
- parts = re.split(r'ANSWERS?:?', text, flags=re.IGNORECASE)
703
-
704
- questions = []
705
- answers = []
706
-
707
- if len(parts) >= 1:
708
- q_text = parts[0]
709
- # Split by line breaks, filter empty lines
710
- lines = [line.strip() for line in q_text.split('\n') if line.strip() and '_' in line]
711
- questions = lines[:expected_count]
712
-
713
- if len(parts) >= 2:
714
- a_text = parts[1]
715
- # Extract answers
716
- ans_lines = re.findall(r'\d+[\.\):\s]+(.+)', a_text)
717
- answers = [ans.strip() for ans in ans_lines[:expected_count]]
718
-
719
- if len(answers) < len(questions):
720
- answers.extend(['_____'] * (len(questions) - len(answers)))
721
-
722
- return questions, answers
723
-
724
- def parse_match_column(text):
725
- """Parse Match the Column questions"""
726
- parts = re.split(r'ANSWERS?:?', text, flags=re.IGNORECASE)
727
-
728
- questions = []
729
- answers = []
730
-
731
- if len(parts) >= 1:
732
- # Extract the table
733
- questions.append(parts[0].strip())
734
-
735
- if len(parts) >= 2:
736
- # Extract matching answers
737
- answers.append(parts[1].strip())
738
-
739
- return questions, answers
740
-
741
- def parse_short_response(text, expected_count):
742
- """Parse Short Response questions - separating questions from expected answers"""
743
- # Split by expected answers section
744
- parts = re.split(r'EXPECTED\s+ANSWERS?:?|MARKING\s+SCHEME:?|SOLUTIONS?\s+WITH\s+WORKING:?', text, flags=re.IGNORECASE)
745
-
746
- questions = []
747
- answers = []
748
-
749
- if len(parts) >= 1:
750
- q_text = parts[0]
751
- # Split by numbered patterns
752
- qs = re.split(r'\n\s*\d+[\.\)]\s+', q_text)
753
- questions = [q.strip() for q in qs if q.strip() and len(q.strip()) > 10][:expected_count]
754
-
755
- if len(parts) >= 2:
756
- a_text = parts[1]
757
- # Extract expected answers/key points
758
- schemes = re.findall(r'\d+[\.\)]\s*(.+?)(?=\d+[\.\)]|$)', a_text, re.DOTALL)
759
- answers = [s.strip() for s in schemes[:expected_count]]
760
-
761
- if len(answers) < len(questions):
762
- answers.extend(['Key points: Provide comprehensive explanation with examples'] * (len(questions) - len(answers)))
763
-
764
- return questions, answers
765
-
766
- def parse_essay(text, expected_count):
767
- """Parse Essay questions - separating questions from expected answers"""
768
- # Split by expected answers section
769
- parts = re.split(r'EXPECTED\s+ANSWERS?:?|MARKING\s+CRITERIA:?|DETAILED\s+SOLUTIONS?:?', text, flags=re.IGNORECASE)
770
-
771
- questions = []
772
- answers = []
773
-
774
- if len(parts) >= 1:
775
- q_text = parts[0]
776
- # Split by numbered patterns or "Question X:"
777
- qs = re.split(r'\n\s*(?:\d+[\.\)]|Question\s+\d+:)\s+', q_text, flags=re.IGNORECASE)
778
- questions = [q.strip() for q in qs if q.strip() and len(q.strip()) > 15][:expected_count]
779
-
780
- if len(parts) >= 2:
781
- a_text = parts[1]
782
- # Extract criteria/expected coverage
783
- criteria = re.findall(r'(?:\d+[\.\)]|Question\s+\d+:)\s*(.+?)(?=(?:\d+[\.\)]|Question\s+\d+:)|$)', a_text, re.DOTALL | re.IGNORECASE)
784
- answers = [c.strip() for c in criteria[:expected_count]]
785
-
786
- if len(answers) < len(questions):
787
- answers.extend(['Main points: Detailed analysis with evidence, examples, and logical argumentation'] * (len(questions) - len(answers)))
788
-
789
- return questions, answers
790
-
791
- def create_answer_table(doc, answers, num_cols=5):
792
- """Helper function to create formatted answer table"""
793
- num_rows = len(answers) + 1
794
- ans_table = doc.add_table(rows=num_rows, cols=num_cols)
795
- ans_table.style = 'Medium Grid 1 Accent 1'
796
-
797
- # Set column widths
798
- for row in ans_table.rows:
799
- for idx, cell in enumerate(row.cells):
800
- if idx == 0:
801
- cell.width = Inches(0.6)
802
- else:
803
- cell.width = Inches(1.2)
804
-
805
- return ans_table
806
-
807
- # ---------------------------
808
- # Enhanced DOCX export supporting all question types
809
- # ---------------------------
810
- def export_to_word(questions, answers, grade, subject, chapter, difficulty, test_type, school_name, total_marks, question_type_code):
811
- """Export questions to professionally formatted Word document"""
812
-
813
- try:
814
- doc = Document()
815
-
816
- # Set margins
817
- sections = doc.sections
818
- for section in sections:
819
- section.top_margin = Inches(0.8)
820
- section.bottom_margin = Inches(0.8)
821
- section.left_margin = Inches(0.9)
822
- section.right_margin = Inches(0.9)
823
-
824
- # Header with school name
825
- if school_name and school_name.strip():
826
- school_heading = doc.add_heading(school_name.upper(), level=0)
827
- school_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
828
- for run in school_heading.runs:
829
- run.font.size = Pt(16)
830
- run.font.color.rgb = RGBColor(0, 51, 102)
831
-
832
- # Test title
833
- test_title = doc.add_heading(f"{test_type.upper()}", level=1)
834
- test_title.alignment = WD_ALIGN_PARAGRAPH.CENTER
835
- for run in test_title.runs:
836
- run.bold = True
837
- run.font.color.rgb = RGBColor(0, 102, 51)
838
- run.font.size = Pt(16)
839
-
840
- # Information table
841
- table = doc.add_table(rows=7, cols=2)
842
- table.style = 'Light Grid Accent 1'
843
-
844
- # Get question type name
845
- q_type_name = next((k for k, v in QUESTION_TYPES.items() if v['code'] == question_type_code), "MCQs")
846
-
847
- info_data = [
848
- ("Grade:", grade),
849
- ("Subject:", subject),
850
- ("Chapter:", chapter),
851
- ("Question Type:", q_type_name),
852
- ("Cognitive Level:", difficulty),
853
- ("Total Marks:", str(total_marks)),
854
- ("Date:", datetime.now().strftime("%d-%m-%Y"))
855
- ]
856
-
857
- for idx, (label, value) in enumerate(info_data):
858
- row = table.rows[idx]
859
- row.cells[0].text = label
860
- row.cells[1].text = value
861
- row.cells[0].paragraphs[0].runs[0].bold = True
862
- row.cells[0].paragraphs[0].runs[0].font.size = Pt(10)
863
- row.cells[1].paragraphs[0].runs[0].font.size = Pt(10)
864
-
865
- # Type-specific instructions
866
- doc.add_paragraph()
867
- instructions = doc.add_paragraph()
868
- instructions.add_run("INSTRUCTIONS:").bold = True
869
-
870
- if question_type_code == "mcq":
871
- doc.add_paragraph("• Read each question carefully before selecting your answer")
872
- doc.add_paragraph("• Each question carries equal marks")
873
- doc.add_paragraph("• Select the MOST appropriate answer from the given options")
874
- elif question_type_code == "fill_blank":
875
- doc.add_paragraph("• Fill in the blanks with appropriate words/phrases")
876
- doc.add_paragraph("• Write clearly and legibly")
877
- doc.add_paragraph("• Each blank carries equal marks")
878
- elif question_type_code == "match_column":
879
- doc.add_paragraph("• Match items from Column A with Column B")
880
- doc.add_paragraph("• Write the letter of the correct match")
881
- doc.add_paragraph("• Each correct match carries equal marks")
882
- elif question_type_code == "short_response":
883
- if subject.lower() == "mathematics":
884
- doc.add_paragraph("• Show all your working clearly")
885
- doc.add_paragraph("• Write the final answer clearly")
886
- doc.add_paragraph("• Marks are awarded for method and accuracy")
887
- else:
888
- doc.add_paragraph("• Answer in 2-3 complete sentences")
889
- doc.add_paragraph("• Be concise and clear")
890
- doc.add_paragraph("• Each question carries 3-5 marks")
891
- elif question_type_code == "essay":
892
- if subject.lower() == "mathematics":
893
- doc.add_paragraph("• Show complete working for all calculations")
894
- doc.add_paragraph("• Write proofs/derivations step-by-step")
895
- doc.add_paragraph("• Label diagrams clearly if required")
896
- doc.add_paragraph("• Each question may have multiple parts")
897
- else:
898
- doc.add_paragraph("• Write detailed, well-organized paragraphs")
899
- doc.add_paragraph("• Support your answers with relevant examples")
900
- doc.add_paragraph("• Each question carries 8-10 marks")
901
-
902
- # Questions section
903
- doc.add_paragraph()
904
- section_title = "SECTION A: " + q_type_name.upper()
905
- questions_heading = doc.add_paragraph()
906
- questions_heading.add_run(section_title).bold = True
907
- questions_heading.runs[0].font.size = Pt(12)
908
- doc.add_paragraph()
909
-
910
- # Add questions based on type
911
- if question_type_code == "mcq":
912
- for i, q in enumerate(questions, start=1):
913
- q_para = doc.add_paragraph()
914
- q_para.add_run(f"{i}. ").bold = True
915
- q_para.add_run(q)
916
- q_para.space_after = Pt(8)
917
- doc.add_paragraph()
918
-
919
- elif question_type_code == "fill_blank":
920
- for i, q in enumerate(questions, start=1):
921
- q_para = doc.add_paragraph()
922
- q_para.add_run(f"{i}. ").bold = True
923
- q_para.add_run(q)
924
- q_para.space_after = Pt(8)
925
- doc.add_paragraph()
926
-
927
- elif question_type_code == "match_column":
928
- for q in questions:
929
- doc.add_paragraph(q)
930
-
931
- elif question_type_code in ["short_response", "essay"]:
932
- marks_per_q = 5 if question_type_code == "short_response" else 10
933
-
934
- # For mathematics, provide space for working
935
- working_lines = 5 if question_type_code == "short_response" else 12
936
-
937
- for i, q in enumerate(questions, start=1):
938
- q_para = doc.add_paragraph()
939
- q_para.add_run(f"Q{i}. ").bold = True
940
- q_para.add_run(q)
941
- q_para.add_run(f" [{marks_per_q} marks]").italic = True
942
- doc.add_paragraph()
943
-
944
- # Add space for answer
945
- if subject.lower() == "mathematics":
946
- # Add "Working:" label for math questions
947
- working_para = doc.add_paragraph()
948
- working_para.add_run("Working:").italic = True
949
-
950
- for _ in range(working_lines):
951
- doc.add_paragraph("_" * 85)
952
- doc.add_paragraph()
953
-
954
- # Answer key (on separate page)
955
- doc.add_page_break()
956
-
957
- ans_heading = doc.add_heading("ANSWER KEY / MARKING SCHEME", level=2)
958
- ans_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
959
- for run in ans_heading.runs:
960
- run.font.color.rgb = RGBColor(153, 0, 0)
961
-
962
- doc.add_paragraph("(For Teacher's Use Only)", style='Intense Quote')
963
- doc.add_paragraph()
964
-
965
- # Add answers based on type
966
- if question_type_code == "mcq":
967
- # Answer table for MCQs
968
- ans_table = create_answer_table(doc, answers, 5)
969
-
970
- headers = ["Q#", "A", "B", "C", "D"]
971
- for idx, header in enumerate(headers):
972
- cell = ans_table.cell(0, idx)
973
- cell.text = header
974
- cell.paragraphs[0].runs[0].bold = True
975
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
976
-
977
- for r, ans in enumerate(answers, start=1):
978
- ans_table.cell(r, 0).text = str(r)
979
- ans_table.cell(r, 0).paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
980
-
981
- for c, option in enumerate(["A", "B", "C", "D"], start=1):
982
- cell = ans_table.cell(r, c)
983
- if ans == option:
984
- cell.text = "✓"
985
- cell.paragraphs[0].runs[0].bold = True
986
- cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(0, 128, 0)
987
- cell.paragraphs[0].runs[0].font.size = Pt(14)
988
- else:
989
- cell.text = ""
990
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
991
-
992
- elif question_type_code == "fill_blank":
993
- # Tabular answer key for Fill in the Blanks
994
- ans_table = create_answer_table(doc, answers, 2)
995
-
996
- # Headers
997
- headers = ["Q#", "Correct Answer"]
998
- for idx, header in enumerate(headers):
999
- cell = ans_table.cell(0, idx)
1000
- cell.text = header
1001
- cell.paragraphs[0].runs[0].bold = True
1002
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
1003
-
1004
- # Fill answers
1005
- for r, ans in enumerate(answers, start=1):
1006
- ans_table.cell(r, 0).text = str(r)
1007
- ans_table.cell(r, 0).paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
1008
- ans_table.cell(r, 1).text = str(ans)
1009
-
1010
- elif question_type_code in ["short_response", "essay"]:
1011
- # Expected answers for subjective questions
1012
- for i, ans in enumerate(answers, start=1):
1013
- ans_para = doc.add_paragraph()
1014
- if subject.lower() == "mathematics":
1015
- ans_para.add_run(f"Q{i}. Solution / Marking Scheme:").bold = True
1016
- else:
1017
- ans_para.add_run(f"Q{i}. Expected Answer:").bold = True
1018
- doc.add_paragraph(str(ans))
1019
- doc.add_paragraph()
1020
-
1021
- else:
1022
- # Text-based answers for other types
1023
- for i, ans in enumerate(answers, start=1):
1024
- ans_para = doc.add_paragraph()
1025
- ans_para.add_run(f"{i}. ").bold = True
1026
- ans_para.add_run(str(ans))
1027
- doc.add_paragraph()
1028
-
1029
- # Footer
1030
- doc.add_paragraph()
1031
- footer = doc.add_paragraph()
1032
- 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
1033
- footer.runs[0].font.size = Pt(8)
1034
- footer.runs[0].font.color.rgb = RGBColor(128, 128, 128)
1035
- footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
1036
-
1037
- # Save file
1038
- safe_chapter = re.sub(r'[\\/*?:"<>|]', "_", chapter)
1039
- safe_school = re.sub(r'[\\/*?:"<>|]', "_", school_name) if school_name else "School"
1040
- safe_qtype = re.sub(r'[\\/*?:"<>|]', "_", q_type_name)
1041
- filename = f"{safe_school}_{grade}_{subject}_{safe_chapter}_{safe_qtype}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.docx"
1042
-
1043
- doc.save(filename)
1044
- return filename, None
1045
-
1046
- except Exception as e:
1047
- return None, f"Error creating document: {str(e)}"
1048
-
1049
- # ---------------------------
1050
- # Main generation handler with Bloom's Taxonomy distribution
1051
- # ---------------------------
1052
- def on_generate(grade, subject, chapter, test_type, school_name, question_type,
1053
- use_bloom_mix, bloom_remember, bloom_understand, bloom_apply,
1054
- bloom_analyze, bloom_evaluate, bloom_create, num_questions):
1055
- """Handle question generation request with optional Bloom's distribution"""
1056
-
1057
- # Validation
1058
- if not all([grade, subject, chapter, question_type]):
1059
- return "⚠️ Please fill all required fields", None, None
1060
-
1061
- if not school_name or not school_name.strip():
1062
- school_name = "Educational Institution"
1063
-
1064
- try:
1065
- # Check if using Bloom's mix
1066
- if use_bloom_mix:
1067
- # Validate percentages
1068
- total_percent = bloom_remember + bloom_understand + bloom_apply + bloom_analyze + bloom_evaluate + bloom_create
1069
- if total_percent != 100:
1070
- return f"❌ Bloom's Taxonomy percentages must total 100% (currently {total_percent}%)", None, None
1071
-
1072
- bloom_distribution = {
1073
- "Remembering": bloom_remember,
1074
- "Understanding": bloom_understand,
1075
- "Applying": bloom_apply,
1076
- "Analyzing": bloom_analyze,
1077
- "Evaluating": bloom_evaluate,
1078
- "Creating": bloom_create
1079
- }
1080
-
1081
- # Show progress
1082
- status_msg = f"🔄 Generating {num_questions} {question_type} with mixed cognitive levels...\n"
1083
- status_msg += f"📊 Bloom's Distribution:\n"
1084
- for level, pct in bloom_distribution.items():
1085
- if pct > 0:
1086
- status_msg += f" • {level}: {pct}% ({int(pct * num_questions / 100)} questions)\n"
1087
-
1088
- # Generate with mixed levels
1089
- questions, answers, error, q_type_code = generate_questions_multi_level(
1090
- grade, subject, chapter, test_type, school_name, question_type,
1091
- bloom_distribution, num_questions
1092
- )
1093
- else:
1094
- # Single level generation (default to Understanding)
1095
- status_msg = f"🔄 Generating {num_questions} {question_type}...\n"
1096
- status_msg += f"📖 Grade: {grade} | Subject: {subject}\n"
1097
- status_msg += f"📚 Chapter: {chapter}\n"
1098
- status_msg += f"🎯 Cognitive Level: Understanding\n"
1099
- status_msg += "⏳ Please wait..."
1100
-
1101
- questions, answers, error, q_type_code = generate_questions(
1102
- grade, subject, chapter, "Understanding",
1103
- num_questions, test_type, school_name, question_type
1104
- )
1105
-
1106
- if error:
1107
- return f"❌ Error: {error}", None, None
1108
-
1109
- if not questions or len(questions) == 0:
1110
- return "❌ No questions generated. Please try again.", None, None
1111
-
1112
- # Calculate marks based on question type
1113
- if q_type_code == "mcq":
1114
- marks_per_q = 1
1115
- elif q_type_code == "fill_blank":
1116
- marks_per_q = 2
1117
- elif q_type_code == "match_column":
1118
- marks_per_q = len(questions) * 0.5
1119
- elif q_type_code == "short_response":
1120
- marks_per_q = 5
1121
- elif q_type_code == "essay":
1122
- marks_per_q = 10
1123
- else:
1124
- marks_per_q = 1
1125
-
1126
- total_marks = int(len(questions) * marks_per_q)
1127
-
1128
- # Export to Word
1129
- file_path, doc_error = export_to_word(
1130
- questions, answers, grade, subject, chapter,
1131
- "Mixed Cognitive Levels" if use_bloom_mix else "Understanding",
1132
- test_type, school_name, total_marks, q_type_code
1133
- )
1134
-
1135
- if doc_error:
1136
- return f"❌ {doc_error}", None, None
1137
-
1138
- # Success message
1139
- success_msg = f"✅ Test generated successfully!\n\n"
1140
- success_msg += f"📊 Test Statistics:\n"
1141
- success_msg += f"• Question Type: {question_type}\n"
1142
- success_msg += f"• Total Questions: {len(questions)}\n"
1143
- success_msg += f"• Total Marks: {total_marks}\n"
1144
- success_msg += f"• Grade: {grade} | Subject: {subject}\n"
1145
- success_msg += f"• Chapter: {chapter}\n"
1146
-
1147
- if use_bloom_mix:
1148
- success_msg += f"\n🎯 Bloom's Taxonomy Distribution:\n"
1149
- for level, pct in bloom_distribution.items():
1150
- if pct > 0:
1151
- success_msg += f" • {level}: {pct}%\n"
1152
-
1153
- success_msg += f"\n📥 Download your test document below"
1154
-
1155
- # Preview
1156
- preview_count = min(3, len(questions))
1157
- preview = f"\n\n📝 PREVIEW (First {preview_count} Questions):\n" + "="*50 + "\n\n"
1158
-
1159
- for i, q in enumerate(questions[:preview_count], 1):
1160
- if q_type_code == "mcq":
1161
- preview += f"{i}. {q[:200]}...\n\n" if len(q) > 200 else f"{i}. {q}\n\n"
1162
- else:
1163
- preview += f"{i}. {q[:200]}...\n\n" if len(q) > 200 else f"{i}. {q}\n\n"
1164
-
1165
- return success_msg + preview, file_path, f"🎉 File ready: {file_path}"
1166
-
1167
- except Exception as e:
1168
- return f"❌ Unexpected error: {str(e)}", None, None
1169
-
1170
- # ---------------------------
1171
- # Enhanced Gradio UI
1172
- # ---------------------------
1173
- def create_ui():
1174
- """Create professional Gradio interface"""
1175
-
1176
- with gr.Blocks(
1177
- theme=gr.themes.Soft(
1178
- primary_hue="green",
1179
- secondary_hue="blue",
1180
- ),
1181
- css="""
1182
- .header {text-align: center; margin-bottom: 20px;}
1183
- .description {text-align: center; color: #666; margin-bottom: 30px;}
1184
- .footer {text-align: center; margin-top: 30px; padding: 20px; color: #888;}
1185
- .warning {background-color: #fff3cd; padding: 10px; border-radius: 5px; margin: 10px 0;}
1186
- """
1187
- ) as demo:
1188
-
1189
- # Header
1190
- gr.Markdown(
1191
- """
1192
- # 📚 Test Generator Pro
1193
- ### **Developer: Najaf Ali Sharqi**
1194
-
1195
- Professional Assessment Tool Aligned with Pakistani National Curriculum (2006)
1196
- """,
1197
- elem_classes="header"
1198
- )
1199
-
1200
- gr.Markdown(
1201
- """
1202
- Generate high-quality assessment questions with multiple formats and Bloom's Taxonomy integration.
1203
- Perfect for educators creating comprehensive tests for Grades 1-12.
1204
-
1205
- **✨ New: Enhanced Mathematics Question Generation** - Creates actual computational problems, equations, and proofs!
1206
- """,
1207
- elem_classes="description"
1208
- )
1209
-
1210
- with gr.Row():
1211
- with gr.Column(scale=1):
1212
- gr.Markdown("### 📋 Test Configuration")
1213
-
1214
- school_name = gr.Textbox(
1215
- label="School/Institution Name",
1216
- placeholder="Enter your school name (optional)",
1217
- value="",
1218
- info="Will appear on the test document header"
1219
- )
1220
-
1221
- test_type = gr.Dropdown(
1222
- choices=[
1223
- "Weekly Test",
1224
- "Monthly Test",
1225
- "Mid-Term Examination",
1226
- "Final Examination",
1227
- "Practice Test",
1228
- "Mock Examination"
1229
- ],
1230
- value="Monthly Test",
1231
- label="Test Type",
1232
- info="Select the type of assessment"
1233
- )
1234
-
1235
- question_type = gr.Dropdown(
1236
- choices=list(QUESTION_TYPES.keys()),
1237
- value="Multiple Choice Questions (MCQs)",
1238
- label="Question Type",
1239
- info="Select the format of questions to generate"
1240
- )
1241
-
1242
- # Show description of selected question type
1243
- question_type_info = gr.Markdown(
1244
- """
1245
- **Description:** Four-option questions testing recall and application
1246
-
1247
- **Format:** Question with options A, B, C, D
1248
- """,
1249
- visible=True
1250
- )
1251
-
1252
- with gr.Row():
1253
- grade_dropdown = gr.Dropdown(
1254
- choices=grades,
1255
- label="Grade Level",
1256
- info="Select student grade level"
1257
- )
1258
-
1259
- num_questions = gr.Slider(
1260
- minimum=5,
1261
- maximum=50,
1262
- value=20,
1263
- step=5,
1264
- label="Number of Questions",
1265
- info="How many questions to generate"
1266
- )
1267
-
1268
- subject_dropdown = gr.Dropdown(
1269
- choices=[],
1270
- label="Subject",
1271
- info="Select subject based on grade"
1272
- )
1273
-
1274
- chapter_dropdown = gr.Dropdown(
1275
- choices=[],
1276
- label="Chapter/Unit",
1277
- info="Select specific chapter or unit"
1278
- )
1279
-
1280
- # Bloom's Taxonomy Distribution Section
1281
- use_bloom_mix = gr.Checkbox(
1282
- label="📊 Use Mixed Bloom's Taxonomy Levels",
1283
- value=False,
1284
- info="Create tests with questions from multiple cognitive levels"
1285
- )
1286
-
1287
- bloom_percentages = gr.Column(visible=False)
1288
-
1289
- with bloom_percentages:
1290
- gr.Markdown("### Set Percentage for Each Cognitive Level")
1291
- gr.Markdown("*(Total must equal 100%)*")
1292
-
1293
- with gr.Row():
1294
- bloom_remember = gr.Slider(
1295
- minimum=0, maximum=100, value=30, step=5,
1296
- label="Remembering %",
1297
- info="Recall facts and basic concepts"
1298
- )
1299
- bloom_understand = gr.Slider(
1300
- minimum=0, maximum=100, value=30, step=5,
1301
- label="Understanding %",
1302
- info="Explain ideas and concepts"
1303
- )
1304
-
1305
- with gr.Row():
1306
- bloom_apply = gr.Slider(
1307
- minimum=0, maximum=100, value=20, step=5,
1308
- label="Applying %",
1309
- info="Use information in new situations"
1310
- )
1311
- bloom_analyze = gr.Slider(
1312
- minimum=0, maximum=100, value=10, step=5,
1313
- label="Analyzing %",
1314
- info="Draw connections and relationships"
1315
- )
1316
-
1317
- with gr.Row():
1318
- bloom_evaluate = gr.Slider(
1319
- minimum=0, maximum=100, value=10, step=5,
1320
- label="Evaluating %",
1321
- info="Justify decisions and make judgments"
1322
- )
1323
- bloom_create = gr.Slider(
1324
- minimum=0, maximum=100, value=0, step=5,
1325
- label="Creating %",
1326
- info="Produce new or original work"
1327
- )
1328
-
1329
- bloom_total = gr.Textbox(
1330
- label="Total Percentage",
1331
- value="100%",
1332
- interactive=False
1333
- )
1334
-
1335
- difficulty_dropdown = gr.Dropdown(
1336
- choices=list(DIFFICULTY_LEVELS.keys()),
1337
- value="Understanding",
1338
- label="Single Cognitive Level (if not using mix)",
1339
- info="Select one level for entire test",
1340
- visible=True
1341
- )
1342
-
1343
- gr.Markdown(
1344
- """
1345
- <div class="warning">
1346
- ⚠️ <strong>Note:</strong> Ensure GROQ_API_KEY is set in environment variables
1347
- </div>
1348
- """
1349
- )
1350
-
1351
- generate_btn = gr.Button(
1352
- "🚀 Generate Test",
1353
- variant="primary",
1354
- size="lg"
1355
- )
1356
-
1357
- gr.Markdown("---")
1358
-
1359
- gr.Markdown(
1360
- """
1361
- ### 💡 Tips for Best Results:
1362
- - **MCQs**: Best for quick assessment and objective grading
1363
- - **Fill in the Blanks**: Tests specific terminology and facts
1364
- - **Match the Column**: Assesses relationships and connections
1365
- - **Short Response**: Evaluates understanding and explanation
1366
- - *Mathematics*: Generates actual calculation problems and equations
1367
- - **Essay Questions**: Measures critical thinking and synthesis
1368
- - *Mathematics*: Creates multi-step problems, proofs, and derivations
1369
- - **Bloom's Mix**: Use for comprehensive assessment across cognitive levels
1370
- - Review generated questions before final use
1371
-
1372
- ### 🔢 Mathematics Question Enhancement:
1373
- - Short questions now include actual numerical problems, equations, and calculations
1374
- - Long questions feature multi-step problems, proofs, and detailed derivations
1375
- - Aligned with FBISE and provincial board exam patterns
1376
- - Grade-appropriate complexity (Middle, Secondary, Higher Secondary)
1377
-
1378
- ### 📧 Custom App Development
1379
- **Need a customized assessment tool?**
1380
- Contact developer **Najaf Ali Sharqi** for:
1381
- - Custom question banks
1382
- - Institution-specific features
1383
- - Advanced analytics
1384
- - Integration with LMS platforms
1385
- """
1386
- )
1387
-
1388
- with gr.Column(scale=1):
1389
- gr.Markdown("### 📊 Output & Preview")
1390
-
1391
- output_text = gr.Textbox(
1392
- label="Generation Status & Preview",
1393
- lines=20,
1394
- placeholder="Click 'Generate Test' to create questions...",
1395
- interactive=False
1396
- )
1397
-
1398
- file_status = gr.Textbox(
1399
- label="File Status",
1400
- lines=1,
1401
- interactive=False,
1402
- visible=False
1403
- )
1404
-
1405
- output_file = gr.File(
1406
- label="📥 Download Test Document (.docx)",
1407
- file_types=[".docx"]
1408
- )
1409
-
1410
- gr.Markdown(
1411
- """
1412
- ### 📄 Document Includes:
1413
- - ✅ Professional header with school name
1414
- - ✅ Complete test information table
1415
- - ✅ Type-specific instructions
1416
- - ✅ Well-formatted questions
1417
- - ✅ Separate answer key / marking scheme
1418
- - ✅ Answer spaces (for written questions)
1419
- - ✅ **NEW**: Proper mathematical notation and working space
1420
- """
1421
- )
1422
-
1423
- # Footer
1424
- gr.Markdown(
1425
- """
1426
- ---
1427
- <div class="footer">
1428
- <strong>Test Generator Pro v2.6</strong><br>
1429
- Comprehensive Assessment Tool with Enhanced Mathematics Support<br>
1430
- Now featuring actual computational problems, equations, and proofs for Mathematics!<br>
1431
- Aligned with Pakistani National Curriculum (2006) & FBISE Standards<br>
1432
- Developer: <strong>Najaf Ali Sharqi</strong><br>
1433
- Based on Professional Item Writing Standards<br><br>
1434
- 📧 For custom app development or support: Contact Najaf Ali Sharqi
1435
- </div>
1436
- """,
1437
- elem_classes="footer"
1438
- )
1439
-
1440
- # Event handlers
1441
- def update_subjects(selected_grade):
1442
- """Update subject dropdown when grade changes"""
1443
- if selected_grade:
1444
- subjects = subjects_by_grade.get(selected_grade, [])
1445
- return gr.update(choices=subjects, value=None)
1446
- return gr.update(choices=[], value=None)
1447
-
1448
- def update_chapters(selected_subject, selected_grade):
1449
- """Update chapter dropdown when subject or grade changes"""
1450
- if selected_grade and selected_subject:
1451
- chapters = chapters_by_subject_and_grade.get(selected_grade, {}).get(selected_subject, [])
1452
- return gr.update(choices=chapters, value=None)
1453
- return gr.update(choices=[], value=None)
1454
-
1455
- def update_question_type_info(q_type):
1456
- """Update question type description"""
1457
- if q_type in QUESTION_TYPES:
1458
- info = QUESTION_TYPES[q_type]
1459
- desc = info.get('description', '')
1460
- format_info = info.get('format', '')
1461
- return f"""
1462
- **Description:** {desc}
1463
-
1464
- **Format:** {format_info}
1465
- """
1466
- return ""
1467
-
1468
- def toggle_bloom_sliders(use_mix):
1469
- """Show/hide Bloom's taxonomy sliders"""
1470
- return gr.update(visible=use_mix), gr.update(visible=not use_mix)
1471
-
1472
- def calculate_bloom_total(r, u, ap, an, e, c):
1473
- """Calculate total percentage"""
1474
- total = r + u + ap + an + e + c
1475
- color = "green" if total == 100 else "red"
1476
- return f'<span style="color:{color}; font-weight:bold;">{total}%</span>'
1477
-
1478
- # Connect event handlers
1479
- grade_dropdown.change(
1480
- update_subjects,
1481
- inputs=[grade_dropdown],
1482
- outputs=[subject_dropdown]
1483
- )
1484
-
1485
- subject_dropdown.change(
1486
- update_chapters,
1487
- inputs=[subject_dropdown, grade_dropdown],
1488
- outputs=[chapter_dropdown]
1489
- )
1490
-
1491
- grade_dropdown.change(
1492
- update_chapters,
1493
- inputs=[subject_dropdown, grade_dropdown],
1494
- outputs=[chapter_dropdown]
1495
- )
1496
-
1497
- question_type.change(
1498
- update_question_type_info,
1499
- inputs=[question_type],
1500
- outputs=[question_type_info]
1501
- )
1502
-
1503
- use_bloom_mix.change(
1504
- toggle_bloom_sliders,
1505
- inputs=[use_bloom_mix],
1506
- outputs=[bloom_percentages, difficulty_dropdown]
1507
- )
1508
-
1509
- # Update total when sliders change
1510
- for slider in [bloom_remember, bloom_understand, bloom_apply, bloom_analyze, bloom_evaluate, bloom_create]:
1511
- slider.change(
1512
- calculate_bloom_total,
1513
- inputs=[bloom_remember, bloom_understand, bloom_apply, bloom_analyze, bloom_evaluate, bloom_create],
1514
- outputs=[bloom_total]
1515
- )
1516
-
1517
- generate_btn.click(
1518
- on_generate,
1519
- inputs=[
1520
- grade_dropdown,
1521
- subject_dropdown,
1522
- chapter_dropdown,
1523
- test_type,
1524
- school_name,
1525
- question_type,
1526
- use_bloom_mix,
1527
- bloom_remember,
1528
- bloom_understand,
1529
- bloom_apply,
1530
- bloom_analyze,
1531
- bloom_evaluate,
1532
- bloom_create,
1533
- num_questions
1534
- ],
1535
- outputs=[output_text, output_file, file_status]
1536
- )
1537
-
1538
- return demo
1539
-
1540
- # ---------------------------
1541
- # Launch application
1542
- # ---------------------------
1543
- try:
1544
- print("🚀 Starting Test Generator Pro v2.6")
1545
- print(f"📦 Gradio version: {gr.__version__}")
1546
-
1547
- # Check API key
1548
- if not GROQ_API_KEY:
1549
- print("⚠️ WARNING: GROQ_API_KEY not found in environment variables")
1550
- print("Please set it in Hugging Face Space Settings > Repository secrets")
1551
- else:
1552
- print(f"✅ API Key configured (length: {len(GROQ_API_KEY)})")
1553
-
1554
- # Create and launch
1555
- demo = create_ui()
1556
- print("✅ UI created successfully")
1557
-
1558
- demo.queue() # Enable queue for better handling
1559
- demo.launch()
1560
-
1561
- except Exception as e:
1562
- print(f"❌ Critical Error: {str(e)}")
1563
- import traceback
1564
- traceback.print_exc()
1565
-
1566
- # Create minimal error interface
1567
- with gr.Blocks() as error_demo:
1568
- gr.Markdown(f"""
1569
- # ❌ Application Error
1570
-
1571
- **Error Message:** {str(e)}
1572
-
1573
- **Common Solutions:**
1574
- 1. Check if GROQ_API_KEY is set in Space settings
1575
- 2. Verify all dependencies are installed
1576
- 3. Check the build logs for errors
1577
-
1578
- **Contact:** Developer - Najaf Ali Sharqi
1579
- """)
1580
-
1581
- error_demo.launch()