import streamlit as st
import requests
from pipelines import generate_quiz
import shelve
from dataclasses import dataclass
from typing import Literal
import streamlit.components.v1 as components
st.title(":red[DEV] *the Quizbot* 🤖")
st.subheader("Hey there! I'm **Dev** the QuizBot! Ready to flaunt your smarts in a topic of your choice?", divider='grey')
@st.experimental_dialog("Invalid Choice", width="small")
def warn():
st.write("Please choose from the given options.")
@dataclass
class Message:
origin: Literal["human", "ai"]
message: str
def load_css():
with open("static/styles.css", "r") as f:
st.markdown(f"", unsafe_allow_html=True)
def initialize_session_state():
defaults = {
"quiz": None,
"current_question_index": 0,
"responses": [],
"interactions": [],
"quiz_completed": False,
"topic": None,
"messages": load_chat_history()
}
for key, value in defaults.items():
if key not in st.session_state:
st.session_state[key] = value
def load_chat_history():
with shelve.open("chat_history") as db:
return db.get("messages", [])
def save_chat_history(messages):
with shelve.open("chat_history") as db:
db["messages"] = messages
from urllib.parse import quote
def fetch_wikipedia_link(topic: str) -> str | None:
try:
encoded_topic = quote(topic)
api_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{encoded_topic}"
headers = {
"User-Agent": "QuizBot/1.0 (https://huggingface.co/your-space) Python/3.x"
}
response = requests.get(api_url, headers=headers, timeout=5)
if response.status_code != 200:
st.error(f"Failed to fetch Wikipedia content for '{topic}' (status {response.status_code})")
return None
data = response.json()
return data.get("content_urls", {}).get("desktop", {}).get("page")
except requests.RequestException as e:
st.error(f"Network error: {e}")
return None
load_css()
initialize_session_state()
placeholder = st.container()
if st.session_state.quiz is None:
st.session_state.interactions = []
with st.form("generate_quiz_form"):
st.markdown('**Enter a Topic:**')
cols = st.columns((4, 1))
topic = cols[0].text_input("Topic:", label_visibility="collapsed")
generate_button = cols[1].form_submit_button("Generate Quiz")
if generate_button:
if topic:
link = fetch_wikipedia_link(topic)
if link:
for _ in range(2):
try:
quiz = generate_quiz(link)
if quiz:
st.session_state.quiz = quiz
st.session_state.current_question_index = 0
st.session_state.responses = []
st.session_state.quiz_completed = False
st.rerun()
except Exception as e:
st.error(f"Failed to generate quiz: {e}")
else:
st.error("Please enter a topic.")
else:
with st.form("quiz_form"):
if st.session_state.quiz_completed:
st.markdown("## Results")
score = sum(1 for interaction in st.session_state.interactions if interaction["is_correct"])
if score > len(st.session_state.quiz['questions']):
score = len(st.session_state.quiz['questions'])
st.markdown(f"Your score: {score} out of {len(st.session_state.quiz['questions'])}")
try_again_button = st.form_submit_button("Try Again")
if try_again_button:
for key in ["quiz", "current_question_index", "responses", "interactions", "quiz_completed", "topic"]:
st.session_state[key] = None if key == "quiz" else 0 if key == "current_question_index" else []
st.cache_data.clear()
st.rerun()
else:
current_question_index = st.session_state.current_question_index
if current_question_index < len(st.session_state.quiz["questions"]):
question = st.session_state.quiz["questions"][current_question_index]
question_text = question.get('question', question.get('statement', question.get('prompt', '')))
options = question.get("options", [])
st.markdown(f"**Question {current_question_index + 1}:** {question_text}")
if options:
st.markdown("Choices:")
for option in options:
st.markdown(option)
user_response = st.text_input("Your answer:", key=f"input_{current_question_index}")
submit_button = st.form_submit_button("Submit Answer")
if submit_button:
st.session_state[f"answer_{current_question_index}"] = user_response
correct_answer = question.get("right_option", question.get("answer"))
if options and options[0].lower() in ['true', 'false']:
invalid_option = user_response.lower() not in ["true", "false"]
else:
valid_options = [opt[0].lower() for opt in options]
invalid_option = user_response.lower() not in valid_options and question.get("statement") is None
is_correct = user_response.lower() == correct_answer.lower()
st.session_state.responses.append(user_response)
st.session_state.interactions.append({
"question": question_text,
"user_response": user_response,
"correct_answer": correct_answer,
"is_correct": is_correct,
"invalid_option": invalid_option,
"options": options
})
if not invalid_option:
st.session_state.current_question_index += 1
if st.session_state.current_question_index >= len(st.session_state.quiz["questions"]):
st.session_state.quiz_completed = True
st.rerun()
else:
warn()
with placeholder.container():
if st.session_state.interactions:
i = 0
for idx, interaction in enumerate(st.session_state.interactions):
if interaction['invalid_option']:
continue
options_html = "
".join(interaction['options'])
question_html = f"Question {i + 1}: {interaction['question']}
Choices:
{options_html}"
div = f"""