munchboka-edutools 0.1.13__py3-none-any.whl

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.

Potentially problematic release.


This version of munchboka-edutools might be problematic. Click here for more details.

Files changed (149) hide show
  1. munchboka_edutools/__init__.py +184 -0
  2. munchboka_edutools/_plotmath_shim.py +126 -0
  3. munchboka_edutools/_version.py +2 -0
  4. munchboka_edutools/directives/__init__.py +1 -0
  5. munchboka_edutools/directives/admonitions.py +389 -0
  6. munchboka_edutools/directives/cas_popup.py +272 -0
  7. munchboka_edutools/directives/clear.py +103 -0
  8. munchboka_edutools/directives/dialogue.py +137 -0
  9. munchboka_edutools/directives/escape_room.py +296 -0
  10. munchboka_edutools/directives/factor_tree.py +549 -0
  11. munchboka_edutools/directives/ggb.py +209 -0
  12. munchboka_edutools/directives/ggb_icon.py +105 -0
  13. munchboka_edutools/directives/ggb_popup.py +165 -0
  14. munchboka_edutools/directives/horner.py +324 -0
  15. munchboka_edutools/directives/interactive_code.py +75 -0
  16. munchboka_edutools/directives/jeopardy.py +252 -0
  17. munchboka_edutools/directives/multi_plot.py +1126 -0
  18. munchboka_edutools/directives/pair_puzzle.py +191 -0
  19. munchboka_edutools/directives/parsons.py +109 -0
  20. munchboka_edutools/directives/plot.py +3105 -0
  21. munchboka_edutools/directives/poly_icon.py +111 -0
  22. munchboka_edutools/directives/polydiv.py +344 -0
  23. munchboka_edutools/directives/popup.py +245 -0
  24. munchboka_edutools/directives/quiz.py +291 -0
  25. munchboka_edutools/directives/signchart.py +516 -0
  26. munchboka_edutools/directives/timed_quiz.py +436 -0
  27. munchboka_edutools/directives/turtle.py +157 -0
  28. munchboka_edutools/static/css/admonitions.css +2012 -0
  29. munchboka_edutools/static/css/cas_popup.css +242 -0
  30. munchboka_edutools/static/css/code_mirror_themes/github_dark_cm.css +112 -0
  31. munchboka_edutools/static/css/code_mirror_themes/github_dark_default_cm.css +40 -0
  32. munchboka_edutools/static/css/code_mirror_themes/github_dark_high_contrast_cm.css +141 -0
  33. munchboka_edutools/static/css/code_mirror_themes/github_light_cm.css +120 -0
  34. munchboka_edutools/static/css/code_mirror_themes/github_light_default_cm.css +108 -0
  35. munchboka_edutools/static/css/code_mirror_themes/one_dark_cm.css +121 -0
  36. munchboka_edutools/static/css/dialogue.css +92 -0
  37. munchboka_edutools/static/css/escapeRoom/escape-room.css +223 -0
  38. munchboka_edutools/static/css/figures.css +274 -0
  39. munchboka_edutools/static/css/general_style.css +74 -0
  40. munchboka_edutools/static/css/github-dark-high-contrast.css +141 -0
  41. munchboka_edutools/static/css/github-dark.css +112 -0
  42. munchboka_edutools/static/css/github-light.css +120 -0
  43. munchboka_edutools/static/css/interactive_code/style.css +575 -0
  44. munchboka_edutools/static/css/interactive_code.css +582 -0
  45. munchboka_edutools/static/css/jeopardy.css +529 -0
  46. munchboka_edutools/static/css/pairPuzzle/style.css +177 -0
  47. munchboka_edutools/static/css/parsons/parsonsPuzzle.css +331 -0
  48. munchboka_edutools/static/css/popup.css +115 -0
  49. munchboka_edutools/static/css/quiz.css +312 -0
  50. munchboka_edutools/static/css/timedQuiz.css +375 -0
  51. munchboka_edutools/static/icons/ggb/mode_evaluate.svg +1 -0
  52. munchboka_edutools/static/icons/ggb/mode_extremum.svg +1 -0
  53. munchboka_edutools/static/icons/ggb/mode_intersect.svg +1 -0
  54. munchboka_edutools/static/icons/ggb/mode_nsolve.svg +1 -0
  55. munchboka_edutools/static/icons/ggb/mode_numeric.svg +1 -0
  56. munchboka_edutools/static/icons/ggb/mode_point.svg +1 -0
  57. munchboka_edutools/static/icons/ggb/mode_solve.svg +1 -0
  58. munchboka_edutools/static/icons/misc/windows-logo.svg +1 -0
  59. munchboka_edutools/static/icons/outline/dark_mode/academic.svg +3 -0
  60. munchboka_edutools/static/icons/outline/dark_mode/backward.svg +3 -0
  61. munchboka_edutools/static/icons/outline/dark_mode/book.svg +3 -0
  62. munchboka_edutools/static/icons/outline/dark_mode/chat_bubble.svg +3 -0
  63. munchboka_edutools/static/icons/outline/dark_mode/check.svg +3 -0
  64. munchboka_edutools/static/icons/outline/dark_mode/cmd_line.svg +3 -0
  65. munchboka_edutools/static/icons/outline/dark_mode/file.svg +1 -0
  66. munchboka_edutools/static/icons/outline/dark_mode/fire.svg +4 -0
  67. munchboka_edutools/static/icons/outline/dark_mode/key.svg +3 -0
  68. munchboka_edutools/static/icons/outline/dark_mode/magnifying.svg +3 -0
  69. munchboka_edutools/static/icons/outline/dark_mode/pencil_square.svg +3 -0
  70. munchboka_edutools/static/icons/outline/dark_mode/play.svg +3 -0
  71. munchboka_edutools/static/icons/outline/dark_mode/question.svg +3 -0
  72. munchboka_edutools/static/icons/outline/dark_mode/square_check.svg +1 -0
  73. munchboka_edutools/static/icons/outline/dark_mode/stop.svg +3 -0
  74. munchboka_edutools/static/icons/outline/dark_mode/summary.svg +3 -0
  75. munchboka_edutools/static/icons/outline/dark_mode/undo.svg +3 -0
  76. munchboka_edutools/static/icons/outline/dark_mode/unlock.svg +3 -0
  77. munchboka_edutools/static/icons/outline/light_mode/academic.svg +3 -0
  78. munchboka_edutools/static/icons/outline/light_mode/backward.svg +3 -0
  79. munchboka_edutools/static/icons/outline/light_mode/book.svg +3 -0
  80. munchboka_edutools/static/icons/outline/light_mode/chat_bubble.svg +3 -0
  81. munchboka_edutools/static/icons/outline/light_mode/check.svg +3 -0
  82. munchboka_edutools/static/icons/outline/light_mode/cmd_line.svg +3 -0
  83. munchboka_edutools/static/icons/outline/light_mode/file.svg +1 -0
  84. munchboka_edutools/static/icons/outline/light_mode/fire.svg +4 -0
  85. munchboka_edutools/static/icons/outline/light_mode/key.svg +3 -0
  86. munchboka_edutools/static/icons/outline/light_mode/magnifying.svg +3 -0
  87. munchboka_edutools/static/icons/outline/light_mode/pencil_square.svg +3 -0
  88. munchboka_edutools/static/icons/outline/light_mode/play.svg +3 -0
  89. munchboka_edutools/static/icons/outline/light_mode/question.svg +3 -0
  90. munchboka_edutools/static/icons/outline/light_mode/square_check.svg +1 -0
  91. munchboka_edutools/static/icons/outline/light_mode/stop.svg +3 -0
  92. munchboka_edutools/static/icons/outline/light_mode/summary.svg +3 -0
  93. munchboka_edutools/static/icons/outline/light_mode/undo.svg +3 -0
  94. munchboka_edutools/static/icons/outline/light_mode/unlock.svg +3 -0
  95. munchboka_edutools/static/icons/polyicons/cubicdown.svg +3 -0
  96. munchboka_edutools/static/icons/polyicons/cubicup.svg +3 -0
  97. munchboka_edutools/static/icons/polyicons/frown.svg +3 -0
  98. munchboka_edutools/static/icons/polyicons/smile.svg +3 -0
  99. munchboka_edutools/static/icons/solid/dark_mode/academic.svg +5 -0
  100. munchboka_edutools/static/icons/solid/dark_mode/backward.svg +3 -0
  101. munchboka_edutools/static/icons/solid/dark_mode/book.svg +3 -0
  102. munchboka_edutools/static/icons/solid/dark_mode/brain.svg +1 -0
  103. munchboka_edutools/static/icons/solid/dark_mode/file.svg +1 -0
  104. munchboka_edutools/static/icons/solid/dark_mode/fire.svg +3 -0
  105. munchboka_edutools/static/icons/solid/dark_mode/key.svg +3 -0
  106. munchboka_edutools/static/icons/solid/dark_mode/pencil_square.svg +4 -0
  107. munchboka_edutools/static/icons/solid/dark_mode/play.svg +3 -0
  108. munchboka_edutools/static/icons/solid/dark_mode/python.svg +1 -0
  109. munchboka_edutools/static/icons/solid/dark_mode/scroll.svg +1 -0
  110. munchboka_edutools/static/icons/solid/dark_mode/stop.svg +3 -0
  111. munchboka_edutools/static/icons/solid/light_mode/academic.svg +5 -0
  112. munchboka_edutools/static/icons/solid/light_mode/backward.svg +3 -0
  113. munchboka_edutools/static/icons/solid/light_mode/book.svg +3 -0
  114. munchboka_edutools/static/icons/solid/light_mode/brain.svg +1 -0
  115. munchboka_edutools/static/icons/solid/light_mode/file.svg +1 -0
  116. munchboka_edutools/static/icons/solid/light_mode/fire.svg +3 -0
  117. munchboka_edutools/static/icons/solid/light_mode/key.svg +3 -0
  118. munchboka_edutools/static/icons/solid/light_mode/pencil_square.svg +4 -0
  119. munchboka_edutools/static/icons/solid/light_mode/play.svg +3 -0
  120. munchboka_edutools/static/icons/solid/light_mode/python.svg +1 -0
  121. munchboka_edutools/static/icons/solid/light_mode/scroll.svg +1 -0
  122. munchboka_edutools/static/icons/solid/light_mode/stop.svg +3 -0
  123. munchboka_edutools/static/icons/stickers/edit.svg +1 -0
  124. munchboka_edutools/static/icons/stickers/pencil_square.svg +3 -0
  125. munchboka_edutools/static/js/casThemeManager.js +99 -0
  126. munchboka_edutools/static/js/escapeRoom/escape-room.js +219 -0
  127. munchboka_edutools/static/js/geogebra-setup.js +6 -0
  128. munchboka_edutools/static/js/highlight-init.js +6 -0
  129. munchboka_edutools/static/js/interactiveCode/codeEditor.js +632 -0
  130. munchboka_edutools/static/js/interactiveCode/interactiveCodeSetup.js +348 -0
  131. munchboka_edutools/static/js/interactiveCode/pythonRunner.js +336 -0
  132. munchboka_edutools/static/js/interactiveCode/turtleCode.js +203 -0
  133. munchboka_edutools/static/js/interactiveCode/workerManager.js +353 -0
  134. munchboka_edutools/static/js/jeopardy.js +523 -0
  135. munchboka_edutools/static/js/pairPuzzle/draggableItem.js +64 -0
  136. munchboka_edutools/static/js/pairPuzzle/dropZone.js +119 -0
  137. munchboka_edutools/static/js/pairPuzzle/game.js +160 -0
  138. munchboka_edutools/static/js/parsons/parsonsPuzzle.js +641 -0
  139. munchboka_edutools/static/js/popup.js +85 -0
  140. munchboka_edutools/static/js/quiz.js +422 -0
  141. munchboka_edutools/static/js/skulpt/skulpt.js +35721 -0
  142. munchboka_edutools/static/js/timedQuiz/multipleChoiceQuestion.js +184 -0
  143. munchboka_edutools/static/js/timedQuiz/timedMultipleChoiceQuiz.js +244 -0
  144. munchboka_edutools/static/js/timedQuiz/utils.js +6 -0
  145. munchboka_edutools/static/js/utils.js +3 -0
  146. munchboka_edutools-0.1.13.dist-info/METADATA +108 -0
  147. munchboka_edutools-0.1.13.dist-info/RECORD +149 -0
  148. munchboka_edutools-0.1.13.dist-info/WHEEL +4 -0
  149. munchboka_edutools-0.1.13.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,348 @@
