otter-service-stdalone 1.1.7__py3-none-any.whl → 1.1.9__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 -35
- otter_service_stdalone/scripts/web_socket.js +4 -3
- otter_service_stdalone/util.py +36 -0
- {otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/METADATA +1 -1
- {otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/RECORD +9 -8
- {otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/WHEEL +0 -0
- {otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/entry_points.txt +0 -0
- {otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.1.
|
1
|
+
__version__ = "1.1.9"
|
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,19 +37,16 @@ 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
|
|
42
44
|
def on_close(self):
|
43
45
|
"""stop the periodic callback on close
|
44
46
|
"""
|
45
|
-
close_code = self.close_code
|
46
|
-
close_reason = self.close_reason
|
47
47
|
if self.get_secure_cookie("user"):
|
48
48
|
user_id = self.get_secure_cookie("user").decode('utf-8')
|
49
|
-
if user_id in session_callbacks and session_callbacks[user_id]
|
50
|
-
log.write_logs("ws-close-removing-user!", close_code, f"{close_code}: {close_reason}", "debug", log_debug)
|
49
|
+
if user_id in session_callbacks and session_callbacks[user_id]:
|
51
50
|
session_callbacks[user_id].stop()
|
52
51
|
session_callbacks.pop(user_id)
|
53
52
|
|
@@ -65,7 +64,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
|
65
64
|
if not q.empty():
|
66
65
|
while not q.empty():
|
67
66
|
user_messages_dict[result_id].append(q.get())
|
68
|
-
|
67
|
+
self.write_message({"messages": user_messages_dict})
|
69
68
|
except tornado.websocket.WebSocketClosedError:
|
70
69
|
log.write_logs("ws-error", "Web Socket Close Error", "", "", log_error)
|
71
70
|
except Exception:
|
@@ -186,16 +185,16 @@ class Download(BaseHandler):
|
|
186
185
|
m = "Download: Directory for Code Not existing"
|
187
186
|
log.write_logs(download_code, m, f"{download_code}", "debug", log_debug)
|
188
187
|
msg = "The download code appears to not be correct or expired "
|
189
|
-
msg += f"- results are deleted regularly: {download_code}."
|
188
|
+
msg += f"- results are deleted regularly: {download_code}. "
|
190
189
|
msg += "Please check the code or upload your notebooks "
|
191
190
|
msg += "and autograder.zip for grading again."
|
192
|
-
self.render("index.html",
|
191
|
+
self.render("index.html", download_message=msg)
|
193
192
|
elif not os.path.exists(f"{directory}/final_grades.csv"):
|
194
193
|
m = "Download: Results Not Ready"
|
195
194
|
log.write_logs(download_code, m, f"{download_code}", "debug", log_debug)
|
196
195
|
msg = "The results of your download are not ready yet. "
|
197
196
|
msg += "Please check back."
|
198
|
-
self.render("index.html",
|
197
|
+
self.render("index.html", download_message=msg, dcode=download_code)
|
199
198
|
else:
|
200
199
|
m = "Download Success: Creating results.zip"
|
201
200
|
log.write_logs(download_code, m, "", "debug", log_debug)
|
@@ -252,7 +251,7 @@ class Upload(BaseHandler):
|
|
252
251
|
results_path = str(uuid.uuid4())
|
253
252
|
autograder = self.request.files['autograder'][0] if "autograder" in files else None
|
254
253
|
notebooks = self.request.files['notebooks'][0] if "notebooks" in files else None
|
255
|
-
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)
|
256
255
|
if autograder is not None and notebooks is not None:
|
257
256
|
notebooks_fname = notebooks['filename']
|
258
257
|
notebooks_extn = os.path.splitext(notebooks_fname)[1]
|
@@ -267,25 +266,36 @@ class Upload(BaseHandler):
|
|
267
266
|
if not os.path.exists(__UPLOADS__):
|
268
267
|
os.mkdir(__UPLOADS__)
|
269
268
|
auto_p = f"{__UPLOADS__}/{autograder_name}"
|
269
|
+
|
270
270
|
notebooks_path = f"{__UPLOADS__}/{notebooks_name}"
|
271
271
|
m = "Step 2a: Uploaded File Names Determined"
|
272
272
|
log.write_logs(results_path, m, f"notebooks path: {notebooks_path}", "debug", log_debug)
|
273
273
|
fh = open(auto_p, 'wb')
|
274
274
|
fh.write(autograder['body'])
|
275
|
-
|
276
|
-
fh = open(notebooks_path, 'wb')
|
277
|
-
fh.write(notebooks['body'])
|
278
|
-
m = "Step 3: Uploaded Files Written to Disk"
|
275
|
+
m = "Step 3A: Uploaded autograder.zip files written to disk - now checking otter version"
|
279
276
|
log.write_logs(results_path, m, f"Results Code: {results_path}", "debug", log_debug)
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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)
|
289
299
|
else:
|
290
300
|
m = "Step 2b: Uploaded Files not given"
|
291
301
|
log.write_logs(results_path, m, "", "debug", log_debug)
|
@@ -297,19 +307,8 @@ class RemoveProgressHandler(BaseHandler):
|
|
297
307
|
"""This handles requests to remove progress on a specific submission
|
298
308
|
|
299
309
|
Args:
|
300
|
-
tornado (
|
310
|
+
tornado (BaseHandler): The request handler
|
301
311
|
"""
|
302
|
-
def set_default_headers(self):
|
303
|
-
"""Set CORS headers to allow cross-origin requests."""
|
304
|
-
self.set_header("Access-Control-Allow-Origin", "*") # Allow requests from any domain
|
305
|
-
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
|
306
|
-
self.set_header("Access-Control-Allow-Methods", "DELETE, GET, POST, OPTIONS")
|
307
|
-
|
308
|
-
def options(self, *args):
|
309
|
-
"""Respond to OPTIONS requests for preflight in CORS."""
|
310
|
-
self.set_status(204)
|
311
|
-
self.finish()
|
312
|
-
|
313
312
|
@tornado.web.authenticated
|
314
313
|
def get(self):
|
315
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.yaml' in file_list:
|
32
|
+
env_target_file = 'environment.yaml'
|
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=lnHAM_YSOt5TvtAm4ZVs32T0TpsCQ5kOOznnQlur1YE,22
|
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=ptsV1_W-IwpBVVk9oPJWNjaqdh8DJKWVpVqXHaQrUO0,1324
|
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.9.dist-info/METADATA,sha256=wKJrBRrfS6-09EtWyq6P4m1Ba7o5ltbat8UUhnyiOH8,1467
|
19
|
+
otter_service_stdalone-1.1.9.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
20
|
+
otter_service_stdalone-1.1.9.dist-info/entry_points.txt,sha256=cx447chuIEl8ly9jEoF5-2xNhaKsWcIMDdhUMH_00qQ,75
|
21
|
+
otter_service_stdalone-1.1.9.dist-info/top_level.txt,sha256=6UP22fD4OhbLt23E01v8Kvjn44vPRbyTIg_GqMYL-Ng,23
|
22
|
+
otter_service_stdalone-1.1.9.dist-info/RECORD,,
|
File without changes
|
{otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/entry_points.txt
RENAMED
File without changes
|
{otter_service_stdalone-1.1.7.dist-info → otter_service_stdalone-1.1.9.dist-info}/top_level.txt
RENAMED
File without changes
|