Update app.py
Browse files
app.py
CHANGED
@@ -1,23 +1,147 @@
|
|
1 |
import streamlit as st
|
2 |
import google.generativeai as genai
|
3 |
import os
|
|
|
4 |
|
5 |
# Set up Streamlit page
|
6 |
-
st.title("Multiple Choice Quiz Generator")
|
7 |
st.markdown("Powered by Gemini API")
|
8 |
|
9 |
-
#
|
10 |
gemini_api_key = st.text_input("Enter your Gemini API Key:", type="password")
|
11 |
|
12 |
if not gemini_api_key:
|
13 |
st.warning("Please enter your Gemini API key to generate a quiz.")
|
14 |
-
st.stop()
|
15 |
else:
|
16 |
genai.configure(api_key=gemini_api_key)
|
17 |
-
model = genai.GenerativeModel('gemini-pro')
|
18 |
|
19 |
-
#
|
20 |
-
st.markdown("<font color='red'>**Warning:** Directly entering your API key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
|
23 |
# User input for topic
|
@@ -27,7 +151,6 @@ if topic:
|
|
27 |
if st.button("Generate Quiz"):
|
28 |
with st.spinner(f"Generating quiz on '{topic}'..."):
|
29 |
try:
|
30 |
-
# Construct the prompt for Gemini
|
31 |
prompt = f"""
|
32 |
Generate a multiple-choice quiz on the topic of "{topic}".
|
33 |
The quiz should have 5 questions.
|
@@ -35,29 +158,33 @@ if topic:
|
|
35 |
Clearly indicate the correct answer for each question at the end in a separate section called "Answer Key".
|
36 |
Format the quiz clearly for easy reading.
|
37 |
"""
|
38 |
-
|
39 |
response = model.generate_content(prompt)
|
40 |
quiz_content = response.text
|
41 |
|
42 |
if quiz_content:
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
st.markdown("### Answer Key:")
|
52 |
-
st.markdown(answer_key)
|
53 |
else:
|
54 |
-
st.
|
55 |
-
|
56 |
else:
|
57 |
st.error("Failed to generate quiz content. Please try again or check your API key.")
|
58 |
|
59 |
except Exception as e:
|
60 |
st.error(f"An error occurred: {e}")
|
61 |
st.error("Please check your API key and network connection. If the problem persists, try a different topic or try again later.")
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
import google.generativeai as genai
|
3 |
import os
|
4 |
+
import re # For improved parsing
|
5 |
|
6 |
# Set up Streamlit page
|
7 |
+
st.title("Interactive Multiple Choice Quiz Generator")
|
8 |
st.markdown("Powered by Gemini API")
|
9 |
|
10 |
+
# API Key Input from User
|
11 |
gemini_api_key = st.text_input("Enter your Gemini API Key:", type="password")
|
12 |
|
13 |
if not gemini_api_key:
|
14 |
st.warning("Please enter your Gemini API key to generate a quiz.")
|
15 |
+
st.stop()
|
16 |
else:
|
17 |
genai.configure(api_key=gemini_api_key)
|
18 |
+
model = genai.GenerativeModel('gemini-pro')
|
19 |
|
20 |
+
# Security Warning
|
21 |
+
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)
|
22 |
+
|
23 |
+
# Initialize session state for quiz data and progress
|
24 |
+
if 'quiz_data' not in st.session_state:
|
25 |
+
st.session_state.quiz_data = None
|
26 |
+
if 'current_question_index' not in st.session_state:
|
27 |
+
st.session_state.current_question_index = 0
|
28 |
+
if 'user_answers' not in st.session_state:
|
29 |
+
st.session_state.user_answers = []
|
30 |
+
if 'quiz_completed' not in st.session_state:
|
31 |
+
st.session_state.quiz_completed = False
|
32 |
+
if 'score' not in st.session_state:
|
33 |
+
st.session_state.score = 0
|
34 |
+
|
35 |
+
|
36 |
+
def parse_quiz_content(quiz_content):
|
37 |
+
"""Parses the quiz content and answer key into a structured format."""
|
38 |
+
questions = []
|
39 |
+
answer_key_dict = {}
|
40 |
+
|
41 |
+
quiz_parts = quiz_content.split("Answer Key")
|
42 |
+
if len(quiz_parts) != 2:
|
43 |
+
st.warning("Could not reliably separate quiz questions and answer key. Parsing might be imperfect.")
|
44 |
+
return None, None
|
45 |
+
|
46 |
+
question_section = quiz_parts[0]
|
47 |
+
answer_key_section = quiz_parts[1]
|
48 |
+
|
49 |
+
# Parse questions and options using regex (more robust)
|
50 |
+
question_blocks = re.split(r'\n(?=\d+\.)', question_section.strip()) # Split by newline followed by number and dot
|
51 |
+
for block in question_blocks:
|
52 |
+
if not block.strip():
|
53 |
+
continue # Skip empty blocks
|
54 |
+
lines = block.strip().split('\n')
|
55 |
+
question_text = lines[0].strip()
|
56 |
+
options = {}
|
57 |
+
for line in lines[1:]:
|
58 |
+
line = line.strip()
|
59 |
+
if line.startswith('A.') or line.startswith('B.') or line.startswith('C.') or line.startswith('D.'):
|
60 |
+
option_letter = line[0]
|
61 |
+
option_text = line[2:].strip()
|
62 |
+
options[option_letter] = option_text
|
63 |
+
questions.append({'question': question_text, 'options': options})
|
64 |
+
|
65 |
+
# Parse answer key (assuming format like 1. A, 2. B, etc.)
|
66 |
+
answer_lines = answer_key_section.strip().split('\n')
|
67 |
+
for line in answer_lines:
|
68 |
+
line = line.strip()
|
69 |
+
match = re.match(r'(\d+)\.\s*([A-D])', line) # Regex to find question number and answer letter
|
70 |
+
if match:
|
71 |
+
question_num = int(match.group(1)) - 1 # Adjust to 0-based index
|
72 |
+
correct_answer = match.group(2)
|
73 |
+
answer_key_dict[question_num] = correct_answer
|
74 |
+
|
75 |
+
# Basic validation: Check if we parsed questions and answers and if counts match
|
76 |
+
if not questions or not answer_key_dict:
|
77 |
+
st.error("Error parsing quiz content. Please try again or check the generated format.")
|
78 |
+
return None, None
|
79 |
+
if len(questions) != len(answer_key_dict):
|
80 |
+
st.warning("Number of questions and answers in answer key do not match. Parsing might be incomplete.")
|
81 |
+
|
82 |
+
|
83 |
+
# Combine parsed questions and answer key into quiz_data
|
84 |
+
quiz_data_list = []
|
85 |
+
for i, q_data in enumerate(questions):
|
86 |
+
correct_answer = answer_key_dict.get(i)
|
87 |
+
if correct_answer:
|
88 |
+
quiz_data_list.append({
|
89 |
+
'question': q_data['question'],
|
90 |
+
'options': q_data['options'],
|
91 |
+
'correct_answer': correct_answer
|
92 |
+
})
|
93 |
+
else:
|
94 |
+
st.warning(f"Could not find correct answer for question {i+1} in the answer key.")
|
95 |
+
return None, None # Inconsistent data, better to stop
|
96 |
+
|
97 |
+
return quiz_data_list, answer_key_dict
|
98 |
+
|
99 |
+
|
100 |
+
def display_question():
|
101 |
+
"""Displays the current question and options for interactive quiz."""
|
102 |
+
if st.session_state.quiz_data is None:
|
103 |
+
st.error("Quiz data not loaded yet. Generate a quiz first.")
|
104 |
+
return
|
105 |
+
|
106 |
+
if st.session_state.current_question_index < len(st.session_state.quiz_data):
|
107 |
+
question_data = st.session_state.quiz_data[st.session_state.current_question_index]
|
108 |
+
question_number = st.session_state.current_question_index + 1
|
109 |
+
st.markdown(f"**Question {question_number}:** {question_data['question']}")
|
110 |
+
|
111 |
+
options_list = [f"{key}. {value}" for key, value in question_data['options'].items()]
|
112 |
+
user_choice = st.radio("Choose an answer:", options_list, key=f"q_{question_number}") # Unique key for radio buttons
|
113 |
+
|
114 |
+
if st.button("Submit Answer", key=f"submit_q_{question_number}"): # Unique key for submit button
|
115 |
+
selected_option_letter = user_choice.split('.')[0] # Extract A, B, C, or D
|
116 |
+
st.session_state.user_answers.append(selected_option_letter)
|
117 |
+
|
118 |
+
if st.session_state.current_question_index < len(st.session_state.quiz_data) - 1:
|
119 |
+
st.session_state.current_question_index += 1
|
120 |
+
else:
|
121 |
+
st.session_state.quiz_completed = True # Mark quiz as completed after last question
|
122 |
+
st.experimental_rerun() # Rerun to update UI
|
123 |
+
|
124 |
+
|
125 |
+
def display_results():
|
126 |
+
"""Displays the quiz results after completion."""
|
127 |
+
if st.session_state.quiz_completed:
|
128 |
+
st.markdown("### Quiz Results")
|
129 |
+
correct_count = 0
|
130 |
+
for i in range(len(st.session_state.quiz_data)):
|
131 |
+
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
|
132 |
+
correct_answer = st.session_state.quiz_data[i]['correct_answer']
|
133 |
+
if user_answer == correct_answer:
|
134 |
+
correct_count += 1
|
135 |
+
result_text = f"**Question {i+1}:** ✅ Correct! (Your answer: {user_answer}, Correct answer: {correct_answer})"
|
136 |
+
color = "green"
|
137 |
+
else:
|
138 |
+
result_text = f"**Question {i+1}:** ❌ Incorrect. (Your answer: {user_answer if user_answer else 'Not answered'}, Correct answer: {correct_answer})"
|
139 |
+
color = "red"
|
140 |
+
st.markdown(f"<font color='{color}'>{result_text}</font>", unsafe_allow_html=True)
|
141 |
+
|
142 |
+
percentage_correct = (correct_count / len(st.session_state.quiz_data)) * 100
|
143 |
+
st.markdown(f"### Final Score: {correct_count} out of {len(st.session_state.quiz_data)} correct ({percentage_correct:.2f}%)")
|
144 |
+
st.session_state.score = correct_count # Store score in session state
|
145 |
|
146 |
|
147 |
# User input for topic
|
|
|
151 |
if st.button("Generate Quiz"):
|
152 |
with st.spinner(f"Generating quiz on '{topic}'..."):
|
153 |
try:
|
|
|
154 |
prompt = f"""
|
155 |
Generate a multiple-choice quiz on the topic of "{topic}".
|
156 |
The quiz should have 5 questions.
|
|
|
158 |
Clearly indicate the correct answer for each question at the end in a separate section called "Answer Key".
|
159 |
Format the quiz clearly for easy reading.
|
160 |
"""
|
|
|
161 |
response = model.generate_content(prompt)
|
162 |
quiz_content = response.text
|
163 |
|
164 |
if quiz_content:
|
165 |
+
parsed_quiz_data, answer_key = parse_quiz_content(quiz_content)
|
166 |
+
if parsed_quiz_data:
|
167 |
+
st.session_state.quiz_data = parsed_quiz_data
|
168 |
+
st.session_state.current_question_index = 0 # Reset to first question
|
169 |
+
st.session_state.user_answers = [] # Clear previous answers
|
170 |
+
st.session_state.quiz_completed = False # Reset completion status
|
171 |
+
st.session_state.score = 0 # Reset score
|
172 |
+
st.success(f"Quiz on '{topic}' generated successfully! Let's begin.")
|
|
|
|
|
173 |
else:
|
174 |
+
st.error("Failed to parse quiz content. Please try generating again.")
|
175 |
+
st.session_state.quiz_data = None # Ensure quiz_data is reset in case of parsing failure
|
176 |
else:
|
177 |
st.error("Failed to generate quiz content. Please try again or check your API key.")
|
178 |
|
179 |
except Exception as e:
|
180 |
st.error(f"An error occurred: {e}")
|
181 |
st.error("Please check your API key and network connection. If the problem persists, try a different topic or try again later.")
|
182 |
+
|
183 |
+
# Quiz Display Logic
|
184 |
+
if st.session_state.quiz_data:
|
185 |
+
if not st.session_state.quiz_completed:
|
186 |
+
display_question()
|
187 |
+
else:
|
188 |
+
display_results()
|
189 |
+
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
|
190 |
+
st.info("Click 'Generate Quiz' to start the quiz.")
|