File size: 9,958 Bytes
7461958
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff40b9b
eccc273
ff40b9b
 
 
eccc273
ff40b9b
 
 
 
eccc273
 
431839d
155cc1f
 
ff40b9b
155cc1f
eccc273
 
155cc1f
 
 
 
 
eccc273
 
 
 
 
155cc1f
eccc273
431839d
 
155cc1f
 
2be75a5
155cc1f
 
ff40b9b
155cc1f
 
 
 
2be75a5
155cc1f
 
431839d
 
88ad4b4
155cc1f
ff40b9b
 
88ad4b4
ff40b9b
eccc273
ff40b9b
 
 
eccc273
ff40b9b
 
 
 
431839d
ff40b9b
 
88ad4b4
ff40b9b
 
 
 
 
 
 
 
 
 
88ad4b4
eccc273
ff40b9b
7461958
 
 
 
 
 
 
 
 
 
 
 
155cc1f
7461958
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155cc1f
 
 
7461958
 
 
 
155cc1f
2e001b9
 
7461958
38dca3c
7461958
155cc1f
2be75a5
7461958
155cc1f
7461958
 
155cc1f
 
7461958
 
 
 
 
 
155cc1f
7461958
 
155cc1f
7ab6452
3ec2b4a
 
 
 
7461958
 
 
 
 
 
 
 
 
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import streamlit as st
import google.generativeai as genai
import os
import re  # For improved parsing

# Set up Streamlit page
st.title("Interactive Multiple Choice Quiz Generator")
st.markdown("Powered by Gemini API")

# API Key Input from User
gemini_api_key = st.text_input("Enter your Gemini API Key:", type="password")

if not gemini_api_key:
    st.warning("Please enter your Gemini API key to generate a quiz.")
    st.stop()
else:
    genai.configure(api_key=gemini_api_key)
    model = genai.GenerativeModel('gemini-pro')

    # Security Warning
    st.markdown("<font color='red'>**Warning:** Directly entering your API key is less secure than using Streamlit Secrets or environment variables. For production, use Secrets.</font>", unsafe_allow_html=True)

# Initialize session state for quiz data and progress
if 'quiz_data' not in st.session_state:
    st.session_state.quiz_data = None
if 'current_question_index' not in st.session_state:
    st.session_state.current_question_index = 0
if 'user_answers' not in st.session_state:
    st.session_state.user_answers = []
if 'quiz_completed' not in st.session_state:
    st.session_state.quiz_completed = False
if 'score' not in st.session_state:
    st.session_state.score = 0


def parse_quiz_content(quiz_content):
    """Parses the quiz content and answer key into a structured format, handling title."""
    questions = []
    answer_key_dict = {}

    quiz_parts = quiz_content.split("Answer Key:")
    if len(quiz_parts) != 2:
        st.warning("Could not reliably separate quiz questions and answer key. Parsing might be imperfect.")
        return None, None

    question_section = quiz_parts[0].strip()
    answer_key_section = quiz_parts[1].strip()

    # --- Split into blocks by double newline ---
    blocks = re.split(r'\n\n+', question_section) # Split by one or more double newlines

    # --- Handle Potential Title (First Block) ---
    if blocks:
        first_block = blocks[0].strip()
        if not re.match(r'^\d+\.\s', first_block): # Check if the first block DOES NOT start with a question number
            # Assume the first block is the title and remove it
            blocks = blocks[1:] # Slice to remove the first block (title)
        else:
            st.warning("Unexpected format: First block starts with a question number. Title might not be removed correctly.")


    # --- Process Question Blocks ---
    for block in blocks:
        block = block.strip()
        if not block:
            continue

        lines = block.split('\n')
        # --- First line is question text (after removing bold markdown) ---
        question_text = re.sub(r'\*\*|\*', '', lines[0]).strip()

        options = {}
        for line in lines[1:]:
            line = line.strip()
            if re.match(r'^[A-D]\.\s', line): # Options are like "A. Elephant"
                option_letter = line[0]
                option_text = line[2:].strip()
                options[option_letter] = option_text

        if question_text: # Only append if we have a question text
            questions.append({'question': question_text, 'options': options})


    # --- Answer Key Parsing (No change needed) ---
    answer_lines = answer_key_section.strip().split('\n')
    for line in answer_lines:
        line = line.strip()
        match = re.match(r'(\d+)\.\s*([A-D])', line)
        if match:
            question_num = int(match.group(1)) - 1
            correct_answer = match.group(2)
            answer_key_dict[question_num] = correct_answer

    # Basic validation
    if not questions or not answer_key_dict:
        st.error("Error parsing quiz content. Please try again or check the generated format.")
        return None, None
    if len(questions) != len(answer_key_dict):
        st.warning(f"Number of questions parsed ({len(questions)}) does not match number of answers in answer key ({len(answer_key_dict)}). Parsing might be incomplete.")


    # Combine parsed questions and answer key into quiz_data
    quiz_data_list = []
    for i, q_data in enumerate(questions):
        correct_answer = answer_key_dict.get(i)
        if correct_answer:
            quiz_data_list.append({
                'question': q_data['question'],
                'options': q_data['options'],
                'correct_answer': correct_answer
            })
        else:
            st.warning(f"Could not find correct answer for question {i+1} in the answer key.")
            return None, None

    return quiz_data_list, answer_key_dict


