File size: 12,510 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 8aa4295 169cec0 8aa4295 d4c89e6 eda0b5b 700a11e bad6a07 8aa4295 d719d03 8aa4295 852a54f 8aa4295 a58f65d 32f2e18 c9ca893 74a6085 c9ca893 74a6085 c9ca893 67f54db 81e422f 74a6085 67f54db 92fe395 67f54db a2ef5f6 67f54db b2acba8 81e422f ee53acf c9ca893 eda4dad 3949aee e48e9e2 852a54f 2b39dec eda0b5b 2b39dec 3949aee eda4dad 74a6085 1596a24 7f3aa91 1596a24 ffbf590 1596a24 9935146 eda4dad 74a6085 e9df8e8 92fe395 e9df8e8 15d45d1 700a11e 34e799a b2acba8 c63a46d 15d45d1 c63a46d 15d45d1 c63a46d 92fe395 c63a46d 92fe395 b2acba8 a086503 a2ef5f6 b09d39c 15d45d1 b09d39c 15d45d1 b09d39c b5d0d2c 74a6085 a086503 74a6085 a086503 92fe395 e6dc747 852a54f 3111cc8 d4c89e6 852a54f e79a4a1 ffbf590 852a54f |
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 |
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=14)
# Title of the report
pdf.cell(0, 10, txt="Magic Math Quiz", ln=True, align="C")
pdf.set_font("Arial", size=10)
pdf.cell(0, 10, txt="by Ghassem Tofighi", ln=True, align="C", link="https://ghassem.com")
pdf.ln(5)
# Load the image from the local directory
pdf.image("assets/gt.png", x=80, w=50, link="https://ghassem.com")
pdf.ln(10)
for i, entry in enumerate(st.session_state.questions):
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"Category: {entry['category']} > Module: {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("_", " "))
else:
category_title = category.title().replace("_", " ")
modules[category_title] = {}
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][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
}
return modules
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: white; padding: 10px; border-radius: 10px; text-align: center; color: black;'>
<h1 style='margin: 0;'>πͺ Magic Math Quiz!</h1>
<a href="https://ghassem.com" target="_blank">
<img src="assets/gt.png" alt="Logo" style="width:40%; border-radius: 5px;">
</a>
<h6 style='margin: 0; font-size: 10px;'><a href="https://ghassem.com" target="_blank" style="color: black; text-decoration: none;'>By Ghassem Tofighi</a></h6>
</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: 20px;'></div>", unsafe_allow_html=True)
# 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 with larger font
st.markdown(f"<span style='font-size: 18px;'><b>Q{st.session_state.current_index + 1}: {current_question['question']}</b></span>", unsafe_allow_html=True)
# Create the form for the question
with st.form(key=f'question_form_{st.session_state.current_index}'):
options = ['a', 'b', 'c', 'd']
selected_answer = st.radio(
"Choose an answer:",
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
current_question['selected'] = selected_answer_text
current_question['answered'] = True
st.session_state.module_question_count[selected_module] += 1
if selected_answer_text == current_question['correct_answer']:
st.session_state.correct_count += 1
st.session_state.module_correct_count[selected_module] += 1
# 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:red;'>{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"
)
|