import streamlit as st import os from fpdf import FPDF import uuid # 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" def reset_pdf_cache(): st.session_state.pdf_data = None def generate_pdf_report(): pdf = FPDF() pdf.add_page() pdf.set_font("Arial", size=10) pdf.cell(200, 10, txt="Quiz Report", ln=True, align="C") 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']}", 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) # 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 = {} module_dir = "modules" for filename in os.listdir(module_dir): if filename.endswith(".py") and filename != "__init__.py": module_name = filename[:-3] # Dynamically import the module only when needed module = __import__(f"{module_dir}.{module_name}", fromlist=['']) modules[module_name] = { "title": getattr(module, "title", module_name), "description": getattr(module, "description", "No description available."), "generate_question": module.generate_question # Access the generate_question function } return modules def generate_new_question(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['module'] = module_name # Add the module name to the question data question_data['selected'] = None # Initialize 'selected' to None # 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 def navigate_question(direction): if direction == "prev" and st.session_state.current_index > 0: st.session_state.current_index -= 1 elif direction == "next" and st.session_state.current_index < len(st.session_state.questions) - 1: st.session_state.current_index += 1 # Load all modules dynamically modules = load_modules() # Streamlit interface st.sidebar.title("Quiz Modules") module_name = st.sidebar.radio("Choose a module:", [modules[module]["title"] for module in modules], index=0) selected_module = None for module in modules: if modules[module]["title"] == module_name: selected_module = module 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.questions.append(generate_new_question(selected_module, modules[selected_module])) st.session_state.module_question_count[selected_module] = st.session_state.module_question_count.get(selected_module, 0) st.session_state.module_correct_count[selected_module] = st.session_state.module_correct_count.get(selected_module, 0) st.session_state.selected_answer = None st.session_state.button_label = "Submit/New" # Load the current module's question current_question = st.session_state.questions[st.session_state.current_index] # Display module title and description with smaller header and font st.markdown(f"### {modules[selected_module]['title']}") st.markdown(f"{modules[selected_module]['description']}", unsafe_allow_html=True) # Navigation and PDF report buttons col1, col2, col3 = st.columns([1, 1, 2]) with col1: if st.button("⬅️ Prev", disabled=st.session_state.current_index == 0): navigate_question("prev") with col2: if st.button("➡️ Next", disabled=st.session_state.current_index >= len(st.session_state.questions) - 1): navigate_question("next") with col3: if any(q['answered'] for q in st.session_state.questions): 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" ) # Display the current question with larger font st.markdown(f"Q{st.session_state.current_index + 1}: {current_question['question']}", 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 if not current_question['answered'] else current_question['options'].index(current_question['selected']), disabled=current_question['answered'] ) submit_button = st.form_submit_button(label="Submit/New", disabled=current_question['answered']) # Handle button state and answer submission if submit_button: if selected_answer is None: st.warning("Please select an option before submitting.", icon="⚠️") else: # 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 st.markdown( """