QuizGenerator 0.4.1__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/PKG-INFO +1 -1
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/contentast.py +2 -2
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +23 -17
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/question.py +60 -21
- quizgenerator-0.4.1/pyproject_prev.toml → quizgenerator-0.4.2/pyproject.toml +1 -1
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/uv.lock +1 -1
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/.envrc +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/.github/pull_request_template.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/.github/workflows/release.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/.gitignore +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/CLAUDE.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/CODEOWNERS +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/LICENSE +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/README.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/__main__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/canvas/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/canvas/canvas_interface.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/canvas/classes.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/constants.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/generate.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/logging.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/misc.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/mixins.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/performance.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/basic.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/languages.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/math_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/memory_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/ostep13_vsfs.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/persistence_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/process.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/gradient_descent/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/math_and_data/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/attention.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/cnns.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/matrices.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/rnns.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/text.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/weight_counting.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/neural-network-basics/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/tensorflow-intro/__init__.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/qrcode_generator.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/quiz.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/regenerate.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/typst_utils.py +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/README.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/documentation/GRADING_GUIDE.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/documentation/LESSONS_LEARNED-adding_questions.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/documentation/PARAMETER_STANDARDS.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/documentation/README.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/documentation/WEB_UI_INTEGRATION.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/all_classes.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/cst334.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/cst463.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/exam_generation.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/scratch.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/specific_generators/cst334.caching.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/specific_generators/cst334.lab-address_translation.yaml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/examples/README.md +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/examples/web_ui_integration_example.py +0 -0
- /quizgenerator-0.4.1/pyproject.toml → /quizgenerator-0.4.2/pyproject_prev.toml +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/scripts/generate_practice_yaml.sh +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/scripts/print.sh +0 -0
- {quizgenerator-0.4.1 → quizgenerator-0.4.2}/scripts/vendor_lms_interface.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QuizGenerator
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Generate randomized quiz questions for Canvas LMS and PDF exams
|
|
5
5
|
Project-URL: Homepage, https://github.com/OtterDen-Lab/QuizGenerator
|
|
6
6
|
Project-URL: Documentation, https://github.com/OtterDen-Lab/QuizGenerator/tree/main/documentation
|
|
@@ -850,7 +850,7 @@ class ContentAST:
|
|
|
850
850
|
if self.inline:
|
|
851
851
|
return f"${self.latex}$"
|
|
852
852
|
else:
|
|
853
|
-
return r"
|
|
853
|
+
return r"$\displaystyle " + f"{self.latex}" + r"$"
|
|
854
854
|
|
|
855
855
|
def render_html(self, **kwargs):
|
|
856
856
|
if self.inline:
|
|
@@ -1048,7 +1048,7 @@ class ContentAST:
|
|
|
1048
1048
|
if self.inline and self.bracket_type == "p":
|
|
1049
1049
|
return f"$\\big(\\begin{{{matrix_env}}} {matrix_content} \\end{{{matrix_env}}}\\big)$"
|
|
1050
1050
|
else:
|
|
1051
|
-
return f"
|
|
1051
|
+
return f"$\\begin{{{matrix_env}}} {matrix_content} \\end{{{matrix_env}}}$"
|
|
1052
1052
|
|
|
1053
1053
|
def render_html(self, **kwargs):
|
|
1054
1054
|
matrix_env = "smallmatrix" if self.inline else f"{self.bracket_type}matrix"
|
|
@@ -49,6 +49,7 @@ class SimpleNeuralNetworkBase(MatrixQuestion, abc.ABC):
|
|
|
49
49
|
# Configuration
|
|
50
50
|
self.activation_function = None
|
|
51
51
|
self.use_bias = kwargs.get("use_bias", True)
|
|
52
|
+
self.param_digits = kwargs.get("param_digits", 1) # Precision for weights/biases
|
|
52
53
|
|
|
53
54
|
# Network parameters (weights and biases)
|
|
54
55
|
self.W1 = None # Input to hidden weights (num_hidden x num_inputs)
|
|
@@ -75,16 +76,19 @@ class SimpleNeuralNetworkBase(MatrixQuestion, abc.ABC):
|
|
|
75
76
|
def _generate_network(self, weight_range=(-2, 2), input_range=(-3, 3)):
|
|
76
77
|
"""Generate random network parameters and input."""
|
|
77
78
|
# Generate weights using MatrixQuestion's rounded matrix method
|
|
79
|
+
# Use param_digits to match display precision in tables and explanations
|
|
78
80
|
self.W1 = self.get_rounded_matrix(
|
|
79
81
|
(self.num_hidden, self.num_inputs),
|
|
80
82
|
low=weight_range[0],
|
|
81
|
-
high=weight_range[1]
|
|
83
|
+
high=weight_range[1],
|
|
84
|
+
digits_to_round=self.param_digits
|
|
82
85
|
)
|
|
83
86
|
|
|
84
87
|
self.W2 = self.get_rounded_matrix(
|
|
85
88
|
(self.num_outputs, self.num_hidden),
|
|
86
89
|
low=weight_range[0],
|
|
87
|
-
high=weight_range[1]
|
|
90
|
+
high=weight_range[1],
|
|
91
|
+
digits_to_round=self.param_digits
|
|
88
92
|
)
|
|
89
93
|
|
|
90
94
|
# Generate biases
|
|
@@ -92,12 +96,14 @@ class SimpleNeuralNetworkBase(MatrixQuestion, abc.ABC):
|
|
|
92
96
|
self.b1 = self.get_rounded_matrix(
|
|
93
97
|
(self.num_hidden,),
|
|
94
98
|
low=weight_range[0],
|
|
95
|
-
high=weight_range[1]
|
|
99
|
+
high=weight_range[1],
|
|
100
|
+
digits_to_round=self.param_digits
|
|
96
101
|
)
|
|
97
102
|
self.b2 = self.get_rounded_matrix(
|
|
98
103
|
(self.num_outputs,),
|
|
99
104
|
low=weight_range[0],
|
|
100
|
-
high=weight_range[1]
|
|
105
|
+
high=weight_range[1],
|
|
106
|
+
digits_to_round=self.param_digits
|
|
101
107
|
)
|
|
102
108
|
else:
|
|
103
109
|
self.b1 = np.zeros(self.num_hidden)
|
|
@@ -256,7 +262,7 @@ class SimpleNeuralNetworkBase(MatrixQuestion, abc.ABC):
|
|
|
256
262
|
for i in range(self.num_inputs):
|
|
257
263
|
left_data.append([
|
|
258
264
|
ContentAST.Equation(f"x_{i+1}", inline=True),
|
|
259
|
-
f"{self.X[i]:.1f}"
|
|
265
|
+
f"{self.X[i]:.1f}" # Inputs are always integers or 1 decimal
|
|
260
266
|
])
|
|
261
267
|
|
|
262
268
|
# Weights from input to hidden
|
|
@@ -264,14 +270,14 @@ class SimpleNeuralNetworkBase(MatrixQuestion, abc.ABC):
|
|
|
264
270
|
for i in range(self.num_inputs):
|
|
265
271
|
left_data.append([
|
|
266
272
|
ContentAST.Equation(f"w_{{{j+1}{i+1}}}", inline=True),
|
|
267
|
-
f"{self.W1[j, i]:.
|
|
273
|
+
f"{self.W1[j, i]:.{self.param_digits}f}"
|
|
268
274
|
])
|
|
269
275
|
|
|
270
276
|
# Weights from hidden to output
|
|
271
277
|
for i in range(self.num_hidden):
|
|
272
278
|
left_data.append([
|
|
273
279
|
ContentAST.Equation(f"w_{i+3}", inline=True),
|
|
274
|
-
f"{self.W2[0, i]:.
|
|
280
|
+
f"{self.W2[0, i]:.{self.param_digits}f}"
|
|
275
281
|
])
|
|
276
282
|
|
|
277
283
|
# Right table: Biases, Activations, Training context
|
|
@@ -283,14 +289,14 @@ class SimpleNeuralNetworkBase(MatrixQuestion, abc.ABC):
|
|
|
283
289
|
for j in range(self.num_hidden):
|
|
284
290
|
right_data.append([
|
|
285
291
|
ContentAST.Equation(f"b_{j+1}", inline=True),
|
|
286
|
-
f"{self.b1[j]:.
|
|
292
|
+
f"{self.b1[j]:.{self.param_digits}f}"
|
|
287
293
|
])
|
|
288
294
|
|
|
289
295
|
# Output bias
|
|
290
296
|
if self.use_bias:
|
|
291
297
|
right_data.append([
|
|
292
298
|
ContentAST.Equation(r"b_{out}", inline=True),
|
|
293
|
-
f"{self.b2[0]:.
|
|
299
|
+
f"{self.b2[0]:.{self.param_digits}f}"
|
|
294
300
|
])
|
|
295
301
|
|
|
296
302
|
# Hidden layer activations (if computed and requested)
|
|
@@ -628,11 +634,11 @@ class ForwardPassQuestion(SimpleNeuralNetworkBase):
|
|
|
628
634
|
# Build equation for z_i
|
|
629
635
|
terms = []
|
|
630
636
|
for j in range(self.num_inputs):
|
|
631
|
-
terms.append(f"({self.W1[i,j]:.
|
|
637
|
+
terms.append(f"({self.W1[i,j]:.{self.param_digits}f})({self.X[j]:.1f})")
|
|
632
638
|
|
|
633
639
|
z_calc = " + ".join(terms)
|
|
634
640
|
if self.use_bias:
|
|
635
|
-
z_calc += f" + {self.b1[i]:.
|
|
641
|
+
z_calc += f" + {self.b1[i]:.{self.param_digits}f}"
|
|
636
642
|
|
|
637
643
|
explanation.add_element(ContentAST.Equation(
|
|
638
644
|
f"z_{i+1} = {z_calc} = {self.z1[i]:.4f}",
|
|
@@ -668,11 +674,11 @@ class ForwardPassQuestion(SimpleNeuralNetworkBase):
|
|
|
668
674
|
|
|
669
675
|
terms = []
|
|
670
676
|
for j in range(self.num_hidden):
|
|
671
|
-
terms.append(f"({self.W2[0,j]:.
|
|
677
|
+
terms.append(f"({self.W2[0,j]:.{self.param_digits}f})({self.a1[j]:.4f})")
|
|
672
678
|
|
|
673
679
|
z_out_calc = " + ".join(terms)
|
|
674
680
|
if self.use_bias:
|
|
675
|
-
z_out_calc += f" + {self.b2[0]:.
|
|
681
|
+
z_out_calc += f" + {self.b2[0]:.{self.param_digits}f}"
|
|
676
682
|
|
|
677
683
|
explanation.add_element(ContentAST.Equation(
|
|
678
684
|
f"z_{{out}} = {z_out_calc} = {self.z2[0]:.4f}",
|
|
@@ -1197,7 +1203,7 @@ class EndToEndTrainingQuestion(SimpleNeuralNetworkBase):
|
|
|
1197
1203
|
# Hidden layer
|
|
1198
1204
|
z1_0 = self.W1[0, 0] * self.X[0] + self.W1[0, 1] * self.X[1] + self.b1[0]
|
|
1199
1205
|
explanation.add_element(ContentAST.Equation(
|
|
1200
|
-
f"z_1 = w_{{11}} x_1 + w_{{12}} x_2 + b_1 = {self.W1[0,0]:.
|
|
1206
|
+
f"z_1 = w_{{11}} x_1 + w_{{12}} x_2 + b_1 = {self.W1[0,0]:.{self.param_digits}f} \\cdot {self.X[0]:.1f} + {self.W1[0,1]:.{self.param_digits}f} \\cdot {self.X[1]:.1f} + {self.b1[0]:.{self.param_digits}f} = {self.z1[0]:.4f}",
|
|
1201
1207
|
inline=False
|
|
1202
1208
|
))
|
|
1203
1209
|
|
|
@@ -1215,7 +1221,7 @@ class EndToEndTrainingQuestion(SimpleNeuralNetworkBase):
|
|
|
1215
1221
|
# Output (pre-activation)
|
|
1216
1222
|
z2 = self.W2[0, 0] * self.a1[0] + self.W2[0, 1] * self.a1[1] + self.b2[0]
|
|
1217
1223
|
explanation.add_element(ContentAST.Equation(
|
|
1218
|
-
f"z_{{out}} = w_3 h_1 + w_4 h_2 + b_2 = {self.W2[0,0]:.
|
|
1224
|
+
f"z_{{out}} = w_3 h_1 + w_4 h_2 + b_2 = {self.W2[0,0]:.{self.param_digits}f} \\cdot {self.a1[0]:.4f} + {self.W2[0,1]:.{self.param_digits}f} \\cdot {self.a1[1]:.4f} + {self.b2[0]:.{self.param_digits}f} = {self.z2[0]:.4f}",
|
|
1219
1225
|
inline=False
|
|
1220
1226
|
))
|
|
1221
1227
|
|
|
@@ -1291,13 +1297,13 @@ class EndToEndTrainingQuestion(SimpleNeuralNetworkBase):
|
|
|
1291
1297
|
|
|
1292
1298
|
new_w3 = self.new_W2[0, 0]
|
|
1293
1299
|
explanation.add_element(ContentAST.Equation(
|
|
1294
|
-
f"w_3^{{new}} = w_3 - \\alpha \\frac{{\\partial L}}{{\\partial w_3}} = {self.W2[0,0]:.
|
|
1300
|
+
f"w_3^{{new}} = w_3 - \\alpha \\frac{{\\partial L}}{{\\partial w_3}} = {self.W2[0,0]:.{self.param_digits}f} - {self.learning_rate} \\cdot {grad_w3:.4f} = {new_w3:.4f}",
|
|
1295
1301
|
inline=False
|
|
1296
1302
|
))
|
|
1297
1303
|
|
|
1298
1304
|
new_w11 = self.new_W1[0, 0]
|
|
1299
1305
|
explanation.add_element(ContentAST.Equation(
|
|
1300
|
-
f"w_{{11}}^{{new}} = w_{{11}} - \\alpha \\frac{{\\partial L}}{{\\partial w_{{11}}}} = {self.W1[0,0]:.
|
|
1306
|
+
f"w_{{11}}^{{new}} = w_{{11}} - \\alpha \\frac{{\\partial L}}{{\\partial w_{{11}}}} = {self.W1[0,0]:.{self.param_digits}f} - {self.learning_rate} \\cdot {grad_w11:.4f} = {new_w11:.4f}",
|
|
1301
1307
|
inline=False
|
|
1302
1308
|
))
|
|
1303
1309
|
|
|
@@ -76,14 +76,21 @@ def parse_spacing(spacing_value) -> float:
|
|
|
76
76
|
|
|
77
77
|
class QuestionRegistry:
|
|
78
78
|
_registry = {}
|
|
79
|
+
_class_name_to_registered_name = {} # Reverse mapping: ClassName -> registered_name
|
|
79
80
|
_scanned = False
|
|
80
|
-
|
|
81
|
+
|
|
81
82
|
@classmethod
|
|
82
83
|
def register(cls, question_type=None):
|
|
83
84
|
def decorator(subclass):
|
|
84
85
|
# Use the provided name or fall back to the class name
|
|
85
86
|
name = question_type.lower() if question_type else subclass.__name__.lower()
|
|
86
87
|
cls._registry[name] = subclass
|
|
88
|
+
|
|
89
|
+
# Build reverse mapping from class name to registered name
|
|
90
|
+
# This allows looking up by class name when QR codes store the class name
|
|
91
|
+
class_name = subclass.__name__.lower()
|
|
92
|
+
cls._class_name_to_registered_name[class_name] = name
|
|
93
|
+
|
|
87
94
|
return subclass
|
|
88
95
|
return decorator
|
|
89
96
|
|
|
@@ -97,29 +104,40 @@ class QuestionRegistry:
|
|
|
97
104
|
# Check to see if it's in the registry
|
|
98
105
|
question_key = question_type.lower()
|
|
99
106
|
if question_key not in cls._registry:
|
|
100
|
-
# Try
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
# Try the reverse mapping from class name to registered name
|
|
108
|
+
# This handles cases where QR codes store class name (e.g., "RNNForwardPass")
|
|
109
|
+
# but the question is registered with a custom name (e.g., "cst463.rnn.forward-pass")
|
|
110
|
+
if question_key in cls._class_name_to_registered_name:
|
|
111
|
+
question_key = cls._class_name_to_registered_name[question_key]
|
|
112
|
+
log.debug(f"Resolved class name '{question_type}' to registered name '{question_key}'")
|
|
113
|
+
else:
|
|
114
|
+
# Try stripping common course prefixes and module paths for backward compatibility
|
|
115
|
+
for prefix in ['cst334.', 'cst463.']:
|
|
116
|
+
if question_key.startswith(prefix):
|
|
117
|
+
stripped_name = question_key[len(prefix):]
|
|
118
|
+
if stripped_name in cls._registry:
|
|
119
|
+
question_key = stripped_name
|
|
120
|
+
break
|
|
121
|
+
# Also try extracting just the final class name after dots
|
|
122
|
+
if '.' in stripped_name:
|
|
123
|
+
final_name = stripped_name.split('.')[-1]
|
|
124
|
+
if final_name in cls._registry:
|
|
125
|
+
question_key = final_name
|
|
126
|
+
break
|
|
127
|
+
else:
|
|
128
|
+
# As a final fallback, try just the last part after dots
|
|
129
|
+
if '.' in question_key:
|
|
130
|
+
final_name = question_key.split('.')[-1]
|
|
110
131
|
if final_name in cls._registry:
|
|
111
132
|
question_key = final_name
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
question_key = final_name
|
|
133
|
+
elif final_name in cls._class_name_to_registered_name:
|
|
134
|
+
# Try the class name reverse mapping on the final part
|
|
135
|
+
question_key = cls._class_name_to_registered_name[final_name]
|
|
136
|
+
log.debug(f"Resolved class name '{final_name}' to registered name '{question_key}'")
|
|
137
|
+
else:
|
|
138
|
+
raise ValueError(f"Unknown question type: {question_type}")
|
|
119
139
|
else:
|
|
120
140
|
raise ValueError(f"Unknown question type: {question_type}")
|
|
121
|
-
else:
|
|
122
|
-
raise ValueError(f"Unknown question type: {question_type}")
|
|
123
141
|
|
|
124
142
|
new_question : Question = cls._registry[question_key](**kwargs)
|
|
125
143
|
# Note: Don't call refresh() here - it will be called by get_question()
|
|
@@ -530,7 +548,8 @@ class Question(abc.ABC):
|
|
|
530
548
|
)
|
|
531
549
|
|
|
532
550
|
# Attach regeneration metadata to the question AST
|
|
533
|
-
|
|
551
|
+
# Use the registered name instead of class name for better QR code regeneration
|
|
552
|
+
question_ast.question_class_name = self._get_registered_name()
|
|
534
553
|
question_ast.generation_seed = actual_seed
|
|
535
554
|
question_ast.question_version = self.VERSION
|
|
536
555
|
# Make a copy of config_params so each question AST has its own
|
|
@@ -642,6 +661,26 @@ class Question(abc.ABC):
|
|
|
642
661
|
return True
|
|
643
662
|
return False
|
|
644
663
|
|
|
664
|
+
def _get_registered_name(self) -> str:
|
|
665
|
+
"""
|
|
666
|
+
Get the registered name for this question class.
|
|
667
|
+
|
|
668
|
+
Returns the name used when registering the question with @QuestionRegistry.register(),
|
|
669
|
+
which may be different from the class name (e.g., "cst463.rnn.forward-pass" vs "RNNForwardPass").
|
|
670
|
+
|
|
671
|
+
This is used for QR code generation to ensure regeneration works correctly.
|
|
672
|
+
Falls back to class name if not found in registry (shouldn't happen in practice).
|
|
673
|
+
"""
|
|
674
|
+
class_name_lower = self.__class__.__name__.lower()
|
|
675
|
+
registered_name = QuestionRegistry._class_name_to_registered_name.get(class_name_lower)
|
|
676
|
+
|
|
677
|
+
if registered_name is None:
|
|
678
|
+
# Fallback to class name if not found (shouldn't happen but be defensive)
|
|
679
|
+
log.warning(f"Question {self.__class__.__name__} not found in registry reverse mapping, using class name")
|
|
680
|
+
return self.__class__.__name__
|
|
681
|
+
|
|
682
|
+
return registered_name
|
|
683
|
+
|
|
645
684
|
class QuestionGroup():
|
|
646
685
|
|
|
647
686
|
def __init__(self, questions_in_group: List[Question], pick_once : bool):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/__init__.py
RENAMED
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/languages.py
RENAMED
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/math_questions.py
RENAMED
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/ostep13_vsfs.py
RENAMED
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst334/process.py
RENAMED
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/cnns.py
RENAMED
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/rnns.py
RENAMED
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/QuizGenerator/premade_questions/cst463/models/text.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/documentation/LESSONS_LEARNED-adding_questions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quizgenerator-0.4.1 → quizgenerator-0.4.2}/example_files/specific_generators/cst334.caching.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|