otter-service-stdalone 1.1.8__py3-none-any.whl → 1.1.10__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.
- otter_service_stdalone/__init__.py +1 -1
- otter_service_stdalone/app.py +34 -32
- otter_service_stdalone/scripts/web_socket.js +4 -3
- otter_service_stdalone/util.py +36 -0
- {otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/METADATA +1 -1
- {otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/RECORD +9 -8
- {otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/WHEEL +0 -0
- {otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/entry_points.txt +0 -0
- {otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.1.
|
1
|
+
__version__ = "1.1.10"
|
otter_service_stdalone/app.py
CHANGED
@@ -11,6 +11,8 @@ from otter_service_stdalone import grade_notebooks
|
|
11
11
|
from zipfile import ZipFile, ZIP_DEFLATED
|
12
12
|
from multiprocessing import Queue
|
13
13
|
|
14
|
+
from .util import otter_version_correct
|
15
|
+
|
14
16
|
|
15
17
|
__UPLOADS__ = "/tmp/uploads"
|
16
18
|
log_debug = f'{os.environ.get("ENVIRONMENT")}-debug'
|
@@ -35,7 +37,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
|
35
37
|
if user_id not in session_callbacks:
|
36
38
|
session_callbacks[user_id] = tornado.ioloop.PeriodicCallback(lambda: self.send_results(user_id), 1000)
|
37
39
|
session_callbacks[user_id].start()
|
38
|
-
|
40
|
+
|
39
41
|
def on_message(self, message):
|
40
42
|
pass # No action needed on incoming message
|
41
43
|
|
@@ -44,7 +46,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
|
44
46
|
"""
|
45
47
|
if self.get_secure_cookie("user"):
|
46
48
|
user_id = self.get_secure_cookie("user").decode('utf-8')
|
47
|
-
if user_id in session_callbacks and session_callbacks[user_id]
|
49
|
+
if user_id in session_callbacks and session_callbacks[user_id]:
|
48
50
|
session_callbacks[user_id].stop()
|
49
51
|
session_callbacks.pop(user_id)
|
50
52
|
|
@@ -62,7 +64,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
|
62
64
|
if not q.empty():
|
63
65
|
while not q.empty():
|
64
66
|
user_messages_dict[result_id].append(q.get())
|
65
|
-
|
67
|
+
self.write_message({"messages": user_messages_dict})
|
66
68
|
except tornado.websocket.WebSocketClosedError:
|
67
69
|
log.write_logs("ws-error", "Web Socket Close Error", "", "", log_error)
|
68
70
|
except Exception:
|
@@ -183,16 +185,16 @@ class Download(BaseHandler):
|
|
183
185
|
m = "Download: Directory for Code Not existing"
|
184
186
|
log.write_logs(download_code, m, f"{download_code}", "debug", log_debug)
|
185
187
|
msg = "The download code appears to not be correct or expired "
|
186
|
-
msg += f"- results are deleted regularly: {download_code}."
|
188
|
+
msg += f"- results are deleted regularly: {download_code}. "
|
187
189
|
msg += "Please check the code or upload your notebooks "
|
188
190
|
msg += "and autograder.zip for grading again."
|
189
|
-
self.render("index.html",
|
191
|
+
self.render("index.html", download_message=msg)
|
190
192
|
elif not os.path.exists(f"{directory}/final_grades.csv"):
|
191
193
|
m = "Download: Results Not Ready"
|
192
194
|
log.write_logs(download_code, m, f"{download_code}", "debug", log_debug)
|
193
195
|
msg = "The results of your download are not ready yet. "
|
194
196
|
msg += "Please check back."
|
195
|
-
self.render("index.html",
|
197
|
+
self.render("index.html", download_message=msg, dcode=download_code)
|
196
198
|
else:
|
197
199
|
m = "Download Success: Creating results.zip"
|
198
200
|
log.write_logs(download_code, m, "", "debug", log_debug)
|
@@ -249,7 +251,7 @@ class Upload(BaseHandler):
|
|
249
251
|
results_path = str(uuid.uuid4())
|
250
252
|
autograder = self.request.files['autograder'][0] if "autograder" in files else None
|
251
253
|
notebooks = self.request.files['notebooks'][0] if "notebooks" in files else None
|
252
|
-
log.write_logs(results_path, "Step 1: Upload accepted", "", "debug", log_debug)
|
254
|
+
log.write_logs(results_path, "Step 1: Upload accepted", "", "debug", log_debug)
|
253
255
|
if autograder is not None and notebooks is not None:
|
254
256
|
notebooks_fname = notebooks['filename']
|
255
257
|
notebooks_extn = os.path.splitext(notebooks_fname)[1]
|
@@ -264,25 +266,36 @@ class Upload(BaseHandler):
|
|
264
266
|
if not os.path.exists(__UPLOADS__):
|
265
267
|
os.mkdir(__UPLOADS__)
|
266
268
|
auto_p = f"{__UPLOADS__}/{autograder_name}"
|
269
|
+
|
267
270
|
notebooks_path = f"{__UPLOADS__}/{notebooks_name}"
|
268
271
|
m = "Step 2a: Uploaded File Names Determined"
|
269
272
|
log.write_logs(results_path, m, f"notebooks path: {notebooks_path}", "debug", log_debug)
|
270
273
|
fh = open(auto_p, 'wb')
|
271
274
|
fh.write(autograder['body'])
|
272
|
-
|
273
|
-
fh = open(notebooks_path, 'wb')
|
274
|
-
fh.write(notebooks['body'])
|
275
|
-
m = "Step 3: Uploaded Files Written to Disk"
|
275
|
+
m = "Step 3A: Uploaded autograder.zip files written to disk - now checking otter version"
|
276
276
|
log.write_logs(results_path, m, f"Results Code: {results_path}", "debug", log_debug)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
277
|
+
if not otter_version_correct(auto_p):
|
278
|
+
m = "Step 3A-1: autograder.zip is wrong version of otter-grader; must be > 5.5.0"
|
279
|
+
log.write_logs(results_path, m, f"Results Code: {results_path}", "debug", log_debug)
|
280
|
+
m = "You need to make sure the autograder.zip uses otter-grader version >5.5.0. BUT you do not need "
|
281
|
+
m += "to re-generate your autograder.zip; un-archive autograder.zip and change the version in either "
|
282
|
+
m += "requirements.txt or environmental.yaml depending on the version of otter-grader the autograder.zip "
|
283
|
+
m += "is created with."
|
284
|
+
self.render("index.html", message=m)
|
285
|
+
else:
|
286
|
+
fh = open(notebooks_path, 'wb')
|
287
|
+
fh.write(notebooks['body'])
|
288
|
+
m = "Step 3B: Uploaded Notebook File Written to Disk"
|
289
|
+
log.write_logs(results_path, m, f"Results Code: {results_path}", "debug", log_debug)
|
290
|
+
m = "Please save this code; it appears in the \"Notebook Grading Progress\" section below. You can "
|
291
|
+
m += f"retrieve your files by submitting this code in the \"Results\" section to the right: {results_path}"
|
292
|
+
self.render("index.html", message=m)
|
293
|
+
try:
|
294
|
+
session_queues[user_id][results_path] = Queue()
|
295
|
+
session_messages[user_id][results_path] = []
|
296
|
+
await g.grade(auto_p, notebooks_path, results_path, session_queues[user_id].get(results_path))
|
297
|
+
except Exception as e:
|
298
|
+
log.write_logs(results_path, "Grading Problem", str(e), "error", log_error)
|
286
299
|
else:
|
287
300
|
m = "Step 2b: Uploaded Files not given"
|
288
301
|
log.write_logs(results_path, m, "", "debug", log_debug)
|
@@ -294,19 +307,8 @@ class RemoveProgressHandler(BaseHandler):
|
|
294
307
|
"""This handles requests to remove progress on a specific submission
|
295
308
|
|
296
309
|
Args:
|
297
|
-
tornado (
|
310
|
+
tornado (BaseHandler): The request handler
|
298
311
|
"""
|
299
|
-
def set_default_headers(self):
|
300
|
-
"""Set CORS headers to allow cross-origin requests."""
|
301
|
-
self.set_header("Access-Control-Allow-Origin", "*") # Allow requests from any domain
|
302
|
-
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
|
303
|
-
self.set_header("Access-Control-Allow-Methods", "DELETE, GET, POST, OPTIONS")
|
304
|
-
|
305
|
-
def options(self, *args):
|
306
|
-
"""Respond to OPTIONS requests for preflight in CORS."""
|
307
|
-
self.set_status(204)
|
308
|
-
self.finish()
|
309
|
-
|
310
312
|
@tornado.web.authenticated
|
311
313
|
def get(self):
|
312
314
|
# this just redirects to login and displays main page
|
@@ -115,6 +115,9 @@ function _setUpNewSubmission(submission_key, index, messages){
|
|
115
115
|
.then(response => {
|
116
116
|
if (response.ok) {
|
117
117
|
newMessage.remove();
|
118
|
+
if(document.querySelector('div[id="messages"]').childElementCount == 1){
|
119
|
+
document.getElementById("none-msg").style.display = "block";
|
120
|
+
}
|
118
121
|
} else {
|
119
122
|
console.error(`Failed to remove item ${submission_key}`);
|
120
123
|
}
|
@@ -122,6 +125,7 @@ function _setUpNewSubmission(submission_key, index, messages){
|
|
122
125
|
.catch(error => {
|
123
126
|
console.error('Failed to remove item:', error);
|
124
127
|
});
|
128
|
+
event.stopPropagation();
|
125
129
|
|
126
130
|
});
|
127
131
|
|
@@ -157,7 +161,4 @@ function _updateSubmission(submission_key, index, messages){
|
|
157
161
|
newMessageContent.style.display = 'none';
|
158
162
|
}
|
159
163
|
}
|
160
|
-
function getXsrfToken() {
|
161
|
-
return xsrfToken; // Use the token variable from the script
|
162
|
-
}
|
163
164
|
connectWebSocket()
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import re
|
2
|
+
import zipfile
|
3
|
+
|
4
|
+
|
5
|
+
def is_version_5(zip_ref, target_file, reg):
|
6
|
+
if target_file:
|
7
|
+
with zip_ref.open(target_file) as file:
|
8
|
+
content = file.read().decode('utf-8')
|
9
|
+
match = reg.search(content)
|
10
|
+
if match:
|
11
|
+
version = match.group(1)
|
12
|
+
version_nums = version.split(".")
|
13
|
+
if len(version_nums) > 0 and int(version_nums[0]) >= 5:
|
14
|
+
return True
|
15
|
+
return False
|
16
|
+
|
17
|
+
|
18
|
+
def otter_version_correct(autograder_path):
|
19
|
+
requirements_regex = re.compile(r"otter-grader==([\d\.]+)")
|
20
|
+
environment_regex = re.compile(r"otter-grader==([\d\.]+)")
|
21
|
+
# Open the zip file
|
22
|
+
with zipfile.ZipFile(autograder_path, 'r') as zip_ref:
|
23
|
+
# Get a list of files in the zip
|
24
|
+
file_list = zip_ref.namelist()
|
25
|
+
|
26
|
+
# Check if 'requirements.txt' or 'environment.yaml' exists
|
27
|
+
req_target_file = None
|
28
|
+
env_target_file = None
|
29
|
+
if 'requirements.txt' in file_list:
|
30
|
+
req_target_file = 'requirements.txt'
|
31
|
+
if 'environment.yml' in file_list:
|
32
|
+
env_target_file = 'environment.yml'
|
33
|
+
|
34
|
+
otter_in_req = is_version_5(zip_ref, req_target_file, requirements_regex)
|
35
|
+
otter_in_env = is_version_5(zip_ref, env_target_file, environment_regex)
|
36
|
+
return otter_in_req or otter_in_env
|
@@ -1,12 +1,13 @@
|
|
1
|
-
otter_service_stdalone/__init__.py,sha256
|
1
|
+
otter_service_stdalone/__init__.py,sha256=-jYnfCtx3xbES2DovUVovBbQqjMljXMQa3xAVnoUFQg,23
|
2
2
|
otter_service_stdalone/access_sops_keys.py,sha256=nboU5aZ84Elrm5vO0dMgpIF5LLcnecpNAwpxKvj6DvU,2129
|
3
|
-
otter_service_stdalone/app.py,sha256=
|
3
|
+
otter_service_stdalone/app.py,sha256=X_u4V02uOiFBTwM9FR0bQjXK6YcsO3Jieuji-UBwgSw,15588
|
4
4
|
otter_service_stdalone/fs_logging.py,sha256=IKFZkc5TmpI6G3vTYFAU9jDjQ-GT5aRxk8kdZ0h0kJE,2390
|
5
5
|
otter_service_stdalone/grade_notebooks.py,sha256=s-nequcOnpsZD4aQeUx_EFbnfKurVj7J8oCTK6Dix1E,3596
|
6
6
|
otter_service_stdalone/index.html,sha256=eEbFGxbV-plE5bAggGYHTv1q7yqoTN4kJbzL729uMmk,7276
|
7
7
|
otter_service_stdalone/upload_handle.py,sha256=PbpQEyUIPKercJ9hegKwvxHBvSc9uylhIfwjvHybjs0,5061
|
8
8
|
otter_service_stdalone/user_auth.py,sha256=L9Kfj1BsQttAteHhRn71IUY8WX9nvBy3MXVGq1yjTtE,4253
|
9
|
-
otter_service_stdalone/
|
9
|
+
otter_service_stdalone/util.py,sha256=FR2b_HXV7mrp7V5qytOULaTxcEh8S3gqZAgRNw_mTFs,1322
|
10
|
+
otter_service_stdalone/scripts/web_socket.js,sha256=VYbwPE6is1BINQ9ToY1HS3I6VWWIVcpCi1sVmxaxdwg,6209
|
10
11
|
otter_service_stdalone/secrets/gh_key.dev.yaml,sha256=ORUVDu8SDcv0OE2ThwROppeg7y8oLkJJbPTCMn0s5l0,1138
|
11
12
|
otter_service_stdalone/secrets/gh_key.local.yaml,sha256=NtPXXyGf1iSgJ9Oa2ahvIEf_fcmflB3dwd3LWyCgxis,1138
|
12
13
|
otter_service_stdalone/secrets/gh_key.prod.yaml,sha256=6vgLqHzDp8cVAOJlpSXGDTUjSI6EyCb6f1-SSVG2rqw,1138
|
@@ -14,8 +15,8 @@ otter_service_stdalone/secrets/gh_key.staging.yaml,sha256=cKVqj4dcwuz2LhXwMNQy_1
|
|
14
15
|
otter_service_stdalone/static_files/README_DO_NOT_DISTRIBUTE.txt,sha256=eMqBa1du1u0c07fuG3Eu9DDHuixRTFEbiQwrlvAnL1Y,353
|
15
16
|
otter_service_stdalone/static_templates/403.html,sha256=7eO3XQsEkl4nF8PEeFkLwCzGBfdZ3kkkeu_Kgpgbh0k,1440
|
16
17
|
otter_service_stdalone/static_templates/500.html,sha256=t6DeEMp8piSWyBToHb_JpTrw3GStAHFrozlmeuXyamg,1421
|
17
|
-
otter_service_stdalone-1.1.
|
18
|
-
otter_service_stdalone-1.1.
|
19
|
-
otter_service_stdalone-1.1.
|
20
|
-
otter_service_stdalone-1.1.
|
21
|
-
otter_service_stdalone-1.1.
|
18
|
+
otter_service_stdalone-1.1.10.dist-info/METADATA,sha256=SwLls0ry1r7dxv3-yG_wsqSTbwQmBZk7njEzIRqDPJY,1468
|
19
|
+
otter_service_stdalone-1.1.10.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
20
|
+
otter_service_stdalone-1.1.10.dist-info/entry_points.txt,sha256=cx447chuIEl8ly9jEoF5-2xNhaKsWcIMDdhUMH_00qQ,75
|
21
|
+
otter_service_stdalone-1.1.10.dist-info/top_level.txt,sha256=6UP22fD4OhbLt23E01v8Kvjn44vPRbyTIg_GqMYL-Ng,23
|
22
|
+
otter_service_stdalone-1.1.10.dist-info/RECORD,,
|
File without changes
|
{otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/entry_points.txt
RENAMED
File without changes
|
{otter_service_stdalone-1.1.8.dist-info → otter_service_stdalone-1.1.10.dist-info}/top_level.txt
RENAMED
File without changes
|