import gradio as gr import pandas as pd from mcq_generator import ImprovedMCQGenerator, is_suitable_for_students import io import tempfile # <-- Import tempfile import traceback # <-- Optional: for better error logging # Initialize MCQ Generator (Keep this outside the function for efficiency) try: mcq_generator = ImprovedMCQGenerator() print("✅ MCQ Generator Initialized Successfully") except Exception as e: print(f"❌ Failed to initialize MCQ Generator: {e}") # You might want to stop the app or handle this more gracefully mcq_generator = None def generate_mcqs_ui(paragraph, num_questions): # Check if generator initialized properly if mcq_generator is None: return None, None, None, "❌ Error: MCQ Generator failed to initialize. Check server logs." # --- Input Validation --- if not paragraph or not paragraph.strip(): return None, None, None, "⚠️ Please enter a valid paragraph." print("\n--- Checking Suitability ---") # Add logging if not is_suitable_for_students(paragraph): print("❌ Paragraph deemed unsuitable.") # Add logging # Return None for HTML and both file paths, plus the status message return None, None, None, "❌ The paragraph is not suitable for MCQ generation (due to bias/toxicity/short length)." print("✅ Paragraph suitable.") # Add logging try: print(f"--- Generating {num_questions} MCQs ---") # Add logging # Generate MCQs using the generator mcqs = mcq_generator.generate_mcqs(paragraph, num_questions) if not mcqs: print("⚠️ No MCQs generated (potentially issue in generator logic).") # Add logging return None, None, None, "⚠️ Could not generate MCQs for this text. Try different content or fewer questions." print(f"✅ Generated {len(mcqs)} MCQs successfully.") # Add logging # --- Create Pretty HTML Output --- pretty_mcqs_html = "" for idx, mcq in enumerate(mcqs): options = "" # Ensure options exist and handle potential errors mcq_options = mcq.get('options', []) answer_index = mcq.get('answer_index', -1) question_text = mcq.get('question', '[No Question Text]') for opt_idx, option in enumerate(mcq_options): options += f"{chr(65+opt_idx)}. {option}
" question_html = f"
" question_html += f"Q{idx+1}: {question_text}

{options}" if 0 <= answer_index < len(mcq_options): question_html += f"Answer: {chr(65+answer_index)}" else: question_html += f"Answer: [Invalid Answer Index]" question_html += "
" pretty_mcqs_html += question_html # --- Prepare Text Output and CSV Data --- txt_output = "" csv_data = [] for idx, mcq in enumerate(mcqs): mcq_options = mcq.get('options', []) answer_index = mcq.get('answer_index', -1) question_text = mcq.get('question', '[No Question Text]') txt_output += f"Q{idx+1}: {question_text}\n" for opt_idx, option in enumerate(mcq_options): txt_output += f" {chr(65+opt_idx)}. {option}\n" if 0 <= answer_index < len(mcq_options): txt_output += f"Answer: {chr(65+answer_index)}\n\n" else: txt_output += f"Answer: [Invalid Answer Index]\n\n" # Ensure 4 options for CSV, padding if necessary options_padded = mcq_options + [''] * (4 - len(mcq_options)) csv_row = { 'Question': question_text, 'Option A': options_padded[0], 'Option B': options_padded[1], 'Option C': options_padded[2], 'Option D': options_padded[3], 'Answer': chr(65+answer_index) if 0 <= answer_index < len(mcq_options) else '[Invalid]' } csv_data.append(csv_row) # --- Create In-Memory Buffers (Still useful for structuring data) --- txt_buffer = io.StringIO() txt_buffer.write(txt_output) txt_buffer.seek(0) csv_buffer = io.StringIO() pd.DataFrame(csv_data).to_csv(csv_buffer, index=False) csv_buffer.seek(0) # --- Create Temporary Files and Get Paths --- print("--- Creating temporary files ---") # Add logging # Use delete=False so Gradio can access the file after the 'with' block closes it. # Gradio should handle the cleanup of these temporary files. with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False, encoding='utf-8') as temp_txt: temp_txt.write(txt_buffer.getvalue()) txt_filepath = temp_txt.name print(f"Created TXT temp file: {txt_filepath}") # Add logging with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False, encoding='utf-8') as temp_csv: temp_csv.write(csv_buffer.getvalue()) csv_filepath = temp_csv.name print(f"Created CSV temp file: {csv_filepath}") # Add logging # --- Return HTML, File Paths, and Status --- # Return order must match the `outputs` list in demo.launch() return pretty_mcqs_html, txt_filepath, csv_filepath, "✅ MCQs generated successfully!" except Exception as e: print(f"❌ Error during MCQ generation process: {e}") # Add logging print(traceback.format_exc()) # Print detailed traceback for debugging # Return None for HTML and both file paths, plus the error message return None, None, None, f"❌ Error generating MCQs: {str(e)}" # --- Define Gradio Interface --- with gr.Blocks(theme=gr.themes.Soft()) as demo: # Added a theme for aesthetics gr.Markdown("

📚 Smart MCQ Generator

") gr.Markdown("Enter a paragraph of study material, choose the number of questions, and get MCQs instantly!") # Added description with gr.Row(): paragraph_input = gr.Textbox( lines=10, # Increased lines label="Enter Paragraph for MCQs", placeholder="Paste your study material here (ideally 50-500 words)...", elem_id="paragraph-input" # Added elem_id for potential CSS styling ) with gr.Row(): num_questions_slider = gr.Slider( minimum=1, # Explicit minimum maximum=10, # Explicit maximum step=1, value=5, label="Number of Questions to Generate", elem_id="num-questions-slider" ) with gr.Row(): generate_btn = gr.Button("🚀 Generate MCQs", variant="primary") # Made button primary # Status box status = gr.Textbox(label="Status", interactive=False, placeholder="Generation status will appear here...", elem_id="status-box") # Use Accordion for tidier output section with gr.Accordion("Generated MCQs & Downloads", open=True): # Default to open # MCQ HTML output mcq_output = gr.HTML(label="Generated MCQs") # Download links - SEPARATED with gr.Row(): download_txt = gr.File(label="Download MCQs (.txt)") download_csv = gr.File(label="Download MCQs (.csv)") # Set up the button click event generate_btn.click( fn=generate_mcqs_ui, inputs=[paragraph_input, num_questions_slider], # Outputs must match the return order of generate_mcqs_ui outputs=[mcq_output, download_txt, download_csv, status], api_name="generate_mcqs" # Added api_name for potential API usage ) # --- Launch the app --- # share=True generates a public link (useful for HF Spaces) # server_name="0.0.0.0" makes it accessible within the container network # server_port=7860 is the standard Gradio port for Spaces print("--- Launching Gradio App ---") demo.launch(share=True, server_name="0.0.0.0", server_port=7860)