QuizGenerator 0.1.0__tar.gz → 0.1.1__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 (74) hide show
  1. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/.gitignore +2 -1
  2. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/PKG-INFO +1 -1
  3. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/generate.py +20 -117
  4. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/pyproject.toml +1 -1
  5. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/uv.lock +0 -71
  6. quizgenerator-0.1.0/.gitmodules +0 -0
  7. quizgenerator-0.1.0/performance_test.py +0 -51
  8. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/.envrc +0 -0
  9. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/.github/pull_request_template.md +0 -0
  10. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/CLAUDE.md +0 -0
  11. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/CODEOWNERS +0 -0
  12. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/LICENSE +0 -0
  13. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/README.md +0 -0
  14. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/__init__.py +0 -0
  15. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/__main__.py +0 -0
  16. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/canvas/__init__.py +0 -0
  17. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/canvas/canvas_interface.py +0 -0
  18. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/canvas/classes.py +0 -0
  19. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/constants.py +0 -0
  20. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/contentast.py +0 -0
  21. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/logging.yaml +0 -0
  22. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/misc.py +0 -0
  23. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/mixins.py +0 -0
  24. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/performance.py +0 -0
  25. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/__init__.py +0 -0
  26. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/basic.py +0 -0
  27. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/__init__.py +0 -0
  28. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/languages.py +0 -0
  29. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/math_questions.py +0 -0
  30. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/memory_questions.py +0 -0
  31. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/ostep13_vsfs.py +0 -0
  32. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/persistence_questions.py +0 -0
  33. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst334/process.py +0 -0
  34. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/__init__.py +0 -0
  35. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/gradient_descent/__init__.py +0 -0
  36. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +0 -0
  37. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +0 -0
  38. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +0 -0
  39. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +0 -0
  40. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/math_and_data/__init__.py +0 -0
  41. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +0 -0
  42. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +0 -0
  43. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/neural-network-basics/__init__.py +0 -0
  44. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +0 -0
  45. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/tensorflow-intro/__init__.py +0 -0
  46. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +0 -0
  47. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/qrcode_generator.py +0 -0
  48. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/question.py +0 -0
  49. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/quiz.py +0 -0
  50. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/QuizGenerator/typst_utils.py +0 -0
  51. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/README.md +0 -0
  52. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/GRADING_GUIDE.md +0 -0
  53. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/LESSONS_LEARNED-adding_questions.md +0 -0
  54. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/PARAMETER_STANDARDS.md +0 -0
  55. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/README.md +0 -0
  56. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/WEB_UI_INTEGRATION.md +0 -0
  57. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/claude_todo.md +0 -0
  58. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/custom_questions.md +0 -0
  59. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/pypi_release_plan.md +0 -0
  60. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/documentation/typst_integration_strategy.md +0 -0
  61. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/all_classes.yaml +0 -0
  62. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/cst334.yaml +0 -0
  63. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/cst463.yaml +0 -0
  64. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/exam_generation.yaml +0 -0
  65. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/scratch.yaml +0 -0
  66. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/specific_generators/cst334.caching.yaml +0 -0
  67. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/example_files/specific_generators/cst334.lab-address_translation.yaml +0 -0
  68. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/examples/README.md +0 -0
  69. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/examples/web_ui_integration_example.py +0 -0
  70. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/grade_from_qr.py +0 -0
  71. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/scripts/README.md +0 -0
  72. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/scripts/generate_practice_yaml.sh +0 -0
  73. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/scripts/print.sh +0 -0
  74. {quizgenerator-0.1.0 → quizgenerator-0.1.1}/scripts/vendor_lms_interface.py +0 -0
@@ -9,4 +9,5 @@ output.csv
9
9
  imgs/
10
10
  debug.*
11
11
  .claude
12
- *.log
12
+ *.log
13
+ dist/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuizGenerator
3
- Version: 0.1.0
3
+ Version: 0.1.1
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
@@ -7,13 +7,10 @@ import subprocess
7
7
  import tempfile
8
8
  from pathlib import Path
9
9
  from dotenv import load_dotenv
10
- from QuizGenerator.canvas.canvas_interface import CanvasInterface, CanvasCourse
10
+ from QuizGenerator.canvas.canvas_interface import CanvasInterface
11
11
 
12
12
  from QuizGenerator.quiz import Quiz
13
13
 
14
- # Load environment variables from ~/.env
15
- load_dotenv(Path.home() / '.env')
16
-
17
14
  import logging
18
15
  log = logging.getLogger(__name__)
19
16
 
@@ -23,20 +20,27 @@ from QuizGenerator.performance import PerformanceTracker
23
20
  def parse_args():
24
21
  parser = argparse.ArgumentParser()
25
22
 
