quizLM / app.py
EdBoy2202's picture
Update app.py
155cc1f verified
raw
history blame
9.96 kB
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.")