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.
@@ -1 +1 @@
1
- __version__ = "1.1.7"
1
+ __version__ = "1.1.9"
@@ -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].callback:
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
- self.write_message({"messages": user_messages_dict})
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", download_message=msg)
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", download_message=msg, dcode=download_code)
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
- m = "Please save this code; it appears in the \"Notebook Grading Progress\" section below. You can "
281
- m += f"retrieve your files by submitting this code in the \"Results\" section to the right: {results_path}"
282
- self.render("index.html", message=m)
283
- try:
284
- session_queues[user_id][results_path] = Queue()
285
- session_messages[user_id][results_path] = []
286
- await g.grade(auto_p, notebooks_path, results_path, session_queues[user_id].get(results_path))
287
- except Exception as e:
288
- 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)
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 (tornado.web.RequestHandler): The request handler
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: otter_service_stdalone
3
- Version: 1.1.7
3
+ Version: 1.1.9
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=p2qAfdU7zHOShJi2f1FW-_7Tbm0-KCPE7CI7284QiHg,22
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=D0Trfw0wwwZaJo9MKPqQptUXhFosn2bq0V_m7dLOsXg,15283
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=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.7.dist-info/METADATA,sha256=Lc6rrx3zJDV1Ops-iRN04Q6NLfATZfYHlnHLeuM880g,1467
18
- otter_service_stdalone-1.1.7.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
19
- otter_service_stdalone-1.1.7.dist-info/entry_points.txt,sha256=cx447chuIEl8ly9jEoF5-2xNhaKsWcIMDdhUMH_00qQ,75
20
- otter_service_stdalone-1.1.7.dist-info/top_level.txt,sha256=6UP22fD4OhbLt23E01v8Kvjn44vPRbyTIg_GqMYL-Ng,23
21
- otter_service_stdalone-1.1.7.dist-info/RECORD,,
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,,