math / app.py
Sina Media Lab
Updates
12e8aed
raw
history blame
12.9 kB
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 link
pdf.set_text_color(0, 0, 255) # Set color to blue for the link
pdf.cell(0, 10, txt="🪄 Magic Math Quiz!", ln=True, align="C", link="https://huggingface.co/spaces/tofighi/math")
pdf.set_text_color(0, 0, 0) # Reset color to black
pdf.ln(5)
# Load the image from the URL, scaled down and centered
pdf.image("assets/gt.png", x=(210 - 25) / 2, w=25, 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("_", " "))
category_description = config.get("description", "No description available.")
order = config.get("order", 100) # Default order is 100 if not specified
else:
category_title = category.title().replace("_", " ")
category_description = "No description available."
order = 100
modules[category_title] = {"order": order, "description": category_description, "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: white; padding: 10px; border-radius: 10px; text-align: center; color: black;'>
<h1 style='margin: 0;'>
<a href="https://huggingface.co/spaces/tofighi/math" target="_blank" style="color: black; text-decoration: none;">
🪄 Magic Math Quiz!<sup>Beta</sup>
</a>
</h1>
<a href="https://ghassem.com" target="_blank">
<img src="https://huggingface.co/spaces/tofighi/math/resolve/main/assets/gt.png" alt="Logo" style="width:50%; margin-top: 10px;">
</a>
</div>
""", unsafe_allow_html=True)
# Sidebar content
st.sidebar.title("Quiz Categories")
selected_category = st.sidebar.selectbox("Choose a category:", list(modules.keys()))
# Get category description for tooltip
category_description = modules[selected_category]["description"] if selected_category and selected_category in modules else "No description available."
if selected_category:
module_options = [modules[selected_category][module]["title"] for module in modules[selected_category]]
selected_module = st.sidebar.radio(
"Choose a module:",
module_options
)
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; color: white; padding: 10px; border-radius: 10px;">
<h2 style="margin: 0;">{selected_category} > {selected_module}</h2>
<p>{selected_module_data['description']}</p>
</div>
""", unsafe_allow_html=True
)
with col2:
if st.button("Download PDF Report", key="download_pdf"):
st.session_state.pdf_data = generate_pdf_report()
st.download_button(
label="Download PDF Report",
data=st.session_state.pdf_data,
file_name=f"quiz_report_{st.session_state.session_id}.pdf",
mime="application/pdf"
)
if selected_module_data:
if st.button(st.session_state.button_label):
if st.session_state.button_label == "Submit":
if st.session_state.selected_answer is not None:
question = st.session_state.questions[st.session_state.current_index]
question['answered'] = True
if st.session_state.selected_answer == question['correct_answer']:
st.session_state.correct_count += 1
st.session_state.module_correct_count[selected_module] = st.session_state.module_correct_count.get(selected_module, 0) + 1
else:
st.session_state.module_correct_count[selected_module] = st.session_state.module_correct_count.get(selected_module, 0)
st.session_state.questions[st.session_state.current_index] = question
st.session_state.current_index += 1
st.session_state.selected_answer = None
st.session_state.button_label = "Next Question"
st.session_state.pdf_data = None # Reset PDF cache when moving to the next question
else:
st.warning("Please select an answer before submitting.")
elif st.session_state.button_label == "Next Question":
if st.session_state.current_index < len(st.session_state.questions):
question = st.session_state.questions[st.session_state.current_index]
st.session_state.selected_answer = None
st.session_state.button_label = "Submit"
else:
st.session_state.button_label = "Submit/New"
st.session_state.current_index = 0
st.session_state.questions = []
st.session_state.correct_count = 0
st.session_state.module_correct_count = {}
st.session_state.pdf_data = generate_pdf_report()
st.experimental_rerun()
if st.session_state.questions:
question = st.session_state.questions[st.session_state.current_index]
st.write(f"**Q{st.session_state.current_index + 1}:** {question['question']}")
options = ['a', 'b', 'c', 'd']
for i, option in enumerate(question['options']):
is_selected = st.session_state.selected_answer == options[i]
option_text = f"{options[i]}. {option}"
if question['answered']:
if options[i] == question['correct_answer']:
st.markdown(f"<p style='color: green;'>{option_text}</p>", unsafe_allow_html=True)
elif is_selected:
st.markdown(f"<p style='color: red;'>{option_text}</p>", unsafe_allow_html=True)
else:
st.markdown(f"<p>{option_text}</p>", unsafe_allow_html=True)
else:
if is_selected:
st.markdown(f"<p style='color: blue;'>{option_text}</p>", unsafe_allow_html=True)
else:
st.markdown(f"<p>{option_text}</p>", unsafe_allow_html=True)
if not question['answered']:
if st.radio("Options", options, key=question['question'], index=options.index(st.session_state.selected_answer) if st.session_state.selected_answer else -1) == options[i]:
st.session_state.selected_answer = options[i]
else:
st.write("No questions available.")
else:
st.write("Please select a category and module.")
# Footer
st.markdown(
"""
<div style='text-align: center; color: grey; font-size: 12px;'>
<p>By Ghassem Tofighi</p>
</div>
""", unsafe_allow_html=True
)