Spaces:
Sleeping
Sleeping
File size: 5,860 Bytes
db2edfa b592dbc db2edfa 2fbf1a0 db2edfa b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc db2edfa b592dbc 2fbf1a0 db2edfa b592dbc db2edfa b592dbc db2edfa b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc 2fbf1a0 b592dbc db2edfa b592dbc 2fbf1a0 b592dbc db2edfa 2fbf1a0 b592dbc 2fbf1a0 db2edfa 2fbf1a0 e4f35d3 db2edfa |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
import gradio as gr
import random
import json
from fpdf import FPDF
import os
# Load question bank
with open("questions.json", "r", encoding="utf-8") as f:
question_bank = json.load(f)
# Function to get random 50 questions
def get_random_questions():
return random.sample(question_bank, 50)
# Function to evaluate answers (handles single or multiple-correct formats)
def evaluate_test(student_name, father_name, roll_no, answers, questions):
# answers: list of 50 selected option strings (or None)
attempted = sum(1 for a in answers if a is not None and a != "")
correct = 0
for q, ans in zip(questions, answers):
if ans is None or ans == "":
continue
correct_key = q.get("answer")
# support either a single string or a list of accepted answers
if isinstance(correct_key, list):
if ans in correct_key:
correct += 1
else:
if ans == correct_key:
correct += 1
total = len(questions) if questions else 0
percentage = (correct / total) * 100 if total > 0 else 0.0
# Generate PDF report (safe encoding)
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
pdf.cell(200, 10, txt="Test Report - Class 6 General Knowledge", ln=True, align="C")
pdf.ln(10)
# Use latin-1 replacement to avoid crashes for non-latin characters
def safe_text(t):
return str(t).encode("latin-1", "replace").decode("latin-1")
pdf.cell(200, 8, txt=safe_text(f"Student Name: {student_name}"), ln=True)
pdf.cell(200, 8, txt=safe_text(f"Father's Name: {father_name}"), ln=True)
pdf.cell(200, 8, txt=safe_text(f"Roll No: {roll_no}"), ln=True)
pdf.ln(6)
pdf.cell(200, 8, txt=f"Total Questions: {total}", ln=True)
pdf.cell(200, 8, txt=f"Attempted: {attempted}", ln=True)
pdf.cell(200, 8, txt=f"Correct: {correct}", ln=True)
pdf.cell(200, 8, txt=f"Percentage: {percentage:.2f}%", ln=True)
# Save file
safe_roll = roll_no if roll_no else "student"
file_name = f"{safe_roll}_result.pdf"
# ensure unique filename to avoid race conditions
idx = 0
base_name = file_name
while os.path.exists(file_name):
idx += 1
file_name = f"{safe_roll}_result_{idx}.pdf"
pdf.output(file_name)
result_summary = (
f"Student: {student_name}\n"
f"Father's Name: {father_name}\n"
f"Roll No: {roll_no}\n\n"
f"Attempted: {attempted}\n"
f"Correct: {correct}\n"
f"Percentage: {percentage:.2f}%"
)
return result_summary, file_name
# Build Gradio interface using Blocks
def build_interface():
def run_test(*args):
"""
args layout:
0: student_name
1: father_name
2: roll_no
3..52: answer values for Q1..Q50
53: questions state (list of 50 question dicts)
"""
if len(args) < 4:
return "Invalid input.", None
student_name = args[0]
father_name = args[1]
roll_no = args[2]
# answers are args[3] .. args[-2], last arg is questions
if len(args) < 5:
# no answers provided
answers = []
questions = []
else:
questions = args[-1]
answers = list(args[3:-1])
# Safety: if questions is a single dict (shouldn't be) or None, handle gracefully
if not isinstance(questions, list):
questions = list(questions) if questions else []
return evaluate_test(student_name, father_name, roll_no, answers, questions)
def update_questions_ui(questions):
"""Return 50 gr.update objects to populate radio labels and choices."""
updates = []
for i, q in enumerate(questions):
updates.append(
gr.update(label=f"Q{i+1}: {q.get('question','')}", choices=q.get("options", []), value=None)
)
return updates
with gr.Blocks() as demo:
gr.Markdown("## Class 6 General Knowledge Test (GFT/O Exam)")
gr.Markdown("Answer 50 random questions and get your result in PDF.")
student_name = gr.Textbox(label="Student Name")
father_name = gr.Textbox(label="Father's Name")
roll_no = gr.Textbox(label="Roll No")
# State to store current random questions (list of 50 question dicts)
question_state = gr.State(get_random_questions())
# Create 50 radio inputs
answer_inputs = []
with gr.Accordion("Questions", open=True):
for i in range(50):
r = gr.Radio(label=f"Q{i+1}", choices=[])
answer_inputs.append(r)
output_text = gr.Textbox(label="Result Summary")
output_file = gr.File(label="Download Result PDF")
submit_btn = gr.Button("Submit Test", variant="primary")
new_test_btn = gr.Button("New Test", variant="secondary")
# On load, populate the 50 radios using the question_state
demo.load(update_questions_ui, inputs=question_state, outputs=answer_inputs)
# Submit test -> run_test which parses args and calls evaluate_test
submit_btn.click(
run_test,
inputs=[student_name, father_name, roll_no] + answer_inputs + [question_state],
outputs=[output_text, output_file],
)
# New Test -> generate fresh questions and update state + radios
def refresh():
qs = get_random_questions()
# return list whose first element will set question_state, followed by 50 radio updates
return [qs] + update_questions_ui(qs)
new_test_btn.click(
refresh,
inputs=None,
outputs=[question_state] + answer_inputs,
)
return demo
demo = build_interface()
if __name__ == "__main__":
demo.launch()
|