File size: 8,173 Bytes
18fc9c1
 
 
 
257c34d
 
 
 
 
 
 
 
 
 
 
18fc9c1
bec29d2
257c34d
 
 
 
 
 
 
bec29d2
257c34d
bec29d2
257c34d
 
 
 
bec29d2
 
257c34d
1279145
bec29d2
 
257c34d
 
 
 
 
 
 
 
bec29d2
 
257c34d
 
 
 
 
 
bec29d2
257c34d
bec29d2
257c34d
 
 
 
 
bec29d2
257c34d
bec29d2
257c34d
bec29d2
 
 
 
257c34d
 
 
 
 
 
bec29d2
257c34d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
01cbd3a
 
 
 
 
 
 
bec29d2
257c34d
 
 
 
 
 
 
 
c1f6444
257c34d
 
 
 
 
 
 
 
bec29d2
 
257c34d
 
 
 
eced281
 
257c34d
 
1279145
257c34d
 
1279145
257c34d
 
 
 
 
 
1279145
 
257c34d
 
 
 
 
 
 
 
1279145
 
257c34d
 
 
 
 
 
 
 
 
 
 
 
 
 
1279145
257c34d
1279145
 
 
257c34d
 
 
1279145
 
257c34d
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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)