File size: 11,421 Bytes
7461958 ff40b9b eccc273 ff40b9b eccc273 ff40b9b eccc273 431839d 2be75a5 ff40b9b 2be75a5 eccc273 2be75a5 eccc273 2be75a5 eccc273 2be75a5 eccc273 431839d 2be75a5 ff40b9b 2be75a5 ff40b9b 2be75a5 82b0b01 ff40b9b 2be75a5 431839d 2be75a5 431839d 2be75a5 ff40b9b 2be75a5 ff40b9b eccc273 ff40b9b eccc273 ff40b9b 431839d ff40b9b eccc273 ff40b9b eccc273 ff40b9b 7461958 2be75a5 82b0b01 2be75a5 7461958 60c5db6 2e001b9 7461958 38dca3c 7461958 2be75a5 7461958 60c5db6 7461958 60c5db6 7461958 60c5db6 7461958 60c5db6 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 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
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, more robust splitting ---
blocks = re.split(r'\n\n+', question_section)
# --- Handle Potential Title (First Block) - More robust title check ---
if blocks:
first_block = blocks[0].strip()
if not re.search(r'Question\s+\d+:', first_block, re.IGNORECASE): # Use re.search for more flexible title detection
blocks = blocks[1:] # Remove title block if it doesn't look like "Question 1:", etc.
# --- Process Question Blocks ---
question_number = 1 # Keep track of expected question number for better parsing
for block in blocks:
block = block.strip()
if not block: # Skip completely empty blocks
continue
lines = block.split('\n')
question_text = ""
options = {}
parsing_question_text = True # Flag to indicate we are parsing question text
for line_index, line in enumerate(lines):
line = line.strip()
if not line: # Skip empty lines within a question block
continue
if parsing_question_text:
if re.match(r'^Question\s+\d+:', line, re.IGNORECASE): # Check for "Question 1:", "Question 2:", etc.
if len(lines) > line_index + 1: # Check if there's a line after "Question N:"
question_text = lines[line_index + 1].strip() # Take the next line as question text
parsing_question_text = False # Stop parsing question text, move to options
else:
st.warning(f"Warning: Question label '{line}' found, but no question text followed.")
question_text = "" # No question text found after label
parsing_question_text = False # Move to options parsing even if question text missing
else:
# If the line is not "Question N:", and we are still parsing question text,
# it might be the question text itself (in case of format variation)
question_text = line # Consider this line as question text
parsing_question_text = False # Move to options parsing
elif re.match(r'^[A-D]\)\s(.+)', line): # Parse options
match = re.match(r'^([A-D])\)\s(.+)', line)
if match:
option_letter = match.group(1)
option_text = match.group(2).strip()
options[option_letter] = option_text
if question_text: # Only add question if we successfully extracted question text
questions.append({'question': question_text, 'options': options})
else:
st.warning(f"Warning: Could not parse question text for block (potential issue in question {question_number}). Block content:\n{block}")
question_number += 1 # Increment expected question number
# --- 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.
Label each question as "Question 1:", "Question 2:", etc.
Provide four plausible options for each question, labeled A), B), C), and D).
Clearly indicate the correct answer for each question at the end in a section called "Answer Key:".
Format the quiz for easy reading.
"""
response = model.generate_content(prompt)
quiz_content = response.text
# --- DEBUGGING PRINTS UNCOMMENTED ---
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:") # Added Parsed Data debug print back
st.write(parsed_quiz_data) # Display the parsed data structure
# --- END DEBUGGING PRINTS UNCOMMENTED ---
if quiz_content:
if parsed_quiz_data:
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:
st.error("Failed to parse quiz content. Please try generating again.")
st.session_state.quiz_data = None
else:
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.") |