QuizGenerator 0.6.2__tar.gz → 0.6.3__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.
Files changed (58) hide show
  1. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/PKG-INFO +1 -1
  2. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/contentast.py +46 -59
  3. quizgenerator-0.6.2/pyproject_prev.toml → quizgenerator-0.6.3/pyproject.toml +1 -1
  4. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/uv.lock +1 -1
  5. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/.envrc +0 -0
  6. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/.gitignore +0 -0
  7. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/CODEOWNERS +0 -0
  8. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/LICENSE +0 -0
  9. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/__init__.py +0 -0
  10. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/__main__.py +0 -0
  11. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/canvas/__init__.py +0 -0
  12. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/canvas/canvas_interface.py +0 -0
  13. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/canvas/classes.py +0 -0
  14. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/constants.py +0 -0
  15. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/generate.py +0 -0
  16. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/misc.py +0 -0
  17. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/mixins.py +0 -0
  18. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/performance.py +0 -0
  19. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/__init__.py +0 -0
  20. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/basic.py +0 -0
  21. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/__init__.py +0 -0
  22. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/languages.py +0 -0
  23. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/math_questions.py +0 -0
  24. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/memory_questions.py +0 -0
  25. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/ostep13_vsfs.py +0 -0
  26. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/persistence_questions.py +0 -0
  27. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst334/process.py +0 -0
  28. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/__init__.py +0 -0
  29. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/gradient_descent/__init__.py +0 -0
  30. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +0 -0
  31. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +0 -0
  32. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +0 -0
  33. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +0 -0
  34. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/math_and_data/__init__.py +0 -0
  35. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +0 -0
  36. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +0 -0
  37. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/__init__.py +0 -0
  38. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/attention.py +0 -0
  39. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/cnns.py +0 -0
  40. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/matrices.py +0 -0
  41. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/rnns.py +0 -0
  42. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/text.py +0 -0
  43. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/models/weight_counting.py +0 -0
  44. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/neural-network-basics/__init__.py +0 -0
  45. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +0 -0
  46. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/tensorflow-intro/__init__.py +0 -0
  47. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +0 -0
  48. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/qrcode_generator.py +0 -0
  49. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/question.py +0 -0
  50. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/quiz.py +0 -0
  51. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/regenerate.py +0 -0
  52. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/QuizGenerator/typst_utils.py +0 -0
  53. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/README.md +0 -0
  54. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/examples/web_ui_integration_example.py +0 -0
  55. /quizgenerator-0.6.2/pyproject.toml → /quizgenerator-0.6.3/pyproject_prev.toml +0 -0
  56. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/scripts/generate_practice_yaml.sh +0 -0
  57. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/scripts/print.sh +0 -0
  58. {quizgenerator-0.6.2 → quizgenerator-0.6.3}/scripts/vendor_lms_interface.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuizGenerator
3
- Version: 0.6.2
3
+ Version: 0.6.3
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
@@ -2315,61 +2315,53 @@ class ContentAST:
2315
2315
  return fractions.Fraction(decimal.Decimal(str(x)))
2316
2316
 
2317
2317
  @staticmethod
2318
- def accepted_strings(
2319
- value,
2320
- *,
2321
- allow_integer=True,
2322
- allow_simple_fraction=True,
2323
- max_denominator=720,
2324
- allow_mixed=False,
2325
- include_spaces=False,
2326
- include_fixed_even_if_integer=False
2327
- ):
2328
- """Return a sorted list of strings you can paste into Canvas as alternate correct answers."""
2329
- decimal.getcontext().prec = max(34, (ContentAST.Answer.DEFAULT_ROUNDING_DIGITS or 0) + 10)
2330
- f = ContentAST.Answer._to_fraction(value)
2318
+ def accepted_strings(value, max_denominator=720):
2319
+ """
2320
+ Return a sorted list of acceptable answer strings for Canvas.
2321
+
2322
+ Generates:
2323
+ - Integer form (if value is a whole number)
2324
+ - Fixed decimal with DEFAULT_ROUNDING_DIGITS (e.g., "1.0000")
2325
+ - Trimmed decimal without trailing zeros (e.g., "1.25")
2326
+ - Simple fraction if exactly representable (e.g., "5/4")
2327
+
2328
+ Examples:
2329
+ 1 ["1", "1.0000"]
2330
+ 1.25 ["1.25", "1.2500", "5/4"]
2331
+ 0.123444... → ["0.1234"]
2332
+ """
2333
+ rounding_digits = ContentAST.Answer.DEFAULT_ROUNDING_DIGITS
2334
+ decimal.getcontext().prec = max(34, rounding_digits + 10)
2335
+
2331
2336
  outs = set()
2332
2337
 
