File size: 11,696 Bytes
7ba3b4e c9ca893 bad6a07 e0f928e a2ef5f6 d924141 c9ca893 e0f928e e9df8e8 9d6157e e9df8e8 795b9ba 169cec0 c9ca893 169cec0 c9ca893 9d5f38a be0b9ff c63a46d a086503 a2ef5f6 9d5f38a 32f2e18 795b9ba a5eb002 169cec0 a5eb002 74a6085 a5eb002 700a11e bad6a07 3a8abb6 b5d0d2c 3a8abb6 15d45d1 700a11e 92fe395 15d45d1 700a11e 3a8abb6 a2ef5f6 15d45d1 700a11e 15d45d1 3a8abb6 15d45d1 700a11e 3a8abb6 700a11e 32f2e18 8e6cd67 32f2e18 c9ca893 74a6085 c9ca893 74a6085 c9ca893 67f54db 81e422f 74a6085 67f54db 92fe395 67f54db a2ef5f6 67f54db b2acba8 81e422f ee53acf c9ca893 e9df8e8 74a6085 e9df8e8 92fe395 e9df8e8 a5eb002 bbd4c6d 74a6085 2eb2833 74a6085 bbd4c6d 74a6085 bbd4c6d 74a6085 bbd4c6d f0a1b24 74a6085 f0a1b24 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 a086503 74a6085 a2ef5f6 |
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 |
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=12)
# Header for PDF report
pdf.set_fill_color(211, 211, 211) # Light gray
pdf.cell(0, 10, txt="Magic Math Quiz!", ln=True, align="C", fill=True)
pdf.ln(5)
pdf.set_font("Arial", size=8)
pdf.cell(0, 10, txt="by Ghassem Tofighi", ln=True, align="C", link="https://ghassem.com")
pdf.ln(10)
for i, entry in enumerate(st.session_state.questions):
# Zebra background
if i % 2 == 0:
pdf.set_fill_color(245, 245, 245) # Light gray
else:
pdf.set_fill_color(255, 255, 255) # White
# Module Title
pdf.set_font("Arial", style='B', size=10)
pdf.multi_cell(0, 10, f"Module: {entry['module_title']}", border=1, fill=True)
# Question
pdf.set_font("Arial", style='B', size=10)
pdf.multi_cell(0, 10, f"Q{i+1}: {entry['question']}", border=1, fill=True)
# Time Taken
pdf.set_font("Arial", size=10)
pdf.multi_cell(0, 10, f"Time Taken: {entry['time_taken']} seconds", border=1, fill=True)
# Options
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 for others
pdf.multi_cell(0, 10, f"{options[j]}. {option}", border=1, fill=True)
pdf.set_text_color(0, 0, 0) # Reset color
pdf.multi_cell(0, 10, f"Explanation: {entry['explanation']}", border=1, fill=True)
pdf.multi_cell(0, 10, f"Step-by-Step Solution: {', '.join(entry['step_by_step_solution'])}", border=1, fill=True)
pdf.ln(10)
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 interface
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
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
# Title header with rounded corners and margin
st.markdown(
"""
<div style='background-color: #D3D3D3; padding: 10px; border-radius: 10px; text-align: center; color: black; margin-top: 20px; position: relative;'>
<h3 style='margin: 0;'>Magic Math Quiz!</h3>
<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 style='position: absolute; right: 10px; top: 10px;'>
<button id="pdf-button" style='font-size: 10px;'>
Download PDF Report π
</button>
</div>
</div>
""", unsafe_allow_html=True)
# Disable PDF download button initially if no questions have been answered
pdf_button_disabled = not any(q['answered'] for q in st.session_state.questions)
st.write('<script>document.getElementById("pdf-button").disabled = {};</script>'.format(pdf_button_disabled), unsafe_allow_html=True)
if not pdf_button_disabled:
pdf = generate_pdf_report()
st.session_state.pdf_data = pdf # Reset PDF cache
st.download_button(
label="Download PDF Report π",
data=st.session_state.pdf_data,
file_name="quiz_report.pdf",
mime="application/pdf",
disabled=pdf_button_disabled
)
# Display module title and description with a larger font for description
st.markdown(f"### {selected_module_data['title']}")
st.markdown(f"<span style='font-size: 14px;'>{selected_module_data['description']}</span>", unsafe_allow_html=True)
# 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 a new question after submission
st.session_state.current_question = generate_new_question(selected_category, selected_module, selected_module_data)
st.session_state.start_time = time.time() # Reset the timer for the new question
|