def display_question():
    """Displays the current question and options for interactive quiz."""
    if st.session_state.quiz_data is None:
        st.error("Quiz data not loaded yet. Generate a quiz first.")
        return

    if st.session_state.current_question_index < len(st.session_state.quiz_data):
        question_data = st.session_state.quiz_data[st.session_state.current_question_index]
        question_number = st.session_state.current_question_index + 1
        st.markdown(f"**Question {question_number}:** {question_data['question']}")

        options_list = [f"{key}. {value}" for key, value in question_data['options'].items()]
        user_choice = st.radio("Choose an answer:", options_list, key=f"q_{question_number}") # Unique key for radio buttons

        if st.button("Submit Answer", key=f"submit_q_{question_number}"): # Unique key for submit button
            selected_option_letter = user_choice.split('.')[0] # Extract A, B, C, or D
            st.session_state.user_answers.append(selected_option_letter)

            if st.session_state.current_question_index < len(st.session_state.quiz_data) - 1:
                st.session_state.current_question_index += 1
            else:
                st.session_state.quiz_completed = True # Mark quiz as completed after last question
            st.rerun()


def display_results():
    """Displays the quiz results after completion."""
    if st.session_state.quiz_completed:
        st.markdown("### Quiz Results")
        correct_count = 0
        for i in range(len(st.session_state.quiz_data)):
            user_answer = st.session_state.user_answers[i] if i < len(st.session_state.user_answers) else None # Handle if user didn't answer all
            correct_answer = st.session_state.quiz_data[i]['correct_answer']
            if user_answer == correct_answer:
                correct_count += 1
                result_text = f"**Question {i+1}:** ✅ Correct! (Your answer: {user_answer}, Correct answer: {correct_answer})"
                color = "green"
            else:
                result_text = f"**Question {i+1}:** ❌ Incorrect. (Your answer: {user_answer if user_answer else 'Not answered'}, Correct answer: {correct_answer})"
                color = "red"
            st.markdown(f"<font color='{color}'>{result_text}</font>", unsafe_allow_html=True)

        percentage_correct = (correct_count / len(st.session_state.quiz_data)) * 100
        st.markdown(f"### Final Score: {correct_count} out of {len(st.session_state.quiz_data)} correct ({percentage_correct:.2f}%)")
        st.session_state.score = correct_count # Store score in session state


# User input for topic
topic = st.text_input("Enter the topic for your quiz:")

if topic:
    if st.button("Generate Quiz"):
        with st.spinner(f"Generating quiz on '{topic}'..."):
            try:
                prompt = f"""
                Generate a multiple-choice quiz on the topic of "{topic}".
                The quiz should have 5 questions.
                For each question, provide four plausible options labeled A, B, C, and D.
                Clearly indicate the correct answer for each question at the end in a separate section called "Answer Key".
                Format the quiz clearly for easy reading.
                """
                response = model.generate_content(prompt)
                quiz_content = response.text

                # --- DEBUGGING PRINTS ---
                st.write("### Raw Quiz Content from Gemini:")
                st.code(quiz_content) # Display raw content in a code block for readability

                parsed_quiz_data, answer_key = parse_quiz_content(quiz_content)

                st.write("### Parsed Quiz Data:")
                st.write(parsed_quiz_data) # Display the parsed data structure

                # --- END DEBUGGING PRINTS ---


                if quiz_content: # Check if quiz_content was generated successfully (outer if)
                    if parsed_quiz_data: # Check if parsing was successful (inner if)
                        st.session_state.quiz_data = parsed_quiz_data
                        st.session_state.current_question_index = 0
                        st.session_state.user_answers = []
                        st.session_state.quiz_completed = False
                        st.session_state.score = 0
                        st.success(f"Quiz on '{topic}' generated successfully! Let's begin.")
                    else: # else associated with inner if parsed_quiz_data
                        st.error("Failed to parse quiz content. Please try generating again.")
                        st.session_state.quiz_data = None
                else: # else associated with outer if quiz_content
                    st.error("Failed to generate quiz content. Please try again or check your API key.")

            except Exception as e:
                st.error(f"An error occurred: {e}")
                st.error("Please check your API key and network connection. If the problem persists, try a different topic or try again later.")

# Quiz Display Logic
if st.session_state.quiz_data:
    if not st.session_state.quiz_completed:
        display_question()
    else:
        display_results()
elif topic and not st.session_state.quiz_data and not st.session_state.quiz_completed: # Message if topic entered but quiz not generated yet
    st.info("Click 'Generate Quiz' to start the quiz.")