File size: 9,432 Bytes
e69209e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5c129c
 
 
 
 
 
 
d85098b
 
 
 
 
a5c129c
 
 
 
 
 
 
 
 
 
 
9115055
e69209e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788cc27
 
e69209e
 
 
 
 
 
 
 
 
 
 
38fe9e9
e69209e
 
 
 
 
 
 
788cc27
 
e69209e
 
 
38fe9e9
e69209e
 
 
 
 
 
 
788cc27
 
e69209e
 
 
38fe9e9
e69209e
 
 
 
 
788cc27
 
 
 
e69209e
 
 
788cc27
 
e69209e
 
788cc27
 
e69209e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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)