import streamlit as st import importlib from fpdf import FPDF import uuid # List of available modules with shorter names and icons module_names = { "Bases": "presentation_bases", "Validity": "valid_invalid_numbers", "Conversion": "conversion_bases", "Grouping": "grouping_techniques", "Addition": "addition_bases", "2's Complement": "twos_complement", "Negative Numbers": "negative_binary", "Subtraction": "subtraction_bases", } # Module descriptions module_descriptions = { "Bases": "This module covers the presentation of numbers in various bases, including binary, octal, decimal, and hexadecimal.", "Validity": "This module helps determine the validity of numbers in different bases.", "Conversion": "This module covers conversion techniques between different bases, including with and without fractions.", "Grouping": "This module focuses on grouping techniques for conversion between bases such as binary and hexadecimal.", "Addition": "This module covers addition operations in bases like binary, octal, and hexadecimal.", "2's Complement": "This module explains the 2's complement method for representing negative numbers.", "Negative Numbers": "This module covers negative binary numbers and their representation.", "Subtraction": "This module focuses on subtraction operations in binary using the 2's complement method.", } # Initialize unique session state if 'session_id' not in st.session_state: st.session_state.session_id = str(uuid.uuid4()) # Initialize session state variables 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 = {name: 0 for name in module_names} if 'module_question_count' not in st.session_state: st.session_state.module_question_count = {name: 0 for name in module_names} if 'pdf_data' not in st.session_state: st.session_state.pdf_data = None 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 in module_names.keys(): pdf.set_text_color(0, 0, 0) pdf.cell(200, 10, txt=f"Module: {module}", ln=True, align="L") pdf.ln(5) correct_count = st.session_state.module_correct_count[module] total_count = st.session_state.module_question_count[module] 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_module(module_name): module_file = module_names[module_name] module = importlib.import_module(f'modules.{module_file}') return module def generate_new_question(module_name): module = load_module(module_name) 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 5 options if len(question_data['options']) != 5: st.warning(f"Question does not have 5 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": if st.session_state.current_index < len(st.session_state.questions) - 1: st.session_state.current_index += 1 else: # Generate a new question if at the end of the list new_question = generate_new_question(st.session_state.current_module) st.session_state.questions.append(new_question) st.session_state.current_index += 1 # Streamlit interface st.sidebar.title("Quiz Modules") module_name = st.sidebar.radio("Choose a module:", list(module_names.keys()), index=0) if module_name != st.session_state.current_module: st.session_state.current_module = module_name st.session_state.current_index = 0 st.session_state.questions = [generate_new_question(module_name)] # Load the current module's question if st.session_state.current_index < len(st.session_state.questions): current_question = st.session_state.questions[st.session_state.current_index] else: current_question = generate_new_question(st.session_state.current_module) st.session_state.questions.append(current_question) st.session_state.current_index = len(st.session_state.questions) - 1 # Display module title and description st.title(module_name) st.write(module_descriptions.get(module_name, "No description available for this module.")) st.write(current_question["question"]) # Use a form to prevent rerun on option selection 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 # No pre-selection ) submit_button = st.form_submit_button(label="Submit") if submit_button and not current_question.get('answered', False): if selected_answer is None: st.warning("Please select an answer before submitting.") else: # Process the answer current_question['selected'] = selected_answer current_question['answered'] = True st.session_state.module_question_count[module_name] += 1 if selected_answer == current_question['correct_answer']: st.session_state.correct_count += 1 st.session_state.module_correct_count[module_name] += 1 # 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}") # Show explanation and step-by-step solution 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) # Navigation buttons (placed after question and options) col1, col2 = st.columns([1, 1]) with col1: if st.button("⬅️ Prev", key=f'prev_button_{st.session_state.current_index}', disabled=st.session_state.current_index == 0): navigate_question("prev") with col2: if st.button("➡️ Next", key=f'next_button_{st.session_state.current_index}'): navigate_question("next") # PDF Download Button (at the bottom) 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" )