mcq-generator / app.py
ManthaBhuvana's picture
Update app.py
263612e verified
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)