Spaces:
Sleeping
Sleeping
| 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() | |