import os import random import traceback import torch import gradio as gr import html from quiz_logic.generator import ( generate_questions, quiz_data, current_question_index, score, user_answer, set_user_answer, reset_user_answer, increment_score, increment_index, ) def get_current_question_ui(): """Get the UI updates for the current question with proper numbering""" if not quiz_data or current_question_index[0] >= len(quiz_data): return ( gr.update(value="Quiz finished!"), gr.update(choices=[], value=None, interactive=False, visible=False), gr.update(value="", visible=False), gr.update(visible=False), gr.update(value=f"🎉 Quiz finished! Your score: {score[0]}/{len(quiz_data)}", visible=True) ) q = quiz_data[current_question_index[0]] question_number = current_question_index[0] + 1 total_questions = len(quiz_data) question_display = f"""### Question {question_number} of {total_questions} **{question_number}.** {q['question']}""" return ( gr.update(value=question_display, visible=True), gr.update(choices=q["options"], value=None, interactive=True, visible=True), gr.update(value="", visible=False), gr.update(visible=False), gr.update(value="", visible=False) ) def next_question_ui(): """Move to next question and return UI updates""" increment_index() return get_current_question_ui() def build_gradio_ui(): def reset_and_start_quiz(topic, n_questions, difficulty): quiz_data.clear() current_question_index[0] = 0 score[0] = 0 user_answer[0] = None success = generate_questions(topic, int(n_questions), difficulty) if not success: return [ gr.update(visible=False), gr.update(value=""), gr.update(choices=[], visible=False), gr.update(value="", visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True, value='
⚠️ Failed to generate questions. Please try again.
'), gr.update(interactive=True, value="Start Quiz") ] question_update, options_update, feedback_update, next_btn_update, score_display_update = get_current_question_ui() return [ gr.update(visible=True), question_update, options_update, feedback_update, next_btn_update, score_display_update, gr.update(visible=False), gr.update(interactive=True, value="Start Quiz") ] with gr.Blocks(title="Smart Quiz Maker", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🧠 Smart Quiz Maker Test your knowledge with high-quality AI-generated multiple choice questions! """) with gr.Row(): topic = gr.Textbox( label="Quiz Topic", placeholder="Enter any topic (e.g. Python, Machine Learning, History, Science)", value="Python" ) n_questions = gr.Radio( label="Number of Questions", choices=[3, 5, 10], value=5 ) difficulty = gr.Radio( label="Difficulty", choices=["easy", "medium", "hard"], value="medium" ) with gr.Column(visible=False) as loading_box: loading_html = gr.HTML( '''
🔍 Generating high-quality quiz questions...
''' ) start_btn = gr.Button("🚀 Start Quiz", variant="primary", size="lg") with gr.Column(visible=False) as quiz_container: question = gr.Markdown() options = gr.Radio( choices=[], visible=True, label="Select your answer:" ) feedback = gr.HTML(visible=False) with gr.Row(): next_btn = gr.Button("Next Question", visible=False, variant="secondary") score_display = gr.Markdown(visible=False) start_btn.click( fn=lambda: [gr.update(visible=True), gr.update(interactive=False, value="⏳ Generating...")], outputs=[loading_box, start_btn], ).then( fn=reset_and_start_quiz, inputs=[topic, n_questions, difficulty], outputs=[ quiz_container, question, options, feedback, next_btn, score_display, loading_box, start_btn ] ) def show_final_score(): percentage = round((score[0] / len(quiz_data)) * 100) if quiz_data else 0 if percentage >= 90: grade = "🏆 Excellent!" color = "#16a34a" elif percentage >= 70: grade = "👍 Good job!" color = "#2563eb" elif percentage >= 50: grade = "📚 Keep practicing!" color = "#f59e0b" else: grade = "💪 Try again!" color = "#dc2626" score_html = f'''

{grade}

{score[0]}/{len(quiz_data)}
{percentage}% Correct
Click "🚀 Start Quiz" to try again with new questions!
''' return [ "", gr.update(visible=False), gr.update(value="", visible=False), gr.update(visible=False), gr.update(visible=True, value=score_html) ] def next_and_show(): question_update, options_update, feedback_update, next_btn_update, score_display_update = next_question_ui() # Check if quiz is finished by looking at the value in the update dict question_value = question_update.get('value', '') if "Quiz finished" in str(question_value): return show_final_score() else: return [ question_update, options_update, feedback_update, next_btn_update, score_display_update ] next_btn.click( fn=next_and_show, outputs=[question, options, feedback, next_btn, score_display] ) def select_and_feedback(option): if option is None: return [ gr.update(interactive=True), gr.update(value="", visible=False), gr.update(visible=False) ] try: qobj = quiz_data[current_question_index[0]] correct_answer = qobj.get("answer") or qobj.get("correct_answer") or qobj.get("correct") explanation = qobj.get("explanation", "") is_correct = str(option).strip().lower() == str(correct_answer).strip().lower() if is_correct: score[0] += 1 def esc(s): return html.escape(str(s)) if s is not None else "" if is_correct: feedback_html = f'''
Correct!
Your answer: {esc(option)}
{f'
Explanation: {esc(explanation)}
' if explanation else ""}
''' else: feedback_html = f'''
Incorrect
Your answer: {esc(option)}
Correct answer: {esc(correct_answer)}
{f'
Explanation: {esc(explanation)}
' if explanation else ""}
''' is_last_question = (current_question_index[0] == len(quiz_data) - 1) next_label = "🎯 View Results" if is_last_question else "➡️ Next Question" return [ gr.update(interactive=False, value=option), gr.update(value=feedback_html, visible=True), gr.update(visible=True, value=next_label) ] except Exception as e: print(f"Error in feedback generation: {e}") return [ gr.update(interactive=False, value=option), gr.update(value="
⚠️ Feedback not available
", visible=True), gr.update(visible=True, value="Next Question") ] options.change( fn=select_and_feedback, inputs=[options], outputs=[options, feedback, next_btn] ) return demo