QuizGenerator 0.8.0__tar.gz → 0.9.0__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.8.0 → quizgenerator-0.9.0}/PKG-INFO +9 -6
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/contentast.py +43 -10
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/generate.py +1 -1
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/mixins.py +6 -2
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/basic.py +49 -7
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/process.py +1 -7
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +92 -82
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +68 -45
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +235 -162
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +0 -1
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +51 -45
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +212 -215
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/question.py +176 -18
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/README.md +8 -5
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/pyproject.toml +1 -1
- quizgenerator-0.9.0/pyproject_prev.toml +76 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/uv.lock +1 -1
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/.envrc +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/.gitignore +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/CODEOWNERS +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/LICENSE +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/__main__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/canvas/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/canvas/canvas_interface.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/canvas/classes.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/constants.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/misc.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/performance.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/languages.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/math_questions.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/memory_questions.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/ostep13_vsfs.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/persistence_questions.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/gradient_descent/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/math_and_data/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/attention.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/cnns.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/matrices.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/rnns.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/text.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/models/weight_counting.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/neural-network-basics/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst463/tensorflow-intro/__init__.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/qrcode_generator.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/quiz.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/regenerate.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/typst_utils.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/examples/web_ui_integration_example.py +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/scripts/generate_practice_yaml.sh +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/scripts/print.sh +0 -0
- {quizgenerator-0.8.0 → quizgenerator-0.9.0}/scripts/vendor_lms_interface.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QuizGenerator
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
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
|
|
@@ -150,26 +150,29 @@ All questions follow the same three‑method flow:
|
|
|
150
150
|
|
|
151
151
|
```python
|
|
152
152
|
class MyQuestion(Question):
|
|
153
|
-
|
|
153
|
+
@classmethod
|
|
154
|
+
def _build_context(cls, *, rng_seed=None, **kwargs):
|
|
154
155
|
context = super()._build_context(rng_seed=rng_seed, **kwargs)
|
|
155
|
-
rng = context
|
|
156
|
+
rng = context.rng
|
|
156
157
|
context["value"] = rng.randint(1, 10)
|
|
157
158
|
return context
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
@classmethod
|
|
161
|
+
def _build_body(cls, context):
|
|
160
162
|
body = ca.Section()
|
|
161
163
|
body.add_element(ca.Paragraph([f"Value: {context['value']}"]))
|
|
162
164
|
body.add_element(ca.AnswerTypes.Int(context["value"], label="Value"))
|
|
163
165
|
return body
|
|
164
166
|
|
|
165
|
-
|
|
167
|
+
@classmethod
|
|
168
|
+
def _build_explanation(cls, context):
|
|
166
169
|
explanation = ca.Section()
|
|
167
170
|
explanation.add_element(ca.Paragraph([f"Answer: {context['value']}"]))
|
|
168
171
|
return explanation
|
|
169
172
|
```
|
|
170
173
|
|
|
171
174
|
Notes:
|
|
172
|
-
- Always use `context["rng"]` for deterministic randomness.
|
|
175
|
+
- Always use `context.rng` (or `context["rng"]`) for deterministic randomness.
|
|
173
176
|
- Avoid `refresh()`; it is no longer part of the API.
|
|
174
177
|
|
|
175
178
|
## Built-in Question Types
|
|
@@ -668,7 +668,7 @@ class Section(Container):
|
|
|
668
668
|
- Organizing complex question content
|
|
669
669
|
|
|
670
670
|
Example:
|
|
671
|
-
def _build_body(
|
|
671
|
+
def _build_body(cls, context):
|
|
672
672
|
body = Section()
|
|
673
673
|
answers = []
|
|
674
674
|
body.add_element(Paragraph(["Calculate the determinant:"]))
|
|
@@ -2263,6 +2263,10 @@ class Answer(Leaf):
|
|
|
2263
2263
|
unit_part = f" {self.unit}" if self.unit else ""
|
|
2264
2264
|
|
|
2265
2265
|
return f"{label_part} {blank}{unit_part}".strip()
|
|
2266
|
+
|
|
2267
|
+
@classmethod
|
|
2268
|
+
def get_entry_warning(cls) -> List[str] | None:
|
|
2269
|
+
return None
|
|
2266
2270
|
|
|
2267
2271
|
# Factory methods for common answer types
|
|
2268
2272
|
@classmethod
|
|
@@ -2330,17 +2334,19 @@ class Answer(Leaf):
|
|
|
2330
2334
|
0.123444... → ["0.1234"]
|
|
2331
2335
|
"""
|
|
2332
2336
|
rounding_digits = Answer.DEFAULT_ROUNDING_DIGITS
|
|
2333
|
-
decimal.getcontext().prec = max(34, rounding_digits + 10)
|
|
2334
|
-
|
|
2335
2337
|
outs = set()
|
|
2336
2338
|
|
|
2337
2339
|
# Round to our standard precision first
|
|
2340
|
+
decimal.getcontext().prec = max(34, rounding_digits + 10)
|
|
2338
2341
|
q = decimal.Decimal(1).scaleb(-rounding_digits)
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2342
|
+
if isinstance(value, str) and '/' in value:
|
|
2343
|
+
f = Answer._to_fraction(value)
|
|
2344
|
+
decimal_value = decimal.Decimal(f.numerator) / decimal.Decimal(f.denominator)
|
|
2345
|
+
elif isinstance(value, fractions.Fraction):
|
|
2346
|
+
decimal_value = decimal.Decimal(value.numerator) / decimal.Decimal(value.denominator)
|
|
2347
|
+
else:
|
|
2348
|
+
decimal_value = decimal.Decimal(str(value))
|
|
2349
|
+
rounded_decimal = decimal_value.quantize(q, rounding=decimal.ROUND_HALF_UP)
|
|
2344
2350
|
|
|
2345
2351
|
# Fixed decimal form (e.g., "1.2500")
|
|
2346
2352
|
fixed_str = format(rounded_decimal, 'f')
|
|
@@ -2431,6 +2437,15 @@ class AnswerTypes:
|
|
|
2431
2437
|
|
|
2432
2438
|
# Concrete type answers
|
|
2433
2439
|
class Float(Answer):
|
|
2440
|
+
@classmethod
|
|
2441
|
+
def get_entry_warning(cls) -> List[str] | None:
|
|
2442
|
+
digits = Answer.DEFAULT_ROUNDING_DIGITS
|
|
2443
|
+
return [
|
|
2444
|
+
f"Round floats to {digits} decimal places (fewer if exact, e.g., `1.25`). "
|
|
2445
|
+
"No mixed numbers (e.g. use `5/4`, not `1 1/4`). "
|
|
2446
|
+
"Integers as integers (e.g., `2`, not `2/1`)."
|
|
2447
|
+
]
|
|
2448
|
+
|
|
2434
2449
|
def get_for_canvas(self, single_answer=False) -> List[dict]:
|
|
2435
2450
|
if single_answer:
|
|
2436
2451
|
canvas_answers = [
|
|
@@ -2460,8 +2475,8 @@ class AnswerTypes:
|
|
|
2460
2475
|
return canvas_answers
|
|
2461
2476
|
|
|
2462
2477
|
def get_display_string(self) -> str:
|
|
2463
|
-
|
|
2464
|
-
return f"{self.
|
|
2478
|
+
answer_strings = Answer.accepted_strings(self.value)
|
|
2479
|
+
return answer_strings[0] if len(answer_strings) > 0 else f"{self.value}"
|
|
2465
2480
|
|
|
2466
2481
|
class Int(Answer):
|
|
2467
2482
|
|
|
@@ -2486,6 +2501,10 @@ class AnswerTypes:
|
|
|
2486
2501
|
pass
|
|
2487
2502
|
|
|
2488
2503
|
class List(Answer):
|
|
2504
|
+
@classmethod
|
|
2505
|
+
def get_entry_warning(cls) -> List[str] | None:
|
|
2506
|
+
return ["Enter lists as comma-separated values with a space after the comma (e.g., `1, 2, 3`)."]
|
|
2507
|
+
|
|
2489
2508
|
def __init__(self, order_matters=True, *args, **kwargs):
|
|
2490
2509
|
super().__init__(*args, **kwargs)
|
|
2491
2510
|
self.order_matters = order_matters
|
|
@@ -2523,6 +2542,13 @@ class AnswerTypes:
|
|
|
2523
2542
|
"""
|
|
2524
2543
|
These are self-contained vectors that will go in a single answer block
|
|
2525
2544
|
"""
|
|
2545
|
+
|
|
2546
|
+
@classmethod
|
|
2547
|
+
def get_entry_warning(cls) -> List[str] | None:
|
|
2548
|
+
return [
|
|
2549
|
+
"Enter vectors as comma-separated values with a space after the comma, "
|
|
2550
|
+
"with optional parentheses (e.g., `1, 2` or `(1, 2)`)."
|
|
2551
|
+
]
|
|
2526
2552
|
|
|
2527
2553
|
# Canvas export methods (from misc.Answer)
|
|
2528
2554
|
def get_for_canvas(self, single_answer=False) -> List[dict]:
|
|
@@ -2565,6 +2591,13 @@ class AnswerTypes:
|
|
|
2565
2591
|
"""
|
|
2566
2592
|
Matrix answers generate multiple blank_ids (e.g., M_0_0, M_0_1, M_1_0, M_1_1).
|
|
2567
2593
|
"""
|
|
2594
|
+
|
|
2595
|
+
@classmethod
|
|
2596
|
+
def get_entry_warning(cls) -> List[str] | None:
|
|
2597
|
+
return [
|
|
2598
|
+
"For result matrices, enter `-` in any cell that does not exist.",
|
|
2599
|
+
*AnswerTypes.Float.get_entry_warning()
|
|
2600
|
+
]
|
|
2568
2601
|
|
|
2569
2602
|
def __init__(self, value, *args, **kwargs):
|
|
2570
2603
|
super().__init__(value=value, *args, **kwargs)
|
|
@@ -153,7 +153,7 @@ def test_all_questions(
|
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
# Generate the question (this calls refresh and builds the AST)
|
|
156
|
-
instance = question.instantiate(rng_seed=seed)
|
|
156
|
+
instance = question.instantiate(rng_seed=seed, max_backoff_attempts=200)
|
|
157
157
|
question_ast = question._build_question_ast(instance)
|
|
158
158
|
|
|
159
159
|
# Try rendering to both formats to catch format-specific issues
|
|
@@ -419,8 +419,10 @@ class MathOperationQuestion(MultiPartQuestionMixin, abc.ABC):
|
|
|
419
419
|
subparts.append((operand_a_latex, self.get_operator(), operand_b_latex))
|
|
420
420
|
return subparts
|
|
421
421
|
|
|
422
|
-
|
|
422
|
+
@classmethod
|
|
423
|
+
def _build_body(cls, context):
|
|
423
424
|
"""Build question body and collect answers."""
|
|
425
|
+
self = context
|
|
424
426
|
body = ca.Section()
|
|
425
427
|
answers = []
|
|
426
428
|
|
|
@@ -453,8 +455,10 @@ class MathOperationQuestion(MultiPartQuestionMixin, abc.ABC):
|
|
|
453
455
|
# Default implementation - subclasses should override for specific answer formats
|
|
454
456
|
return []
|
|
455
457
|
|
|
456
|
-
|
|
458
|
+
@classmethod
|
|
459
|
+
def _build_explanation(cls, context):
|
|
457
460
|
"""Default explanation structure. Subclasses should override for specific explanations."""
|
|
461
|
+
self = context
|
|
458
462
|
explanation = ca.Section()
|
|
459
463
|
|
|
460
464
|
explanation.add_element(ca.Paragraph([self.get_explanation_intro()]))
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
from typing import Tuple, List
|
|
5
|
+
from types import SimpleNamespace
|
|
5
6
|
import random
|
|
6
7
|
|
|
7
8
|
import logging
|
|
@@ -17,19 +18,23 @@ log = logging.getLogger(__name__)
|
|
|
17
18
|
class FromText(Question):
|
|
18
19
|
|
|
19
20
|
def __init__(self, *args, text, **kwargs):
|
|
21
|
+
kwargs["text"] = text
|
|
20
22
|
super().__init__(*args, **kwargs)
|
|
21
23
|
self.text = text
|
|
22
24
|
self.possible_variations = 1
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
@classmethod
|
|
27
|
+
def _build_context(cls, *, rng_seed=None, **kwargs):
|
|
25
28
|
context = super()._build_context(rng_seed=rng_seed, **kwargs)
|
|
26
|
-
context["text"] =
|
|
29
|
+
context["text"] = kwargs.get("text", "")
|
|
27
30
|
return context
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
@classmethod
|
|
33
|
+
def _build_body(cls, context) -> Tuple[ca.Element, List[ca.Answer]]:
|
|
30
34
|
return ca.Section([ca.Text(context["text"])]), []
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
@classmethod
|
|
37
|
+
def _build_explanation(cls, context) -> Tuple[ca.Element, List[ca.Answer]]:
|
|
33
38
|
return ca.Section(), []
|
|
34
39
|
|
|
35
40
|
|
|
@@ -43,6 +48,7 @@ class FromGenerator(FromText, TableQuestionMixin):
|
|
|
43
48
|
if generator is None:
|
|
44
49
|
generator = text
|
|
45
50
|
|
|
51
|
+
kwargs["generator"] = generator
|
|
46
52
|
super().__init__(*args, text="", **kwargs)
|
|
47
53
|
self.possible_variations = kwargs.get("possible_variations", float('inf'))
|
|
48
54
|
|
|
@@ -72,15 +78,51 @@ class FromGenerator(FromText, TableQuestionMixin):
|
|
|
72
78
|
# Attach the function dynamically
|
|
73
79
|
attach_function_to_object(self, generator, "generator")
|
|
74
80
|
|
|
75
|
-
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _compile_generator(function_code, function_name="generator"):
|
|
83
|
+
# Provide a deterministic RNG handle for generator snippets.
|
|
84
|
+
function_code = "rng = self.rng\n" + function_code
|
|
85
|
+
|
|
86
|
+
local_namespace = {
|
|
87
|
+
'ca': ca,
|
|
88
|
+
'Section': ca.Section,
|
|
89
|
+
'Text': ca.Text,
|
|
90
|
+
'Table': ca.Table,
|
|
91
|
+
'Paragraph': ca.Paragraph
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
exec_globals = {**globals(), **local_namespace}
|
|
95
|
+
exec(
|
|
96
|
+
f"def {function_name}(self):\n" + "\n".join(f" {line}" for line in function_code.splitlines()),
|
|
97
|
+
exec_globals,
|
|
98
|
+
local_namespace
|
|
99
|
+
)
|
|
100
|
+
return local_namespace[function_name]
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def _build_context(cls, *, rng_seed=None, **kwargs):
|
|
76
104
|
context = super()._build_context(rng_seed=rng_seed, **kwargs)
|
|
105
|
+
for key, value in kwargs.items():
|
|
106
|
+
if key not in context:
|
|
107
|
+
context[key] = value
|
|
77
108
|
# Preserve prior behavior for generators that use the global random module.
|
|
78
109
|
random.seed(rng_seed)
|
|
110
|
+
generator_text = kwargs.get("generator")
|
|
111
|
+
if generator_text is not None:
|
|
112
|
+
context["generator_fn"] = cls._compile_generator(generator_text)
|
|
113
|
+
context["generator_scope"] = SimpleNamespace(
|
|
114
|
+
rng=context.rng,
|
|
115
|
+
**context.data
|
|
116
|
+
)
|
|
79
117
|
return context
|
|
80
118
|
|
|
81
|
-
|
|
119
|
+
@classmethod
|
|
120
|
+
def _build_body(cls, context) -> Tuple[ca.Element, List[ca.Answer]]:
|
|
82
121
|
try:
|
|
83
|
-
|
|
122
|
+
generator_fn = context.get("generator_fn")
|
|
123
|
+
if generator_fn is None:
|
|
124
|
+
raise TypeError("No generator provided for FromGenerator.")
|
|
125
|
+
generated_content = generator_fn(context.get("generator_scope"))
|
|
84
126
|
if isinstance(generated_content, ca.Section):
|
|
85
127
|
body = generated_content
|
|
86
128
|
elif isinstance(generated_content, str):
|
{quizgenerator-0.8.0 → quizgenerator-0.9.0}/QuizGenerator/premade_questions/cst334/process.py
RENAMED
|
@@ -433,13 +433,7 @@ class SchedulingQuestion(ProcessQuestion, RegenerableChoiceMixin, TableQuestionM
|
|
|
433
433
|
f"Break any ties using the job number."
|
|
434
434
|
)
|
|
435
435
|
|
|
436
|
-
|
|
437
|
-
f"Please format answer as fractions, mixed numbers, or numbers rounded to a maximum of {ca.Answer.DEFAULT_ROUNDING_DIGITS} digits after the decimal. "
|
|
438
|
-
"Examples of appropriately formatted answers would be `0`, `3/2`, `1 1/3`, `1.6667`, and `1.25`. "
|
|
439
|
-
"Note that answers that can be rounded to whole numbers should be, rather than being left in fractional form."
|
|
440
|
-
])])
|
|
441
|
-
|
|
442
|
-
body = cls.create_fill_in_table_body(intro_text, instructions, scheduling_table)
|
|
436
|
+
body = cls.create_fill_in_table_body(intro_text, None, scheduling_table)
|
|
443
437
|
body.add_element(average_block)
|
|
444
438
|
return body
|
|
445
439
|
|