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=12) pdf.cell(200, 10, txt="Quiz Report", ln=True, align="C") pdf.ln(10) for module, data in st.session_state.module_question_count.items(): correct_count = st.session_state.module_correct_count.get(module, 0) total_count = data pdf.cell(200, 10, txt=f"Module: {module}", ln=True, align="L") pdf.ln(5) pdf.cell(200, 10, txt=f"Correct Answers: {correct_count}/{total_count}", ln=True, align="L") pdf.ln(5) for entry in st.session_state.questions: if entry['module'] == module: question, options, selected, correct, explanation, step_by_step_solution = ( entry['question'], entry['options'], entry['selected'] if entry['selected'] is not None else "Not Answered", entry['correct_answer'], entry['explanation'], entry['step_by_step_solution'] ) pdf.multi_cell(0, 10, f"Q: {question}") for option in options: if option == correct: pdf.set_text_color(0, 128, 0) # Green for correct pdf.multi_cell(0, 10, f"{option}") elif option == selected: pdf.set_text_color(255, 0, 0) # Red for incorrect pdf.multi_cell(0, 10, f"{option}") else: pdf.set_text_color(0, 0, 0) # Default color for others pdf.multi_cell(0, 10, f" {option}") pdf.set_text_color(0, 0, 0) # Reset color pdf.multi_cell(0, 10, f"Explanation: {explanation}") pdf.ln(5) pdf.multi_cell(0, 10, "Step-by-Step Solution:") for step in step_by_step_solution: pdf.multi_cell(0, 10, step) pdf.ln(10) pdf.ln(10) # Add space after each module 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']() question_data['answered'] = False question_data['module'] = module_name question_data['selected'] = None 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 = 0 st.session_state.questions = [generate_new_question(selected_module, modules[selected_module])] st.session_state.module_question_count[selected_module] = 0 st.session_state.module_correct_count[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 st.title(modules[selected_module]["title"]) st.write(modules[selected_module]["description"]) # 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 len(st.session_state.questions) > 0: 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" ) st.write(current_question["question"]) # Create the form for the question with st.form(key=f'question_form_{st.session_state.current_index}'): selected_answer = st.radio( "Choose an answer:", options=current_question['options'], key=f"question_{st.session_state.current_index}_options", index=None # Ensure no option is pre-selected ) submit_button = st.form_submit_button(label=st.session_state.button_label) # Handle button state and answer submission if submit_button and st.session_state.button_label == "Submit/New": if selected_answer is not None: # Process the answer current_question['selected'] = selected_answer current_question['answered'] = True st.session_state.module_question_count[selected_module] += 1 if selected_answer == current_question['correct_answer']: st.session_state.correct_count += 1 st.session_state.module_correct_count[selected_module] += 1 st.session_state.button_label = "Next Question" else: st.warning("Please select an option before submitting.", icon="⚠️") st.stop() # Show correct/incorrect feedback after submission if current_question.get('answered', False): for option in current_question['options']: if option == current_question['correct_answer']: st.markdown(f"{option} ✅", unsafe_allow_html=True) elif option == current_question['selected']: st.markdown(f"{option} ❌", unsafe_allow_html=True) else: st.markdown(f"{option}") st.write(f"**Explanation:** {current_question['explanation']}") st.write("**Step-by-Step Solution:**") for step in current_question['step_by_step_solution']: st.write(step) # Disable options and button when navigating through previous questions if st.session_state.current_index < len(st.session_state.questions) - 1: st.session_state.button_label = "Next Question" else: st.session_state.button_label = "Submit/New" if st.session_state.current_index > 0: st.session_state.button_label = "Next Question" st.session_state.selected_answer = None st.form_submit_button(label=st.session_state.button_label, disabled=True) st.radio("Choose an answer:", options=current_question['options'], index=None, disabled=True)