EdBoy2202 commited on
Commit
431839d
·
verified ·
1 Parent(s): 87af401

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -154
app.py CHANGED
@@ -1,38 +1,3 @@
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 = []
@@ -43,26 +8,40 @@ def parse_quiz_content(quiz_content):
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()
@@ -72,15 +51,15 @@ def parse_quiz_content(quiz_content):
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)
@@ -94,108 +73,4 @@ def parse_quiz_content(quiz_content):
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.rerun()
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
148
- topic = st.text_input("Enter the topic for your quiz:")
149
-
150
- if 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.
157
- For each question, provide four plausible options labeled A, B, C, and D.
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
- # --- DEBUGGING PRINTS ---
165
- st.write("### Raw Quiz Content from Gemini:")
166
- st.code(quiz_content) # Display raw content in a code block for readability
167
-
168
- parsed_quiz_data, answer_key = parse_quiz_content(quiz_content)
169
-
170
- st.write("### Parsed Quiz Data:")
171
- st.write(parsed_quiz_data) # Display the parsed data structure
172
-
173
- # --- END DEBUGGING PRINTS ---
174
-
175
-
176
- if quiz_content: # Check if quiz_content was generated successfully (outer if)
177
- if parsed_quiz_data: # Check if parsing was successful (inner if)
178
- st.session_state.quiz_data = parsed_quiz_data
179
- st.session_state.current_question_index = 0
180
- st.session_state.user_answers = []
181
- st.session_state.quiz_completed = False
182
- st.session_state.score = 0
183
- st.success(f"Quiz on '{topic}' generated successfully! Let's begin.")
184
- else: # else associated with inner if parsed_quiz_data
185
- st.error("Failed to parse quiz content. Please try generating again.")
186
- st.session_state.quiz_data = None
187
- else: # else associated with outer if quiz_content
188
- st.error("Failed to generate quiz content. Please try again or check your API key.")
189
-
190
- except Exception as e:
191
- st.error(f"An error occurred: {e}")
192
- st.error("Please check your API key and network connection. If the problem persists, try a different topic or try again later.")
193
-
194
- # Quiz Display Logic
195
- if st.session_state.quiz_data:
196
- if not st.session_state.quiz_completed:
197
- display_question()
198
- else:
199
- display_results()
200
- 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
201
- st.info("Click 'Generate Quiz' to start the quiz.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  def parse_quiz_content(quiz_content):
2
  """Parses the quiz content and answer key into a structured format."""
3
  questions = []
 
8
  st.warning("Could not reliably separate quiz questions and answer key. Parsing might be imperfect.")
9
  return None, None
10
 
11
+ question_section = quiz_parts[0].strip() # Strip whitespace from question section
12
+ answer_key_section = quiz_parts[1].strip() # Strip whitespace from answer key section
13
+
14
+ # --- Improved Question Block Splitting ---
15
+ # Split question section by lines starting with a number followed by a dot and a space (e.g., "1. ")
16
+ question_blocks = re.split(r'\n(?=\d+\.\s)', question_section)
17
 
 
 
18
  for block in question_blocks:
19
+ block = block.strip() # Strip whitespace for each block
20
+ if not block:
21
  continue # Skip empty blocks
22
+
23
+ # --- Improved Question and Options Parsing within each block ---
24
+ lines = block.split('\n')
25
+ question_text = lines[0].strip() # First line is the question
26
+
27
  options = {}
28
+ for line in lines[1:]: # Start from the second line to parse options
29
  line = line.strip()
30
+ if re.match(r'^[A-D]\)\s', line): # Match options starting with A), B), C), D) and a space
31
+ option_letter = line[0]
32
+ option_text = line[2:].strip() # Take text after the "letter) "
33
+ options[option_letter] = option_text
34
+ elif re.match(r'^[A-D]\.\s', line): # Also handle options starting with A., B., C., D. and a space (if Gemini uses this format sometimes)
35
  option_letter = line[0]
36
  option_text = line[2:].strip()
37
  options[option_letter] = option_text
 
38
 
39
+
40
+ if question_text: # Only append if we have a question text (to avoid empty questions from title etc.)
41
+ questions.append({'question': question_text, 'options': options})
42
+
43
+
44
+ # --- Answer Key Parsing (No significant change needed, but strip whitespace) ---
45
  answer_lines = answer_key_section.strip().split('\n')
46
  for line in answer_lines:
47
  line = line.strip()
 
51
  correct_answer = match.group(2)
52
  answer_key_dict[question_num] = correct_answer
53
 
54
+ # Basic validation (similar to before)
55
  if not questions or not answer_key_dict:
56
  st.error("Error parsing quiz content. Please try again or check the generated format.")
57
  return None, None
58
  if len(questions) != len(answer_key_dict):
59
+ 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.")
60
 
61
 
62
+ # Combine parsed questions and answer key into quiz_data (similar to before)
63
  quiz_data_list = []
64
  for i, q_data in enumerate(questions):
65
  correct_answer = answer_key_dict.get(i)
 
73
  st.warning(f"Could not find correct answer for question {i+1} in the answer key.")
74
  return None, None # Inconsistent data, better to stop
75
 
76
+ return quiz_data_list, answer_key_dict