Spaces:
Sleeping
Sleeping
| 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"<b>{chr(65+opt_idx)}.</b> {option}<br>" | |
| question_html = f"<div style='margin-bottom:20px; padding:10px; border:1px solid #ccc; border-radius:10px; background:#f9f9f9;'>" | |
| question_html += f"<b>Q{idx+1}:</b> {question_text}<br><br>{options}" | |
| if 0 <= answer_index < len(mcq_options): | |
| question_html += f"<i><b>Answer:</b> {chr(65+answer_index)}</i>" | |
| else: | |
| question_html += f"<i><b>Answer:</b> [Invalid Answer Index]</i>" | |
| question_html += "</div>" | |
| 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("<h1 style='text-align:center;'>π Smart MCQ Generator</h1>") | |
| 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) |