Sina Media Lab commited on
Commit
3a56900
·
1 Parent(s): f59ba06
Files changed (1) hide show
  1. modules/valid_invalid_numbers.py +204 -42
modules/valid_invalid_numbers.py CHANGED
@@ -1,48 +1,210 @@
1
- # modules/valid_invalid_numbers.py
 
 
 
 
2
 
3
- title = "Validity of Numbers in Bases"
4
- description = "This module helps determine the validity of numbers in different bases like binary, octal, decimal, and hexadecimal."
 
5
 
6
- def generate_question():
7
- import random
8
-
9
- base = random.choice([2, 8, 10, 16])
10
- length = random.randint(3, 5)
11
-
12
- def generate_valid_number():
13
- return ''.join([str(random.randint(0, base - 1)) for _ in range(length)])
14
-
15
- def generate_invalid_number():
16
- valid_digits = ''.join([str(random.randint(0, base - 1)) for _ in range(length - 1)])
17
- if base < 10:
18
- invalid_digit = str(random.randint(base, 9)) # For bases < 10, pick an invalid digit from [base, 9]
19
- else:
20
- invalid_digit = random.choice('ABCDEF') # For bases >= 10, pick a hex digit to make it invalid
21
- return valid_digits + invalid_digit
 
 
22
 
23
- correct_answer = generate_invalid_number()
24
- options = [correct_answer]
25
 
26
- # Generate valid options
27
- while len(options) < 4:
28
- valid_option = generate_valid_number()
29
- if valid_option not in options:
30
- options.append(valid_option)
31
 
