File size: 13,478 Bytes
7ba3b4e c9ca893 bad6a07 e0f928e a2ef5f6 d924141 c9ca893 e0f928e e9df8e8 9d6157e e9df8e8 795b9ba 169cec0 c9ca893 169cec0 c9ca893 9d5f38a d719d03 be0b9ff c63a46d a086503 a2ef5f6 9d5f38a d719d03 32f2e18 795b9ba 0fdb658 169cec0 98ba964 d4c89e6 eda0b5b 14697cf c7d4159 700a11e bad6a07 8aa4295 c0c50e8 8aa4295 c0c50e8 8aa4295 852a54f 8aa4295 a58f65d 32f2e18 c9ca893 74a6085 bacb566 74a6085 bacb566 74a6085 19710ad 74a6085 bacb566 74a6085 bacb566 c9ca893 74a6085 c9ca893 67f54db 81e422f 74a6085 67f54db 92fe395 67f54db a2ef5f6 67f54db b2acba8 81e422f ee53acf c9ca893 eda4dad 3949aee 98ba964 0fdb658 c7d4159 0fdb658 3949aee eda4dad 74a6085 fcee975 74a6085 19710ad 74a6085 1596a24 19710ad 1596a24 19710ad 1596a24 19710ad 1596a24 19710ad 9935146 19710ad 9935146 19710ad 9935146 eda4dad 19710ad b09d39c 19710ad 5eeb228 19710ad 5eeb228 19710ad 5eeb228 19710ad 74a6085 19710ad 5eeb228 74a6085 19710ad 852a54f 19710ad |
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 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
import streamlit as st
import os
from fpdf import FPDF
import uuid
import time
# Initialize session state variables
if 'session_id' not in st.session_state:
st.session_state.session_id = str(uuid.uuid4())
if 'questions' not in st.session_state:
st.session_state.questions = []
if 'current_index' not in st.session_state:
st.session_state.current_index = 0
if 'current_module' not in st.session_state:
st.session_state.current_module = None
if 'correct_count' not in st.session_state:
st.session_state.correct_count = 0
if 'module_correct_count' not in st.session_state:
st.session_state.module_correct_count = {}
if 'module_question_count' not in st.session_state:
st.session_state.module_question_count = {}
if 'pdf_data' not in st.session_state:
st.session_state.pdf_data = None
if 'selected_answer' not in st.session_state:
st.session_state.selected_answer = None
if 'button_label' not in st.session_state:
st.session_state.button_label = "Submit/New"
if 'start_time' not in st.session_state:
st.session_state.start_time = time.time()
def reset_pdf_cache():
st.session_state.pdf_data = None
def generate_pdf_report():
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", style='B', size=18)
# Title of the report with a clickable link
pdf.set_text_color(0, 0, 255) # Set text color to blue for the link
pdf.set_font("Arial", style='B', size=18)
pdf.cell(0, 10, txt="Magic Math Quiz!", ln=True, align="C", link="https://huggingface.co/spaces/tofighi/math")
pdf.ln(5)
# Load the image from the URL, scaled down and centered
pdf.image("assets/gt.png", x=(210 - 10) / 2, w=10, link="https://ghassem.com") # Centered horizontally and scaled down
pdf.ln(10)
for i, entry in enumerate(st.session_state.questions):
# Add Category > Module as a header
pdf.set_fill_color(0, 0, 0) # Black background
pdf.set_text_color(255, 255, 255) # White text
pdf.set_font("Arial", style='B', size=12)
pdf.cell(0, 10, f"{entry['category']} > {entry['module_title']}", ln=True, align="L", fill=True)
pdf.set_text_color(0, 0, 0) # Reset text color to black
pdf.set_font("Arial", size=12)
pdf.ln(5)
# Question number and text
pdf.set_font("Arial", style='B', size=12)
pdf.multi_cell(0, 10, f"Q{i+1}: {entry['question']}")
pdf.ln(3)
# Time taken
pdf.set_font("Arial", size=10)
pdf.multi_cell(0, 10, f"Time Taken: {entry['time_taken']} seconds")
pdf.ln(3)
# Options and correct/incorrect answer
pdf.set_font("Arial", size=10)
options = ['a', 'b', 'c', 'd']
for j, option in enumerate(entry['options']):
if option == entry['correct_answer']:
pdf.set_text_color(0, 128, 0) # Green for correct
elif option == entry['selected']:
pdf.set_text_color(255, 0, 0) # Red for incorrect
else:
pdf.set_text_color(0, 0, 0) # Default color
pdf.multi_cell(0, 10, f"{options[j]}. {option}")
pdf.set_text_color(0, 0, 0) # Reset color
pdf.ln(5)
# Explanation and Step-by-Step Solution
pdf.set_font("Arial", style='B', size=10)
pdf.multi_cell(0, 10, "Explanation:")
pdf.set_font("Arial", size=10)
pdf.multi_cell(0, 10, entry['explanation'])
pdf.ln(3)
pdf.set_font("Arial", style='B', size=10)
pdf.multi_cell(0, 10, "Step-by-Step Solution:")
pdf.set_font("Arial", size=10)
for step in entry['step_by_step_solution']:
pdf.multi_cell(0, 10, step)
pdf.ln(10) # Add space after each question
return pdf.output(dest='S').encode('latin1', 'replace')
def load_modules():
modules = {}
base_dir = "modules"
for category in os.listdir(base_dir):
category_dir = os.path.join(base_dir, category)
if os.path.isdir(category_dir):
config_path = os.path.join(category_dir, "config.py")
if os.path.exists(config_path):
config = {}
with open(config_path) as f:
exec(f.read(), config)
category_title = config.get("title", category.title().replace("_", " "))
order = config.get("order", 100) # Default order is 100 if not specified
else:
category_title = category.title().replace("_", " ")
order = 100
modules[category_title] = {"order": order, "modules": {}}
for filename in os.listdir(category_dir):
if filename.endswith(".py") and filename != "__init__.py" and filename != "config.py":
module_name = filename[:-3]
module = __import__(f"{category_dir.replace('/', '.')}.{module_name}", fromlist=[''])
modules[category_title]["modules"][module_name] = {
"title": getattr(module, "title", module_name.replace("_", " ").title()),
"description": getattr(module, "description", "No description available."),
"generate_question": module.generate_question # Access the generate_question function
}
# Sort categories by the order value
sorted_categories = sorted(modules.items(), key=lambda x: x[1]['order'])
return {category: data["modules"] for category, data in sorted_categories}
def generate_new_question(category_name, module_name, module):
question_data = module['generate_question']()
# Ensure 'answered' is initialized to False and add the 'module' and 'selected' keys
question_data['answered'] = False
question_data['category'] = category_name # Add the category name to the question data
question_data['module'] = module_name # Add the module name to the question data
question_data['module_title'] = module['title'] # Add the module title to the question data
question_data['selected'] = None # Initialize 'selected' to None
question_data['time_taken'] = 0 # Initialize time taken to 0
# Ensure there are exactly 4 options
if len(question_data['options']) != 4:
st.warning(f"Question in module '{module_name}' does not have 4 options. Found {len(question_data['options'])}.")
return question_data
# Load all modules dynamically
modules = load_modules()
# Streamlit sidebar
st.sidebar.markdown(
"""
<div style='background-color: lightgray; padding: 10px; border-radius: 10px; text-align: center; color: black;'>
<a href="https://huggingface.co/spaces/tofighi/math" target="_blank" style='text-decoration: none; color: black;'>
<h1 style='margin: 0;'>πͺ Magic Math Quiz!<sup>Beta</sup></h1>
</a>
<a href="https://ghassem.com" target="_blank">
<img src="https://huggingface.co/spaces/tofighi/math/resolve/main/assets/gt.png" alt="Logo" style="width:25%; margin-top: 10px;">
</a>
</div>
""", unsafe_allow_html=True)
st.sidebar.title("Quiz Categories")
selected_category = st.sidebar.selectbox("Choose a category:", list(modules.keys()))
if selected_category:
selected_module = st.sidebar.radio("Choose a module:", [modules[selected_category][module]["title"] for module in modules[selected_category]])
for module_name, module_data in modules[selected_category].items():
if module_data["title"] == selected_module:
selected_module_data = module_data
break
# Create a horizontal layout with the category title and the PDF download button
col1, col2 = st.columns([3, 1])
with col1:
st.markdown(
f"""
<div style="background-color: #333; padding: 10px; border-radius: 5px; margin-top: 20px;">
<span style='font-size: 18px; color: white;'>{selected_category} > {selected_module_data['title']}</span>
</div>
""",
unsafe_allow_html=True
)
with col2:
# Add some spacing above the button by placing it inside an empty container
st.markdown("<div style='margin-top: 25px;'></div>", unsafe_allow_html=True) # Increased margin by 5px
# Show PDF report button, initially disabled until a question is answered
pdf_disabled = len(st.session_state.questions) == 0
if st.session_state.pdf_data:
st.download_button(
label="Quiz Report",
data=st.session_state.pdf_data,
file_name="quiz_report.pdf",
mime="application/pdf",
disabled=False,
key="pdf_download_button"
)
if selected_module != st.session_state.current_module:
st.session_state.current_module = selected_module
st.session_state.current_index = len(st.session_state.questions) # Continue numbering from previous questions
st.session_state.selected_answer = None
st.session_state.button_label = "Submit/New"
st.session_state.start_time = time.time() # Start the timer for the new question
# Initialize question count and correct count if not already done
if selected_module not in st.session_state.module_question_count:
st.session_state.module_question_count[selected_module] = 0
if selected_module not in st.session_state.module_correct_count:
st.session_state.module_correct_count[selected_module] = 0
# Generate a new question without adding it to the answered list yet
st.session_state.current_question = generate_new_question(selected_category, selected_module, selected_module_data)
current_question = st.session_state.current_question
# Display the current question inside the options box
with st.form(key=f'question_form_{st.session_state.current_index}'):
options = ['a', 'b', 'c', 'd']
st.markdown(f"<b>Q{st.session_state.current_index + 1}: {current_question['question']}</b>", unsafe_allow_html=True)
selected_answer = st.radio(
"", # Empty label to put the question inside the options box
options=[f"{options[i]}. {opt}" for i, opt in enumerate(current_question['options'])],
key=f"question_{st.session_state.current_index}_options",
index=None,
)
submit_button = st.form_submit_button(label="Submit/New")
# Handle button state and answer submission
if submit_button:
if selected_answer is None:
st.warning("Please select an option before submitting.", icon="β οΈ")
else:
# Calculate time taken to answer the question
current_question['time_taken'] = int(time.time() - st.session_state.start_time)
# Process the answer
selected_answer_text = selected_answer.split(". ", 1)[1] # Extract the text part after "a. ", "b. ", etc.
current_question['selected'] = selected_answer_text
current_question['answered'] = True
st.session_state.module_question_count[selected_module] += 1
# Check if the answer is correct
if selected_answer_text == current_question['correct_answer']:
st.session_state.correct_count += 1
st.session_state.module_correct_count[selected_module] += 1
feedback_text = "Correct! β
"
feedback_color = "green"
else:
feedback_text = "Incorrect β"
feedback_color = "red"
# Show correct/incorrect feedback, explanation, and step-by-step solution
col1, col2 = st.columns(2)
with col1:
for i, option in enumerate(current_question['options']):
option_text = f"{options[i]}. {option}"
if option == current_question['correct_answer']:
st.markdown(f"<span style='color:green;'>{option_text} β
</span>", unsafe_allow_html=True)
elif option == selected_answer_text:
st.markdown(f"<span style='color:{feedback_color};'>{option_text} β</span>", unsafe_allow_html=True)
else:
st.markdown(f"{option_text}", unsafe_allow_html=True)
with col2:
st.markdown(f"<span style='font-size: 14px;'><b>Explanation:</b> {current_question['explanation']}</span>", unsafe_allow_html=True)
st.markdown(f"<span style='font-size: 14px;'><b>Step-by-Step Solution:</b></span>", unsafe_allow_html=True)
for step in current_question['step_by_step_solution']:
st.markdown(f"<span style='font-size: 14px;'>{step}</span>", unsafe_allow_html=True)
# Add the question to the answered list only after submission
st.session_state.questions.append(current_question)
st.session_state.current_index = len(st.session_state.questions)
# Generate the PDF report data after each submission
st.session_state.pdf_data = generate_pdf_report()
# Refresh the PDF button state
st.download_button(
label="Quiz Report",
data=st.session_state.pdf_data,
file_name="quiz_report.pdf",
mime="application/pdf",
disabled=False,
key="pdf_download_button_updated"
)
# Generate a new question for the next round
st.session_state.current_question = generate_new_question(selected_category, selected_module, selected_module_data)
|