Dev-Bot / app.py
Abdul-Haseeb's picture
Update app.py
adf3093 verified
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"<style>{f.read()}</style>", 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 = "<br>".join(interaction['options'])
question_html = f"Question {i + 1}: {interaction['question']}<br>Choices:<br>{options_html}"
div = f"""
<div class="chat-row">
<img class="chat-icon" src="/static/ai_icon.png" width=32 height=32>
<div class="chat-bubble ai-bubble">
{question_html}
</div>
</div>
"""
st.markdown(div, unsafe_allow_html=True)
user_response_html = f"Your answer: {interaction['user_response']}"
div = f"""
<div class="chat-row row-reverse">
<img class="chat-icon" src="/static/user_icon.png" width=32 height=32>
<div class="chat-bubble human-bubble">
{user_response_html}
</div>
</div>
"""
st.markdown(div, unsafe_allow_html=True)
feedback_html = f"Correct! The correct answer is: {interaction['correct_answer']}" if interaction['is_correct'] else f"Wrong. The correct answer is: {interaction['correct_answer']}"
div = f"""
<div class="chat-row">
<img class="chat-icon" src="/static/ai_icon.png" width=32 height=32>
<div class="chat-bubble ai-bubble">
{feedback_html}
</div>
</div>
"""
if idx == len(st.session_state.interactions) - 1:
div = f'<div id="last-message">{div}</div>'
st.markdown(div, unsafe_allow_html=True)
i += 1
save_chat_history(st.session_state.messages)
components.html("""
<script>
const streamlitDoc = window.parent.document;
function scrollToBottom() {
const lastMessage = streamlitDoc.getElementById('last-message');
if (lastMessage) {
lastMessage.scrollIntoView({ behavior: 'smooth' });
}
}
function focusInput() {
const answerInput = streamlitDoc.getElementById('answer_input');
if (answerInput) {
answerInput.focus();
}
}
scrollToBottom();
focusInput();
const buttons = Array.from(
streamlitDoc.querySelectorAll('.stButton > button')
);
const submitButton = buttons.find(
el => el.innerText === 'Submit Answer'
);
streamlitDoc.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
submitButton.click();
setTimeout(() => {
scrollToBottom();
focusInput();
}, 500);
}
});
</script>
""", height=0, width=0)