1
+ class InteractiveCodeSetup {
2
+ constructor(containerId, initialCode, preloadPackages = null) {
3
+ this.containerId = containerId;
4
+ this.initialCode = initialCode;
5
+ this.preloadPackages = preloadPackages;
6
+ console.log("InteractiveCodeSetup - Preload packages:", this.preloadPackages);
7
+ this.uniqueId = this.generateUUID();
8
+ console.log("Unique ID:", this.uniqueId);
9
+
10
+ // HTML element IDs
11
+ this.editorId = `code-editor-${this.uniqueId}`;
12
+ this.runButtonId = `run-button-${this.uniqueId}`;
13
+ this.resetButtonId = `reset-button-${this.uniqueId}`;
14
+ this.cancelButtonId = `cancel-button-${this.uniqueId}`;
15
+ this.outputId = `output-${this.uniqueId}`;
16
+ this.errorBoxId = `error-box-${this.uniqueId}`;
17
+
18
+ // Store instances of CodeEditor and PythonRunner
19
+ this.editorInstance = null;
20
+ this.runnerInstance = null;
21
+
22
+
23
+ // Initialize the editor setup
24
+ this.createEditorHTML();
25
+ this.setupInteractiveEditor();
26
+ }
27
+
28
+ generateUUID() {
29
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
30
+ const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
31
+ return v.toString(16);
32
+ });
33
+ }
34
+
35
+ createEditorHTML() {
36
+ const container = document.getElementById(this.containerId);
37
+ if (!container) {
38
+ console.error(`Container with ID ${this.containerId} not found.`);
39
+ return;
40
+ }
41
+
42
+ const runIcon = `
43
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
44
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z" />
45
+ </svg>
46
+ `;
47
+ const resetIcon = `
48
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
49
+ <path fill-rule="evenodd" d="M9.53 2.47a.75.75 0 0 1 0 1.06L4.81 8.25H15a6.75 6.75 0 0 1 0 13.5h-3a.75.75 0 0 1 0-1.5h3a5.25 5.25 0 1 0 0-10.5H4.81l4.72 4.72a.75.75 0 1 1-1.06 1.06l-6-6a.75.75 0 0 1 0-1.06l6-6a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" />
50
+ </svg>
51
+ `;
52
+
53
+ const cancelIcon = `
54
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
55
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 7.5A2.25 2.25 0 0 1 7.5 5.25h9a2.25 2.25 0 0 1 2.25 2.25v9a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-9Z" />
56
+ </svg>
57
+ `;
58
+
59
+ const html = `
60
+ <div>
61
+ <textarea id="${this.editorId}" name="code-${this.uniqueId}">${this.initialCode}</textarea>
62
+
63
+ <button id="${this.runButtonId}" class="button button-run">Kjør kode ${runIcon}</button>
64
+ <button id="${this.resetButtonId}" class="button button-reset">Reset kode ${resetIcon}</button>
65
+ <button id="${this.cancelButtonId}" class="button button-cancel">Avbryt kjøring ${cancelIcon}</button>
66
+ </div>
67
+ <div id="${this.errorBoxId}"></div>
68
+ <pre id="${this.outputId}" class="pythonoutput"></pre>
69
+ `;
70
+
71
+ container.innerHTML = html;
72
+ }
73
+
74
+ setupInteractiveEditor() {
75
+ this.editorInstance = new CodeEditor(this.editorId); // Initialize the CodeEditor
76
+ this.runnerInstance = new PythonRunner(this.outputId, this.errorBoxId, this.preloadPackages); // Initialize the PythonRunner
77
+
78
+ // Add event listeners for buttons
79
+ document.getElementById(this.runButtonId).addEventListener("click", () => this.runCode());
80
+ document.getElementById(this.resetButtonId).addEventListener("click", () => this.resetCode());
81
+ document.getElementById(this.cancelButtonId).addEventListener("click", () => this.cancelCodeExecution());
82
+ }
83
+
84
+
85
+
86
+ runCode() {
87
+ this.clearOutput();
88
+ this.runnerInstance.run(this.editorInstance); // Pass the editor instance to PythonRunner for execution
89
+ }
90
+
91
+ resetCode() {
92
+ this.clearOutput();
93
+ this.editorInstance.resetEditor(this.initialCode); // Reset the editor content to the initial code
94
+ }
95
+
96
+ cancelCodeExecution() {
97
+ if (this.runnerInstance.workerManager) {
98
+ this.runnerInstance.workerManager.restartWorker(); // Restart the worker to cancel code execution
99
+ }
100
+ }
101
+
102
+ clearOutput() {
103
+ document.getElementById(this.outputId).textContent = "";
104
+ document.getElementById(this.errorBoxId).textContent = "";
105
+ }
106
+ }
107
+
108
+
109
+ function makeInteractiveCode(containerId, initialCode, preloadPackages = null) {
110
+ return new InteractiveCodeSetup(containerId, initialCode, preloadPackages);
111
+ }
112
+
113
+
114
+ // predictionInteractiveCodeSetup.js
115
+
116
+ class PredictionInteractiveCodeSetup extends InteractiveCodeSetup {
117
+ constructor(containerId, initialCode) {
118
+ super(containerId, initialCode);
119
+
120
+ // Additional IDs for the prediction feature
121
+ this.predictionInputId = `prediction-input-${this.uniqueId}`;
122
+ this.lockPredictionButtonId = `lock-prediction-button-${this.uniqueId}`;
123
+ this.predictionDisplayId = `prediction-display-${this.uniqueId}`;
124
+ this.predictionOutputId = `prediction-output-${this.uniqueId}`;
125
+ this.predictionOutputContainerId = `prediction-output-container-${this.uniqueId}`;
126
+ this.predictionContainerId = `prediction-container-${this.uniqueId}`;
127
+ // this.initialCode = initialCode;
128
+
129
+ // Flag to track whether the prediction has been displayed
130
+ this.predictionDisplayed = false;
131
+
132
+ // Modify the HTML to include prediction elements
133
+ this.addPredictionHTML();
134
+
135
+ // Set up the prediction feature
136
+ this.setupPredictionFeature();
137
+ }
138
+
139
+ addPredictionHTML() {
140
+ const container = document.getElementById(this.containerId);
141
+ if (!container) {
142
+ console.error(`Container with ID ${this.containerId} not found.`);
143
+ return;
144
+ }
145
+
146
+ // Build the prediction input HTML
147
+ const predictionHtml = `
148
+ <div id="${this.predictionContainerId}" class="prediction-container">
149
+ <textarea id="${this.predictionInputId}" rows="3" placeholder="Skriv inn svaret ditt her! \n \nTrykk på Enter (&#9166;) for en ny linje."></textarea>
150
+ <button id="${this.lockPredictionButtonId}" class="button button-run">Sjekk svaret!</button>
151
+ </div>
152
+ `;
153
+
154
+ // Insert the prediction input after the existing editor HTML
155
+ container.insertAdjacentHTML('beforeend', predictionHtml);
156
+
157
+ // Create the prediction-output container
158
+ const predictionOutputContainer = document.createElement('div');
159
+ predictionOutputContainer.id = this.predictionOutputContainerId;
160
+ predictionOutputContainer.className = 'prediction-output-container';
161
+ predictionOutputContainer.style.display = 'none';
162
+
163
+ // Create the prediction display
164
+ const predictionDisplay = document.createElement('div');
165
+ predictionDisplay.className = 'prediction-display';
166
+ predictionDisplay.innerHTML = `
167
+ <h3>Ditt svar:</h3>
168
+ <pre id="${this.predictionDisplayId}"></pre>
169
+ `;
170
+
171
+ // Create the output display for prediction phase
172
+ const outputDisplay = document.createElement('div');
173
+ outputDisplay.className = 'output-display';
174
+ outputDisplay.innerHTML = `
175
+ <h3>Faktisk utskrift:</h3>
176
+ <pre id="${this.predictionOutputId}" class="pythonoutput"></pre>
177
+ `;
178
+
179
+ // Append the displays to the prediction-output container
180
+ predictionOutputContainer.appendChild(predictionDisplay);
181
+ predictionOutputContainer.appendChild(outputDisplay);
182
+
183
+ // Append the prediction-output container after the error box
184
+ const errorBoxElement = document.getElementById(this.errorBoxId);
185
+ if (errorBoxElement) {
186
+ errorBoxElement.insertAdjacentElement('afterend', predictionOutputContainer);
187
+ } else {
188
+ console.error(`Error box element with ID ${this.errorBoxId} not found.`);
189
+ container.appendChild(predictionOutputContainer);
190
+ }
191
+
192
+ // Save a reference to the original output element
193
+ this.originalOutputElement = document.getElementById(this.outputId);
194
+ }
195
+
196
+
197
+
198
+ setupPredictionFeature() {
199
+ // Make the code editor read-only initially
200
+ // Use setTimeout to ensure the editor is fully initialized
201
+ const setupReadOnly = () => {
202
+ // Check if editor instance exists and has been properly initialized
203
+ if (this.editorInstance && this.editorInstance.editor) {
204
+ console.log("Setting editor to read-only");
205
+ this.editorInstance.editor.setOption('readOnly', true);
206
+
207
+ // Hide the buttons now that we know the editor is ready
208
+ document.getElementById(this.runButtonId).style.display = 'none';
209
+ document.getElementById(this.resetButtonId).style.display = 'none';
210
+ document.getElementById(this.cancelButtonId).style.display = 'none';
211
+ } else {
212
+ console.log("Editor not ready yet, retrying in 100ms...");
213
+ // Retry after a short delay
214
+ setTimeout(setupReadOnly, 100);
215
+ }
216
+ };
217
+
218
+ // Start the setup process
219
+ setupReadOnly();
220
+
221
+
222
+ // this.editorInstance.editor.setOption('readOnly', true);
223
+
224
+ // // Hide the run/reset/cancel buttons until the prediction is made
225
+ // document.getElementById(this.runButtonId).style.display = 'none';
226
+ // document.getElementById(this.resetButtonId).style.display = 'none';
227
+ // document.getElementById(this.cancelButtonId).style.display = 'none';
228
+
229
+ // Add event listener for the lock prediction button
230
+ document.getElementById(this.lockPredictionButtonId).addEventListener("click", () => this.lockPrediction());
231
+ }
232
+
233
+ lockPrediction() {
234
+ const prediction = document.getElementById(this.predictionInputId).value;
235
+
236
+ // Store the prediction
237
+ this.prediction = prediction;
238
+
239
+ // Hide the prediction input area
240
+ const predictionContainer = document.getElementById(this.predictionContainerId);
241
+ if (predictionContainer) {
242
+ predictionContainer.style.display = 'none';
243
+ }
244
+
245
+ // Make the code editor editable
246
+ this.editorInstance.editor.setOption('readOnly', false);
247
+
248
+ // Show the run/reset/cancel buttons
249
+ document.getElementById(this.runButtonId).style.display = 'inline-block';
250
+ document.getElementById(this.resetButtonId).style.display = 'inline-block';
251
+ document.getElementById(this.cancelButtonId).style.display = 'inline-block';
252
+
253
+ // Automatically run the code
254
+ this.runCode();
255
+ }
256
+
257
+ runCode() {
258
+ if (this.predictionDisplayed) {
259
+ // Replace with a new InteractiveCodeSetup instance
260
+ this.initialCode = this.editorInstance.getValue();
261
+ this.replaceWithInteractiveCodeSetup();
262
+
263
+ } else {
264
+ // First run after prediction is locked in
265
+ this.clearOutput();
266
+ this.runnerInstance.run(this.editorInstance, this.predictionOutputId); // Pass the new output ID
267
+
268
+ // Display the prediction and output side by side
269
+ this.displayPredictionAndOutput();
270
+
271
+ // Set flag to indicate prediction has been displayed
272
+ this.predictionDisplayed = true;
273
+ }
274
+ }
275
+
276
+ resetCode() {
277
+ if (this.predictionDisplayed) {
278
+ // Replace with a new InteractiveCodeSetup instance
279
+ this.replaceWithInteractiveCodeSetup();
280
+ } else {
281
+ // First reset (unlikely, but handle just in case)
282
+ this.clearOutput();
283
+ this.editorInstance.resetEditor(this.initialCode);
284
+ }
285
+ }
286
+
287
+ cancelCodeExecution() {
288
+ if (this.predictionDisplayed) {
289
+ // Replace with a new InteractiveCodeSetup instance
290
+ this.replaceWithInteractiveCodeSetup();
291
+ } else {
292
+ // First cancel after prediction is locked in
293
+ if (this.runnerInstance.workerManager) {
294
+ this.runnerInstance.workerManager.restartWorker();
295
+ }
296
+ }
297
+ }
298
+
299
+ replaceWithInteractiveCodeSetup() {
300
+ // Get the container element
301
+ const container = document.getElementById(this.containerId);
302
+ if (container) {
303
+ // Clear the container's content
304
+ container.innerHTML = '';
305
+
306
+ // Create a new instance of InteractiveCodeSetup
307
+ new InteractiveCodeSetup(this.containerId, this.initialCode);
308
+ } else {
309
+ console.error(`Container with ID ${this.containerId} not found.`);
310
+ }
311
+ }
312
+
313
+ displayPredictionAndOutput() {
314
+ // Display the user's prediction
315
+ document.getElementById(this.predictionDisplayId).textContent = this.prediction;
316
+
317
+ // Hide the original output element
318
+ if (this.originalOutputElement) {
319
+ this.originalOutputElement.style.display = 'none';
320
+ }
321
+
322
+ // Show the prediction-output container
323
+ document.getElementById(this.predictionOutputContainerId).style.display = 'flex';
324
+ }
325
+
326
+ clearOutput() {
327
+ if (this.predictionDisplayed) {
328
+ const outputElement = document.getElementById(this.predictionOutputId);
329
+ if (outputElement) {
330
+ outputElement.textContent = "";
331
+ }
332
+ } else {
333
+ const outputElement = document.getElementById(this.outputId);
334
+ if (outputElement) {
335
+ outputElement.textContent = "";
336
+ }
337
+ }
338
+ const errorBoxElement = document.getElementById(this.errorBoxId);
339
+ if (errorBoxElement) {
340
+ errorBoxElement.textContent = "";
341
+ }
342
+ }
343
+ }
344
+
345
+ // Function to initialize the prediction code editor
346
+ function makePredictionInteractiveCode(containerId, initialCode) {
347
+ return new PredictionInteractiveCodeSetup(containerId, initialCode);
348
+ }
@@ -0,0 +1,336 @@
1
+ // pythonRunner.js
2
+
3
+ class PythonRunner {
4
+ constructor(outputId, errorBoxId, preloadPackages = ['casify']) {
5
+ this.outputId = outputId; // ID of the HTML element where output will be displayed
6
+ this.errorBoxId = errorBoxId; // ID of the HTML element for displaying errors
7
+ this.workerManager = WorkerManager.getInstance(preloadPackages);
8
+ this.preloadPackages = preloadPackages;
9
+ }
10
+
11
+ /**
12
+ * Prepares and runs the code provided in the editor.
13
+ * @param {Object} editor - The CodeMirror editor instance containing the code.
14
+ */
15
+ async run(editor, outputId = null) {
16
+ // Wait until worker is ready and preload packages are loaded
17
+ try {
18
+ await this.workerManager.workerReadyPromise;
19
+ } catch (error) {
20
+ console.error("Worker failed to initialize:", error);
21
+ this.handleErrorMessage("Failed to initialize Python environment.");
22
+ return;
23
+ }
24
+
25
+ this.editorInstance = editor;
26
+ this.editorInstance.clearLineHighlights();
27
+ let code = editor.getValue();
28
+ this.currentCode = code;
29
+
30
+ if (outputId) {
31
+ this.outputId = outputId;
32
+ }
33
+
34
+ // Handle input statements and modify the code accordingly
35
+ const inputStatements = this.findInputStatements(this.currentCode);
36
+ if (inputStatements.length > 0) {
37
+ const userValues = await this.getUserInputs(inputStatements);
38
+ this.currentCode = this.replaceInputStatements(this.currentCode, userValues);
39
+ console.log("Modified code:", this.currentCode);
40
+ }
41
+
42
+ // Extract and load necessary packages
43
+ const packages = this.extractPackageNames(this.currentCode);
44
+
45
+ if (!packages.includes('matplotlib')) {
46
+ packages.push('matplotlib');
47
+ }
48
+
49
+ console.log("Packages to load:", packages);
50
+ if (packages.length > 0) {
51
+ try {
52
+ await this.workerManager.loadPackages(packages);
53
+ } catch (error) {
54
+ // Handle package load error
55
+ console.error("Failed to load packages:", error);
56
+ this.handleErrorMessage(error.message);
57
+ return;
58
+ }
59
+ }
60
+
61
+
62
+ const callback = (data) => {
63
+ if (data.type === 'stdout') {
64
+ this.handleWorkerMessage(data);
65
+ } else if (data.type === 'stderr') {
66
+ this.handleErrorMessage(data.msg); // Displays the error
67
+ } else if (data.type === 'plot') {
68
+ const outputElement = document.getElementById(this.outputId);
69
+ if (!outputElement) {
70
+ console.error("Output element not found:", this.outputId);
71
+ return;
72
+ }
73
+
74
+ const img = document.createElement('img');
75
+ img.src = 'data:image/png;base64,' + data.data;
76
+ img.style.width = '100%'; // Or any other appropriate styling
77
+ img.style.height = 'auto'; // Or any other appropriate styling
78
+ img.style.maxHeight = '500px'; // Example: set a max width
79
+
80
+ outputElement.appendChild(img);
81
+ this.scrollToBottom(outputElement); // Scroll to show new plot
82
+ } else if (data.type === 'executionComplete') {
83
+ console.log("Code execution complete for messageId:", data.messageId);
84
+ }
85
+ };
86
+
87
+
88
+ this.workerManager.runCode(this.currentCode, callback);
89
+ }
90
+
91
+
92
+ /**
93
+ * Handles incoming messages from the WorkerManager.
94
+ * @param {Object} data - The parsed message data from the worker.
95
+ */
96
+ handleWorkerMessage(data) {
97
+ const { type, msg } = data;
98
+ console.log("Message from worker:", data);
99
+ const outputElement = document.getElementById(this.outputId);
100
+
101
+ if (!outputElement) {
102
+ console.error("Output element not found:", this.outputId);
103
+ return;
104
+ }
105
+
106
+ if (type === 'stdout') {
107
+ // Replace "&" with "∧", "oo" with "∞", and "|" with "∨"
108
+ let formattedMsg = msg
109
+ .replace(/And\(([^)]+)\)/g, (match, p1) => {
110
+ // Split conditions by comma, trim spaces, and wrap each in parentheses
111
+ const conditions = p1.split(',').map(cond => cond.trim());
112
+ return conditions.map(cond => `(${cond})`).join(' ∧ ');
113
+ })
114
+ // .replace(/&/g, '∧')
115
+ .replace(/oo/g, '∞')
116
+ .replace(/\|/g, '∨');
117
+ outputElement.innerHTML += this.formatErrorMessage(formattedMsg);
118
+ this.highlightLine(this.editorInstance, data.msg);
119
+ this.scrollToBottom(outputElement);
120
+
121
+ } else if (type === 'stderr') {
122
+ console.log("Error message:", msg);
123
+ this.handleErrorMessage(msg);
124
+ }
125
+ }
126
+
127
+ scrollToBottom(element) {
128
+ element.scrollTop = element.scrollHeight;
129
+ }
130
+
131
+ /**
132
+ * Handles error messages from the worker.
133
+ * @param {string} msg - The error message.
134
+ */
135
+ handleErrorMessage(msg) {
136
+ const errorElement = document.getElementById(this.errorBoxId);
137
+ if (errorElement) {
138
+ errorElement.innerHTML = this.formatErrorMessage(msg);
139
+ }
140
+ this.highlightLine(this.editorInstance, msg);
141
+
142
+ }
143
+
144
+ /**
145
+ * Formats output messages for display.
146
+ * @param {string} msg - The message to be formatted.
147
+ * @returns {string} - The formatted message.
148
+ */
149
+ formatOutput(msg) {
150
+ return msg;
151
+ }
152
+
153
+
154
+ highlightLine(editor, msg) {
155
+ const linePattern = /File "<exec>", line (\d+)/;
156
+ const match = linePattern.exec(msg);
157
+ if (match) {
158
+ const lineNumber = parseInt(match[1]) - 1;
159
+ console.log("Highlighting line:", lineNumber);
160
+ editor.highlightLine(lineNumber);
161
+ console.log("Highlighting error at line:", lineNumber);
162
+ }
163
+ }
164
+
165
+ formatErrorMessage(errorMsg) {
166
+ let formattedMessage = errorMsg;
167
+ let content = '';
168
+ let title = '';
169
+ let knownError = false;
170
+
171
+
172
+ // Highlight the line number in the pattern 'File "<exec>", line <number>'
173
+ const fileLinePattern = /File "<exec>", line (\d+)/g;
174
+ formattedMessage = formattedMessage.replace(fileLinePattern, (match, p1) => {
175
+ return match.replace(`line ${p1}`, `<span class="error-line">line ${p1}</span>`);
176
+ });
177
+
178
+
179
+ // Highlight the error type
180
+ const errorTypeMatch = errorMsg.match(/(\w+Error):/);
181
+ if (errorTypeMatch) {
182
+ console.log("Error type match:", errorTypeMatch[1]);
183
+ formattedMessage = formattedMessage.replace(errorTypeMatch[1], `<span class="error-type">${errorTypeMatch[1]}</span>`);
184
+
185
+ }
186
+
187
+
188
+ return formattedMessage;
189
+ }
190
+
191
+
192
+
193
+ /**
194
+ * Extracts package names from the code based on import statements.
195
+ * @param {string} code - The Python code to be analyzed.
196
+ * @returns {Array<string>} - An array of package names.
197
+ */
198
+ extractPackageNames(code) {
199
+ // Matches "import <package> as <alias>" and "import <package>"
200
+ const importRegex = /^\s*import\s+([^;\s]+)\s*/gm;
201
+
202
+ // Matches "from <package> import <something>"
203
+ const fromImportRegex = /^\s*from\s+([^;\s]+)\s+import/gm;
204
+
205
+ const packages = new Set();
206
+ const standardLibs = new Set([
207
+ 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64',
208
+ 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk',
209
+ 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser',
210
+ 'contextlib', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal',
211
+ 'difflib', 'dis', 'distutils', 'doctest', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler',
212
+ 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fractions', 'ftplib', 'functools', 'gc', 'getopt',
213
+ 'getpass', 'gettext', 'glob', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr',
214
+ 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale',
215
+ 'logging', 'lzma', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt',
216
+ 'multiprocessing', 'netrc', 'nntplib', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib',
217
+ 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'pprint', 'profile',
218
+ 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib',
219
+ 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal',
220
+ 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'ssl', 'stat', 'statistics',
221
+ 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'tabnanny',
222
+ 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token',
223
+ 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata',
224
+ 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound',
225
+ 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib'
226
+ ]);
227
+
228
+ let match;
229
+
230
+ // Process "import" statements
231
+ while ((match = importRegex.exec(code)) !== null) {
232
+ const packageName = match[1].split('.')[0];
233
+ if (!standardLibs.has(packageName)) {
234
+ packages.add(packageName);
235
+ }
236
+ }
237
+
238
+ // Process "from ... import" statements
239
+ while ((match = fromImportRegex.exec(code)) !== null) {
240
+ const packageName = match[1].split('.')[0];
241
+ if (!standardLibs.has(packageName)) {
242
+ packages.add(packageName);
243
+ }
244
+ }
245
+
246
+ console.log("Packages to load:", Array.from(packages));
247
+
248
+ return Array.from(packages);
249
+ }
250
+
251
+ /**
252
+ * Finds input statements in the code.
253
+ * @param {string} code - The Python code to be analyzed.
254
+ * @returns {Array<Object>} - An array of input statements found.
255
+ */
256
+ findInputStatements(code) {
257
+ const inputRegex = /(\w+)\s*=\s*(int|float|eval)?\(?input\(["'](.*?)["']\)\)?/g;
258
+ let match;
259
+ let inputs = [];
260
+
261
+ while ((match = inputRegex.exec(code)) !== null) {
262
+ inputs.push({
263
+ variable: match[1],
264
+ promptText: match[3]
265
+ });
266
+ }
267
+
268
+ return inputs;
269
+ }
270
+
271
+ /**
272
+ * Prompts the user for input when input statements are found in the code.
273
+ * @param {Array<Object>} inputs - An array of input statements.
274
+ * @returns {Object} - A dictionary of user inputs mapped to variable names.
275
+ */
276
+ async getUserInputs(inputs) {
277
+ let userValues = {};
278
+
279
+ for (let input of inputs) {
280
+ let promptText = input.promptText.replace(/['"]+/g, '');
281
+ let userValue = await this.promptUser(promptText);
282
+ userValues[input.variable] = userValue;
283
+ }
284
+
285
+ return userValues;
286
+ }
287
+
288
+ /**
289
+ * Prompts the user for input.
290
+ * @param {string} promptText - The text to display in the prompt.
291
+ * @returns {Promise<string>} - A promise that resolves with the user input.
292
+ */
293
+ promptUser(promptText) {
294
+ return new Promise((resolve) => {
295
+ let userInput = prompt(promptText);
296
+ resolve(userInput);
297
+ });
298
+ }
299
+
300
+ /**
301
+ * Replaces input statements in the code with user-provided values.
302
+ * @param {string} code - The Python code.
303
+ * @param {Object} userValues - A dictionary of user-provided values.
304
+ * @returns {string} - The modified code with input statements replaced.
305
+ */
306
+
307
+ replaceInputStatements(code, userValues) {
308
+ let codeLines = code.split('\n');
309
+
310
+ codeLines = codeLines.map(line => {
311
+ for (let variable in userValues) {
312
+ console.log("Variable: ", variable);
313
+ // Adjust the regex to handle Unicode characters in variable names
314
+ const inputRegex = new RegExp(`\\b${variable}\\b\\s*=\\s*(float|int|eval)?\\(?input\\(.*?\\)\\)?`, 'gu');
315
+
316
+ if (inputRegex.test(line)) {
317
+ let userValue = userValues[variable];
318
+
319
+ // Detect the type of the user input
320
+ if (!isNaN(userValue)) {
321
+ // Check if the input is a number (int or float)
322
+ //userValue = Number(userValue); // Convert the string to a number
323
+ } else if (typeof userValue === 'string') {
324
+ // Check if the input is a string and doesn't contain only numbers
325
+ userValue = `"${userValue}"`; // Add quotes around string inputs
326
+ }
327
+
328
+ line = line.replace(inputRegex, `${variable} = ${userValue}`);
329
+ }
330
+ }
331
+ return line;
332
+ });
333
+
334
+ return codeLines.join('\n');
335
+ }
336
+ }