2333
- # Integer form
2334
- if f.denominator == 1 and allow_integer:
2335
- outs.add(str(f.numerator))
2336
- if include_fixed_even_if_integer:
2337
- q = decimal.Decimal(1).scaleb(-ContentAST.Answer.DEFAULT_ROUNDING_DIGITS)
2338
- d = decimal.Decimal(f.numerator).quantize(q, rounding=decimal.ROUND_HALF_UP)
2339
- outs.add(format(d, 'f'))
2340
-
2341
- # Simple fraction
2342
- if allow_simple_fraction:
2343
- fr = f.limit_denominator(max_denominator)
2344
- if fr == f:
2345
- a, b = fr.numerator, fr.denominator
2346
- if fr.denominator > 1:
2347
- outs.add(f"{a}/{b}")
2348
- if include_spaces:
2349
- outs.add(f"{a} / {b}")
2350
- if allow_mixed and b != 1 and abs(a) > b:
2351
- sign = '-' if a < 0 else ''
2352
- A = abs(a)
2353
- whole, rem = divmod(A, b)
2354
- outs.add(f"{sign}{whole} {rem}/{b}")
2355
- else:
2356
- return sorted(outs, key=lambda s: (len(s), s))
2357
-
2358
- # Fixed-decimal form
2359
- q = decimal.Decimal(1).scaleb(-ContentAST.Answer.DEFAULT_ROUNDING_DIGITS)
2360
- d = (decimal.Decimal(f.numerator) / decimal.Decimal(f.denominator)).quantize(q, rounding=decimal.ROUND_HALF_UP)
2361
- outs.add(format(d, 'f'))
2362
-
2363
- # Trimmed decimal
2364
- if ContentAST.Answer.DEFAULT_ROUNDING_DIGITS:
2365
- q = decimal.Decimal(1).scaleb(-ContentAST.Answer.DEFAULT_ROUNDING_DIGITS)
2366
- d = (decimal.Decimal(f.numerator) / decimal.Decimal(f.denominator)).quantize(q, rounding=decimal.ROUND_HALF_UP)
2367
- s = format(d, 'f').rstrip('0').rstrip('.')
2368
- if s.startswith('.'):
2369
- s = '0' + s
2370
- if s == '-0':
2371
- s = '0'
2372
- outs.add(s)
2338
+ # Round to our standard precision first
2339
+ q = decimal.Decimal(1).scaleb(-rounding_digits)
2340
+ rounded_decimal = decimal.Decimal(str(value)).quantize(q, rounding=decimal.ROUND_HALF_UP)
2341
+
2342
+ # Normalize negative zero to positive zero
2343
+ if rounded_decimal == 0:
2344
+ rounded_decimal = abs(rounded_decimal)
2345
+
2346
+ # Fixed decimal form (e.g., "1.2500")
2347
+ fixed_str = format(rounded_decimal, 'f')
2348
+ outs.add(fixed_str)
2349
+
2350
+ # Trimmed decimal form (e.g., "1.25")
2351
+ trimmed_str = fixed_str.rstrip('0').rstrip('.')
2352
+ if trimmed_str.startswith('.'):
2353
+ trimmed_str = '0' + trimmed_str
2354
+ outs.add(trimmed_str)
2355
+
2356
+ # Integer form (if it's a whole number after rounding)
2357
+ if rounded_decimal == rounded_decimal.to_integral_value():
2358
+ outs.add(str(int(rounded_decimal)))
2359
+
2360
+ # Fraction form (only if exactly representable with reasonable denominator)
2361
+ f = ContentAST.Answer._to_fraction(rounded_decimal)
2362
+ fr = f.limit_denominator(max_denominator)
2363
+ if fr == f and fr.denominator > 1:
2364
+ outs.add(f"{fr.numerator}/{fr.denominator}")
2373
2365
 
2374
2366
  return sorted(outs, key=lambda s: (len(s), s))
2375
2367
 
@@ -2455,12 +2447,7 @@ class AnswerTypes:
2455
2447
  # Use the accepted_strings helper
2456
2448
  answer_strings = ContentAST.Answer.accepted_strings(
2457
2449
  self.value,
2458
- allow_integer=True,
2459
- allow_simple_fraction=True,
2460
- max_denominator=60,
2461
- allow_mixed=True,
2462
- include_spaces=False,
2463
- include_fixed_even_if_integer=True
2450
+ max_denominator=60
2464
2451
  )
2465
2452
 
2466
2453
  canvas_answers = [
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "QuizGenerator"
7
- version = "0.6.1"
7
+ version = "0.6.3"
8
8
  description = "Generate randomized quiz questions for Canvas LMS and PDF exams"
9
9
  readme = "README.md"
10
10
  license = {text = "GPL-3.0-or-later"}
@@ -1214,7 +1214,7 @@ wheels = [
1214
1214
 
1215
1215
  [[package]]
1216
1216
  name = "quizgenerator"
1217
- version = "0.6.2"
1217
+ version = "0.6.3"
1218
1218
  source = { editable = "." }
1219
1219
  dependencies = [
1220
1220
  { name = "canvasapi" },
File without changes
File without changes
File without changes
File without changes
File without changes