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.
@@ -1 +1 @@
1
- __version__ = "1.1.8"
1
+ __version__ = "1.1.10"
@@ -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].callback:
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
- self.write_message({"messages": user_messages_dict})
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", download_message=msg)
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", download_message=msg, dcode=download_code)
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
- m = "Please save this code; it appears in the \"Notebook Grading Progress\" section below. You can "
278
- m += f"retrieve your files by submitting this code in the \"Results\" section to the right: {results_path}"
279
- self.render("index.html", message=m)
280
- try:
281
- session_queues[user_id][results_path] = Queue()
282
- session_messages[user_id][results_path] = []
283
- await g.grade(auto_p, notebooks_path, results_path, session_queues[user_id].get(results_path))
284
- except Exception as e:
285
- log.write_logs(results_path, "Grading Problem", str(e), "error", log_error)
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 (tornado.web.RequestHandler): The request handler
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: otter_service_stdalone
3
- Version: 1.1.8
3
+ Version: 1.1.10
4
4
  Summary: Grading Service for Instructors using Otter Grader
5
5
  Home-page: https://github.com/sean-morris/otter-service-stdalone
6
6
  Author: Sean Morris
@@ -1,12 +1,13 @@
1
- otter_service_stdalone/__init__.py,sha256=ii9AGvWXG_4qHN0FEZs45urJ5UrPQ9I83YjXUjFMvtI,22
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=cDo8-24OtPDMkHlYWqncQwCNwmcHqcY8h04zx6E3xSE,15082
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/scripts/web_socket.js,sha256=xoG7kt5xU9jS1vqw7_NuPLShzEqHXtQyaCnmvhCOrUk,6081
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.8.dist-info/METADATA,sha256=pZHap_5lAAN5RKLHD-qLAXcJUffRm4MexOzyL9_UKbQ,1467
18
- otter_service_stdalone-1.1.8.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
19
- otter_service_stdalone-1.1.8.dist-info/entry_points.txt,sha256=cx447chuIEl8ly9jEoF5-2xNhaKsWcIMDdhUMH_00qQ,75
20
- otter_service_stdalone-1.1.8.dist-info/top_level.txt,sha256=6UP22fD4OhbLt23E01v8Kvjn44vPRbyTIg_GqMYL-Ng,23
21
- otter_service_stdalone-1.1.8.dist-info/RECORD,,
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,,