32
- random.shuffle(options)
33
-
34
- question = f"Which of the following is an invalid number in base {base}?"
35
- explanation = f"The number {correct_answer} is invalid in base {base} because it contains a digit outside the range 0-{base-1}."
36
- step_by_step_solution = [
37
- "Step 1: Identify the valid digits for the base.",
38
- "Step 2: Check each option to see if it contains any invalid digits.",
39
- "Step 3: The correct answer will have a digit that is not allowed in the specified base."
40
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- return {
43
- "question": question,
44
- "options": options,
45
- "correct_answer": correct_answer,
46
- "explanation": explanation,
47
- "step_by_step_solution": step_by_step_solution
48
- }
 
1
+ import streamlit as st
2
+ import importlib
3
+ import os
4
+ from fpdf import FPDF
5
+ import uuid
6
 
7
+ # Initialize session state variables
8
+ if 'session_id' not in st.session_state:
9
+ st.session_state.session_id = str(uuid.uuid4())
10
 
11
+ if 'questions' not in st.session_state:
12
+ st.session_state.questions = []
13
+ if 'current_index' not in st.session_state:
14
+ st.session_state.current_index = 0
15
+ if 'current_module' not in st.session_state:
16
+ st.session_state.current_module = None
17
+ if 'correct_count' not in st.session_state:
18
+ st.session_state.correct_count = 0
19
+ if 'module_correct_count' not in st.session_state:
20
+ st.session_state.module_correct_count = {}
21
+ if 'module_question_count' not in st.session_state:
22
+ st.session_state.module_question_count = {}
23
+ if 'pdf_data' not in st.session_state:
24
+ st.session_state.pdf_data = None
25
+ if 'submit_disabled' not in st.session_state:
26
+ st.session_state.submit_disabled = True
27
+ if 'next_disabled' not in st.session_state:
28
+ st.session_state.next_disabled = True
29
 
30
+ def reset_pdf_cache():
31
+ st.session_state.pdf_data = None
32
 
33
+ def generate_pdf_report():
34
+ pdf = FPDF()
35
+ pdf.add_page()
36
+ pdf.set_font("Arial", size=12)
 
37
 
38
+ pdf.cell(200, 10, txt="Quiz Report", ln=True, align="C")
39
+ pdf.ln(10)
40
+
41
+ for module, data in st.session_state.module_question_count.items():
42
+ correct_count = st.session_state.module_correct_count.get(module, 0)
43
+ total_count = data
44
+ pdf.cell(200, 10, txt=f"Module: {module}", ln=True, align="L")
45
+ pdf.ln(5)
46
+ pdf.cell(200, 10, txt=f"Correct Answers: {correct_count}/{total_count}", ln=True, align="L")
47
+ pdf.ln(5)
48
+
49
+ for entry in st.session_state.questions:
50
+ if entry['module'] == module:
51
+ question, options, selected, correct, explanation, step_by_step_solution = (
52
+ entry['question'],
53
+ entry['options'],
54
+ entry['selected'] if entry['selected'] is not None else "Not Answered",
55
+ entry['correct_answer'],
56
+ entry['explanation'],
57
+ entry['step_by_step_solution']
58
+ )
59
+ pdf.multi_cell(0, 10, f"Q: {question}")
60
+ for option in options:
61
+ if option == correct:
62
+ pdf.set_text_color(0, 128, 0) # Green for correct
63
+ pdf.multi_cell(0, 10, f"{option}")
64
+ elif option == selected:
65
+ pdf.set_text_color(255, 0, 0) # Red for incorrect
66
+ pdf.multi_cell(0, 10, f"{option}")
67
+ else:
68
+ pdf.set_text_color(0, 0, 0) # Default color for others
69
+ pdf.multi_cell(0, 10, f" {option}")
70
+ pdf.set_text_color(0, 0, 0) # Reset color
71
+ pdf.multi_cell(0, 10, f"Explanation: {explanation}")
72
+ pdf.ln(5)
73
+ pdf.multi_cell(0, 10, "Step-by-Step Solution:")
74
+ for step in step_by_step_solution:
75
+ pdf.multi_cell(0, 10, step)
76
+ pdf.ln(10)
77
+
78
+ pdf.ln(10) # Add space after each module
79
+
80
+ return pdf.output(dest='S').encode('latin1', 'replace')
81
+
82
+ def load_modules():
83
+ modules = {}
84
+ module_dir = "modules"
85
+ for filename in os.listdir(module_dir):
86
+ if filename.endswith(".py") and filename != "__init__.py":
87
+ module_name = filename[:-3]
88
+ module = importlib.import_module(f"{module_dir}.{module_name}")
89
+ modules[module_name] = {
90
+ "title": getattr(module, "title", module_name),
91
+ "description": getattr(module, "description", "No description available."),
92
+ "generate_question": module.generate_question
93
+ }
94
+ return modules
95
+
96
+ def generate_new_question(module_name, module):
97
+ question_data = module['generate_question']()
98
+ # Ensure 'answered' is initialized to False and add the 'module' and 'selected' keys
99
+ question_data['answered'] = False
100
+ question_data['module'] = module_name # Add the module name to the question data
101
+ question_data['selected'] = None # Initialize 'selected' to None
102
+ # Ensure there are exactly 4 options
103
+ if len(question_data['options']) != 4:
104
+ st.warning(f"Question in module '{module_name}' does not have 4 options. Found {len(question_data['options'])}.")
105
+ return question_data
106
+
107
+ def navigate_question(direction):
108
+ if direction == "prev" and st.session_state.current_index > 0:
109
+ st.session_state.current_index -= 1
110
+ elif direction == "next":
111
+ new_question = generate_new_question(st.session_state.current_module, modules[st.session_state.current_module])
112
+ st.session_state.questions.append(new_question)
113
+ st.session_state.current_index = len(st.session_state.questions) - 1
114
+
115
+ # Load all modules dynamically
116
+ modules = load_modules()
117
+
118
+ # Streamlit interface
119
+ st.sidebar.title("Quiz Modules")
120
+ module_name = st.sidebar.radio("Choose a module:", [modules[module]["title"] for module in modules], index=0)
121
+
122
+ selected_module = None
123
+ for module in modules:
124
+ if modules[module]["title"] == module_name:
125
+ selected_module = module
126
+ break
127
+
128
+ if selected_module != st.session_state.current_module:
129
+ st.session_state.current_module = selected_module
130
+ st.session_state.current_index = 0
131
+ st.session_state.questions = [generate_new_question(selected_module, modules[selected_module])]
132
+ st.session_state.module_question_count[selected_module] = 0
133
+ st.session_state.module_correct_count[selected_module] = 0
134
+ st.session_state.submit_disabled = True
135
+ st.session_state.next_disabled = True
136
+
137
+ # Load the current module's question
138
+ current_question = st.session_state.questions[st.session_state.current_index]
139
+
140
+ # Display module title and description
141
+ st.title(modules[selected_module]["title"])
142
+ st.write(modules[selected_module]["description"])
143
+
144
+ # Navigation and PDF report buttons
145
+ col1, col2, col3 = st.columns([1, 1, 2])
146
+ with col1:
147
+ if st.button("⬅️ Prev", disabled=st.session_state.current_index == 0):
148
+ navigate_question("prev")
149
+ with col2:
150
+ if st.button("➡️ Next", disabled=st.session_state.next_disabled):
151
+ st.session_state.submit_disabled = True
152
+ st.session_state.next_disabled = True
153
+ navigate_question("next")
154
+ with col3:
155
+ if len(st.session_state.questions) > 0:
156
+ pdf = generate_pdf_report()
157
+ st.session_state.pdf_data = pdf # Reset PDF cache
158
+ st.download_button(
159
+ label="Download PDF Report 📄",
160
+ data=st.session_state.pdf_data,
161
+ file_name="quiz_report.pdf",
162
+ mime="application/pdf"
163
+ )
164
+
165
+ st.write(current_question["question"])
166
+
167
+ # Use a form to prevent rerun on option selection
168
+ with st.form(key=f'question_form_{st.session_state.current_index}'):
169
+ selected_answer = st.radio(
170
+ "Choose an answer:",
171
+ options=current_question['options'],
172
+ key=f"question_{st.session_state.current_index}_options",
173
+ index=None # No pre-selection
174
+ )
175
+ submit_button = st.form_submit_button(label="Submit", disabled=st.session_state.submit_disabled)
176
+
177
+ if selected_answer and st.session_state.submit_disabled:
178
+ st.session_state.submit_disabled = False
179
+
180
+ if submit_button and not current_question.get('answered', False):
181
+ if selected_answer is None:
182
+ st.warning("Please select an answer before submitting.")
183
+ else:
184
+ # Process the answer
185
+ current_question['selected'] = selected_answer
186
+ current_question['answered'] = True
187
+ st.session_state.module_question_count[selected_module] += 1
188
+
189
+ if selected_answer == current_question['correct_answer']:
190
+ st.session_state.correct_count += 1
191
+ st.session_state.module_correct_count[selected_module] += 1
192
+
193
+ st.session_state.submit_disabled = True
194
+ st.session_state.next_disabled = False
195
+
196
+ # Show correct/incorrect feedback after submission
197
+ if current_question.get('answered', False):
198
+ for option in current_question['options']:
199
+ if option == current_question['correct_answer']:
200
+ st.markdown(f"<span style='color:green;'>{option} ✅</span>", unsafe_allow_html=True)
201
+ elif option == current_question['selected']:
202
+ st.markdown(f"<span style='color:red;'>{option} ❌</span>", unsafe_allow_html=True)
203
+ else:
204
+ st.markdown(f"{option}")
205
 
206
+ # Show explanation and step-by-step solution
207
+ st.write(f"**Explanation:** {current_question['explanation']}")
208
+ st.write("**Step-by-Step Solution:**")
209
+ for step in current_question['step_by_step_solution']:
210
+ st.write(step)