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( """

🪄 Magic Math Quiz!Beta

Logo
""", 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"""
{selected_category} > {selected_module_data['title']}
""", unsafe_allow_html=True ) with col2: # Add some spacing above the button by placing it inside an empty container st.markdown("
", 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"Q{st.session_state.current_index + 1}: {current_question['question']}", 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"{option_text} ✅", unsafe_allow_html=True) elif option == selected_answer_text: st.markdown(f"{option_text} ❌", unsafe_allow_html=True) else: st.markdown(f"{option_text}", unsafe_allow_html=True) with col2: st.markdown(f"Explanation: {current_question['explanation']}", unsafe_allow_html=True) st.markdown(f"Step-by-Step Solution:", unsafe_allow_html=True) for step in current_question['step_by_step_solution']: st.markdown(f"{step}", 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)