26
- parser.add_argument("--prod", action="store_true")
27
- parser.add_argument("--course_id", type=int)
23
+ parser.add_argument(
24
+ "--env",
25
+ default=os.path.join(Path.home(), '.env'),
26
+ help="Path to .env file specifying canvas details"
27
+ )
28
28
 
29
29
  parser.add_argument("--quiz_yaml", default=os.path.join(os.path.dirname(os.path.abspath(__file__)), "example_files/exam_generation.yaml"))
30
- parser.add_argument("--num_canvas", default=0, type=int)
31
- parser.add_argument("--num_pdfs", default=0, type=int)
32
30
  parser.add_argument("--seed", type=int, default=None,
33
31
  help="Random seed for quiz generation (default: None for random)")
34
- parser.add_argument("--typst", action="store_true",
35
- help="Use Typst instead of LaTeX for PDF generation")
36
- parser.add_argument("--typst-measurement", action="store_true",
37
- help="Use Typst to measure question heights for optimal bin-packing (requires Typst)")
32
+
33
+ # Canvas flags
34
+ parser.add_argument("--num_canvas", default=0, type=int, help="How many variations of each question to try to upload to canvas.")
35
+ parser.add_argument("--prod", action="store_true")
36
+ parser.add_argument("--course_id", type=int)
38
37
  parser.add_argument("--delete-assignment-group", action="store_true",
39
38
  help="Delete existing assignment group before uploading new quizzes")
39
+
40
+ # PDF Flags
41
+ parser.add_argument("--num_pdfs", default=0, type=int, help="How many PDF quizzes to create")
42
+ parser.add_argument("--typst", action="store_true",
43
+ help="Use Typst instead of LaTeX for PDF generation")
40
44
 
41
45
  subparsers = parser.add_subparsers(dest='command')
42
46
  test_parser = subparsers.add_parser("TEST")
@@ -54,111 +58,6 @@ def parse_args():
54
58
  def test():
55
59
  log.info("Running test...")
56
60
 
57
- # Load the CST463 quiz configuration to test vector questions
58
- quiz_yaml = os.path.join(os.path.dirname(os.path.abspath(__file__)), "example_files/cst463.yaml")
59
-
60
- # Generate a quiz
61
- quizzes = Quiz.from_yaml(quiz_yaml)
62
-
63
- # Find a vector question to test
64
- from QuizGenerator.question import QuestionRegistry
65
-
66
- print("="*60)
67
- print("CANVAS ANSWER DUPLICATION TEST")
68
- print("="*60)
69
-
70
- # Test multiple question types to find which ones use VECTOR variable kind
71
- question_types = ["VectorAddition", "VectorDotProduct", "VectorMagnitude", "DerivativeBasic", "DerivativeChain"]
72
-
73
- for question_type in question_types:
74
- print(f"\n" + "="*20 + f" TESTING {question_type} " + "="*20)
75
- question = QuestionRegistry.create(question_type, name=f"Test {question_type}", points_value=1)
76
-
77
- print(f"Question answers: {len(question.answers)} answer objects")
78
-
79
- # Show all individual answers and their Canvas representations
80
- for key, answer in question.answers.items():
81
- canvas_answers = answer.get_for_canvas()
82
- print(f"\nAnswer key '{key}':")
83
- print(f" Variable kind: {answer.variable_kind}")
84
- print(f" Value: {answer.value}")
85
- print(f" Canvas entries: {len(canvas_answers)}")
86
- for i, canvas_answer in enumerate(canvas_answers):
87
- print(f" {i+1}: '{canvas_answer['answer_text']}'")
88
-
89
- # Show duplicates if they exist
90
- texts = [ca['answer_text'] for ca in canvas_answers]
91
- unique_texts = set(texts)
92
- duplicates = len(texts) - len(unique_texts)
93
- if duplicates > 0:
94
- print(f" *** {duplicates} DUPLICATE ENTRIES FOUND ***")
95
- for text in sorted(unique_texts):
96
- count = texts.count(text)
97
- if count > 1:
98
- print(f" '{text}' appears {count} times")
99
-
100
- # Check for phantom blank_ids that aren't displayed
101
- print(f"\n" + "="*20 + " PHANTOM BLANK_ID CHECK " + "="*20)
102
-
103
- for question_type in ["DerivativeBasic", "DerivativeChain"]:
104
- print(f"\n--- {question_type} ---")
105
- question = QuestionRegistry.create(question_type, name=f"Test {question_type}", points_value=1)
106
-
107
- # Get the question body to see what blank_ids are actually displayed
108
- body = question.get_body()
109
- body_html = body.render("html")
110
-
111
- print(f" HTML body preview:")
112
- print(f" {body_html[:200]}...")
113
-
114
- # Extract blank_ids from the HTML using regex
115
- import re
116
- displayed_blank_ids = set(re.findall(r'name="([^"]*)"', body_html))
117
-
118
- # Get all blank_ids from the answers
119
- question_type_enum, canvas_answers = question.get_answers()
120
- all_blank_ids = set(answer['blank_id'] for answer in canvas_answers)
121
-
122
- print(f" Total answers in self.answers: {len(question.answers)}")
123
- print(f" Total Canvas answer entries: {len(canvas_answers)}")
124
- print(f" Unique blank_ids in Canvas answers: {len(all_blank_ids)}")
125
- print(f" Blank_ids displayed in HTML: {len(displayed_blank_ids)}")
126
-
127
- # Find phantom blank_ids
128
- phantom_blank_ids = all_blank_ids - displayed_blank_ids
129
- if phantom_blank_ids:
130
- print(f" *** PHANTOM BLANK_IDS FOUND: {phantom_blank_ids} ***")
131
- for phantom_id in phantom_blank_ids:
132
- phantom_answers = [a for a in canvas_answers if a['blank_id'] == phantom_id]
133
- print(f" '{phantom_id}': {len(phantom_answers)} entries not displayed")
134
- else:
135
- print(f" No phantom blank_ids found")
136
-
137
- # Show what blank_ids are actually displayed
138
- print(f" Displayed blank_ids: {sorted(displayed_blank_ids)}")
139
- print(f" All answer blank_ids: {sorted(all_blank_ids)}")
140
-
141
- # Now create a synthetic test to demonstrate the VECTOR bug
142
- print(f"\n" + "="*20 + " SYNTHETIC VECTOR TEST " + "="*20)
143
- from QuizGenerator.misc import Answer
144
-
145
- # Create a synthetic answer with VECTOR variable kind to demonstrate the bug
146
- vector_answer = Answer(
147
- key="test_vector",
148
- value=[1, 2, 3], # 3D vector
149
- variable_kind=Answer.VariableKind.VECTOR
150
- )
151
-
152
- canvas_answers = vector_answer.get_for_canvas()
153
- print(f"Synthetic VECTOR answer:")
154
- print(f" Value: {vector_answer.value}")
155
- print(f" Canvas entries: {len(canvas_answers)}")
156
- # Only show first few to save space
157
- for i, canvas_answer in enumerate(canvas_answers[:5]):
158
- print(f" {i+1}: '{canvas_answer['answer_text']}'")
159
- if len(canvas_answers) > 5:
160
- print(f" ... and {len(canvas_answers) - 5} more entries")
161
-
162
61
  print("\n" + "="*60)
163
62
  print("TEST COMPLETE")
164
63
  print("="*60)
@@ -245,6 +144,7 @@ def generate_typst(typst_text, remove_previous=False):
245
144
  if os.path.exists(tmp_typ.name):
246
145
  os.unlink(tmp_typ.name)
247
146
 
147
+
248
148
  def generate_quiz(
249
149
  path_to_quiz_yaml,
250
150
  num_pdfs=0,
@@ -337,6 +237,9 @@ def generate_quiz(
337
237
  def main():
338
238
 
339
239
  args = parse_args()
240
+
241
+ # Load environment variables
242
+ load_dotenv(args.env)
340
243
 
341
244
  if args.command == "TEST":
342
245
  test()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "QuizGenerator"
7
- version = "0.1.0"
7
+ version = "0.1.1"
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"}
@@ -158,15 +158,6 @@ wheels = [
158
158
  { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
159
159
  ]
160
160
 
161
- [[package]]
162
- name = "colorama"
163
- version = "0.4.6"
164
- source = { registry = "https://pypi.org/simple" }
165
- sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
166
- wheels = [
167
- { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
168
- ]
169
-
170
161
  [[package]]
171
162
  name = "contourpy"
172
163
  version = "1.3.3"
@@ -352,15 +343,6 @@ wheels = [
352
343
  { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" },
353
344
  ]
354
345
 
355
- [[package]]
356
- name = "fuzzywuzzy"
357
- version = "0.18.0"
358
- source = { registry = "https://pypi.org/simple" }
359
- sdist = { url = "https://files.pythonhosted.org/packages/11/4b/0a002eea91be6048a2b5d53c5f1b4dafd57ba2e36eea961d05086d7c28ce/fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", size = 28888, upload-time = "2020-02-13T21:06:27.054Z" }
360
- wheels = [
361
- { url = "https://files.pythonhosted.org/packages/43/ff/74f23998ad2f93b945c0309f825be92e04e0348e062026998b5eefef4c33/fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993", size = 18272, upload-time = "2020-02-13T21:06:25.209Z" },
362
- ]
363
-
364
346
  [[package]]
365
347
  name = "graphviz"
366
348
  version = "0.21"
@@ -691,53 +673,6 @@ wheels = [
691
673
  { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
692
674
  ]
693
675
 
694
- [[package]]
695
- name = "pandas"
696
- version = "2.3.3"
697
- source = { registry = "https://pypi.org/simple" }
698
- dependencies = [
699
- { name = "numpy" },
700
- { name = "python-dateutil" },
701
- { name = "pytz" },
702
- { name = "tzdata" },
703
- ]
704
- sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
705
- wheels = [
706
- { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" },
707
- { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" },
708
- { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" },
709
- { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" },
710
- { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" },
711
- { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" },
712
- { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" },
713
- { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" },
714
- { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" },
715
- { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" },
716
- { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" },
717
- { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" },
718
- { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" },
719
- { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" },
720
- { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" },
721
- { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" },
722
- { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" },
723
- { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" },
724
- { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" },
725
- { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" },
726
- { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" },
727
- { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" },
728
- { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" },
729
- { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" },
730
- { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" },
731
- { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" },
732
- { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" },
733
- { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" },
734
- { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" },
735
- { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" },
736
- { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" },
737
- { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" },
738
- { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
739
- ]
740
-
741
676
  [[package]]
742
677
  name = "pathvalidate"
743
678
  version = "3.3.1"
@@ -942,14 +877,11 @@ version = "0.1.0"
942
877
  source = { editable = "." }
943
878
  dependencies = [
944
879
  { name = "canvasapi" },
945
- { name = "colorama" },
946
880
  { name = "cryptography" },
947
- { name = "fuzzywuzzy" },
948
881
  { name = "graphviz" },
949
882
  { name = "jinja2" },
950
883
  { name = "markdown" },
951
884
  { name = "matplotlib" },
952
- { name = "pandas" },
953
885
  { name = "pylatex" },
954
886
  { name = "pypandoc" },
955
887
  { name = "pytablewriter" },
@@ -969,14 +901,11 @@ grading = [
969
901
  [package.metadata]
970
902
  requires-dist = [
971
903
  { name = "canvasapi", specifier = "==3.2.0" },
972
- { name = "colorama" },
973
904
  { name = "cryptography", specifier = ">=41.0.0" },
974
- { name = "fuzzywuzzy" },
975
905
  { name = "graphviz", specifier = ">=0.21" },
976
906
  { name = "jinja2", specifier = "==3.1.3" },
977
907
  { name = "markdown", specifier = ">=3.9" },
978
908
  { name = "matplotlib" },
979
- { name = "pandas" },
980
909
  { name = "pillow", marker = "extra == 'grading'", specifier = ">=10.0.0" },
981
910
  { name = "pylatex", specifier = ">=1.4.2" },
982
911
  { name = "pypandoc", specifier = "~=1.6.3" },
File without changes
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env python
2
- """
3
- Test script to exercise the performance instrumentation system without
4
- actually uploading to Canvas.
5
- """
6
-
7
- from QuizGenerator.quiz import Quiz
8
- from QuizGenerator.performance import PerformanceTracker
9
-
10
- def test_performance_instrumentation():
11
- print("Testing performance instrumentation...")
12
-
13
- # Clear any previous metrics
14
- PerformanceTracker.clear_metrics()
15
-
16
- # Load a quiz
17
- quizzes = Quiz.from_yaml("example_files/scratch.yaml")
18
- quiz = quizzes[0]
19
-
20
- # Generate some questions to exercise the timing code
21
- print("Generating questions...")
22
- for i in range(3):
23
- print(f" Generating question {i+1}/3")
24
- quiz_doc = quiz.get_quiz(rng_seed=i)
25
-
26
- # Render to HTML (to exercise AST rendering)
27
- for element in quiz_doc.elements:
28
- if hasattr(element, 'body'):
29
- element.body.render("html")
30
- if hasattr(element, 'explanation'):
31
- element.explanation.render("html")
32
-
33
- # Generate performance report
34
- print("\n" + "="*80)
35
- print("PERFORMANCE TEST REPORT")
36
- print("="*80)
37
-
38
- metrics = PerformanceTracker.get_metrics()
39
- print(f"Total metrics recorded: {len(metrics)}")
40
-
41
- if metrics:
42
- PerformanceTracker.report_summary(min_duration=0.001)
43
-
44
- print("\nDetailed breakdown:")
45
- for metric in metrics[:10]: # Show first 10 metrics
46
- print(f" {metric.operation}: {metric.duration:.4f}s ({metric.question_name}, {metric.question_type})")
47
- else:
48
- print("No metrics recorded. This suggests the instrumentation may not be working.")
49
-
50
- if __name__ == "__main__":
51
- test_performance_instrumentation()
File without changes
File without changes
File without changes
File without changes
File without changes