11import csv
2- from xxlimited import new
2+ from datetime import timedelta
33from flask import render_template , abort , request , jsonify
44from http import HTTPStatus
5- import json , re
6-
5+ import json
76from src import db , util , auth , bw_api , sched_api
7+ from src .common import wrap_delete_scheduled_run
88from src .common import verify_staff , verify_admin , verify_student
99
1010MIN_PREDEADLINE_RUNS = 1 # Minimum pre-deadline runs for every assignment
11-
11+
1212class AdminRoutes :
1313 def __init__ (self , blueprint ):
1414 def none_modified (result ):
@@ -33,7 +33,7 @@ def get_course_staff_roster(netid, cid):
3333
3434 staff = course ["staff" ]
3535 # get all admin_ids by filtering out all non-admins
36- admin = dict (filter (lambda x : x [1 ].get ("is_admin" ) == True , staff .items ()))
36+ admin = dict (filter (lambda x : x [1 ].get ("is_admin" ), staff .items ()))
3737 admin = list (admin .keys ())
3838 # get the entire staff
3939 total_staff = list (staff .keys ())
@@ -144,12 +144,38 @@ def upload_roster_file(netid, cid):
144144 return util .error ("The new roster is the same as the current one." )
145145 return util .success ("Successfully updated roster." )
146146
147+ @blueprint .route ("/staff/course/<cid>/set_template_configs/" , methods = ["POST" ])
148+ @auth .require_auth
149+ @auth .require_admin_status
150+ def set_template_values (netid , cid ):
151+ if not db .get_course (cid ):
152+ return abort (HTTPStatus .NOT_FOUND )
153+
154+ missing = util .check_missing_fields (request .form ,
155+ * ["config" , "grading_config" ])
156+ if missing :
157+ return util .error (f"Missing fields ({ ', ' .join (missing )} )." )
158+ try :
159+ json .loads (request .form ["config" ])
160+ except Exception :
161+ return util .error ("Failed to decode student job JSON." )
162+ try :
163+ json .loads (request .form ["grading_config" ])
164+ except Exception :
165+ return util .error ("Failed to decode grading job JSON." )
166+ try :
167+ db .set_templates_for_course (cid , request .form ['config' ], request .form ['grading_config' ])
168+ except Exception :
169+ return util .error ("Failed to save template to database." )
170+ return util .success ("" )
171+
172+
147173 @blueprint .route ("/staff/course/<cid>/add_assignment/" , methods = ["POST" ])
148174 @auth .require_auth
149175 @auth .require_admin_status
150176 def add_assignment (netid , cid ):
151177 missing = util .check_missing_fields (request .form ,
152- * ["aid" , "max_runs" , "quota" , "start" , "end" , "config" , "visibility" ])
178+ * ["aid" , "max_runs" , "quota" , "start" , "end" , "config" , "visibility" , "grading_config" ])
153179 if missing :
154180 return util .error (f"Missing fields ({ ', ' .join (missing )} )." )
155181
@@ -174,6 +200,9 @@ def add_assignment(netid, cid):
174200
175201 start = util .parse_form_datetime (request .form ["start" ]).timestamp ()
176202 end = util .parse_form_datetime (request .form ["end" ]).timestamp ()
203+ run_start_str = (util .parse_form_datetime (request .form ["end" ]) + timedelta (minutes = 5 )).strftime ("%Y-%m-%dT%H:%M" )
204+ end_str = util .parse_form_datetime (request .form ["end" ]).strftime ("%Y-%m-%dT%H:%M" )
205+ run_id = db .generate_new_id ()
177206 if start is None or end is None :
178207 return util .error ("Missing or invalid Start or End." )
179208 if start >= end :
@@ -191,6 +220,9 @@ def add_assignment(netid, cid):
191220 visibility = request .form ["visibility" ]
192221
193222 db .add_assignment (cid , aid , max_runs , quota , start , end , visibility )
223+ # Schedule Final Grading Run
224+ add_or_edit_scheduled_run (cid , aid , run_id , {"run_time" : run_start_str , "due_time" : end_str , "name" : "Final Grading Run" , "config" : request .form ['grading_config' ]}, None )
225+ db .pair_assignment_final_grading_run (cid , aid , str (run_id ))
194226 return util .success ("" )
195227
196228 @blueprint .route ("/staff/course/<cid>/<aid>/edit/" , methods = ["POST" ])
@@ -288,15 +320,20 @@ def staff_add_extension(netid, cid, aid):
288320 return util .error ("Start must be before End." )
289321
290322 for student_netid in student_netids :
291- db .add_extension (cid , aid , student_netid , max_runs , start , end )
323+ end_str = util .parse_form_datetime (request .form ["end" ]).strftime ("%Y-%m-%dT%H:%M" )
324+ # avoid that weird race condition - start run 5 min after, but with a container due date of the original time
325+ ext_end = (util .parse_form_datetime (request .form ["end" ]) + timedelta (minutes = 5 )).strftime ("%Y-%m-%dT%H:%M" )
326+ run_id = db .generate_new_id ()
327+ add_or_edit_scheduled_run (cid , aid , run_id , {"run_time" : ext_end , "due_time" : end_str , "name" : f"Extension Run - { student_netid } " , "config" : request .form ['config' ], "roster" : student_netid }, None )
328+ db .add_extension (cid , aid , student_netid , max_runs , start , end , run_id )
292329 return util .success ("" )
293330
294331 @blueprint .route ("/staff/course/<cid>/<aid>/extensions/" , methods = ["DELETE" ])
295332 @auth .require_auth
296333 @auth .require_admin_status
297334 def staff_delete_extension (netid , cid , aid ):
298335 extension_id = request .form ["_id" ]
299- delete_result = db .delete_extension (extension_id )
336+ delete_result = db .delete_extension (cid , aid , extension_id )
300337
301338 if delete_result is None :
302339 return util .error ("Invalid extension, please refresh the page." )
@@ -313,26 +350,26 @@ def add_or_edit_scheduled_run(cid, aid, run_id, form, scheduled_run_id):
313350 return abort (HTTPStatus .NOT_FOUND )
314351
315352 # form validation
316- missing = util .check_missing_fields (request . form , "run_time" , "due_time" , "name" , "config" )
353+ missing = util .check_missing_fields (form , "run_time" , "due_time" , "name" , "config" )
317354 if missing :
318355 return util .error (f"Missing fields ({ ', ' .join (missing )} )." )
319- run_time = util .parse_form_datetime (request . form ["run_time" ]).timestamp ()
356+ run_time = util .parse_form_datetime (form ["run_time" ]).timestamp ()
320357 if run_time is None :
321358 return util .error ("Missing or invalid run time." )
322359 if run_time <= util .now_timestamp ():
323360 return util .error ("Run time must be in the future." )
324- due_time = util .parse_form_datetime (request . form ["due_time" ]).timestamp ()
361+ due_time = util .parse_form_datetime (form ["due_time" ]).timestamp ()
325362 if due_time is None :
326363 return util .error ("Missing or invalid due time." )
327- if "roster" not in request . form or not request . form ["roster" ]:
364+ if "roster" not in form or not form ["roster" ]:
328365 roster = None
329366 else :
330- roster = request . form ["roster" ].replace (" " , "" ).lower ().split ("," )
367+ roster = form ["roster" ].replace (" " , "" ).lower ().split ("," )
331368 for student_netid in roster :
332369 if not util .valid_id (student_netid ) or not verify_student (student_netid , cid ):
333370 return util .error (f"Invalid or non-existent student NetID: { student_netid } " )
334371 try :
335- config = json .loads (request . form ["config" ])
372+ config = json .loads (form ["config" ])
336373 msg = bw_api .set_assignment_config (cid , f"{ aid } _{ run_id } " , config )
337374 if msg :
338375 return util .error (f"Failed to upload config to Broadway: { msg } " )
@@ -351,7 +388,7 @@ def add_or_edit_scheduled_run(cid, aid, run_id, form, scheduled_run_id):
351388
352389 assert scheduled_run_id is not None
353390
354- if not db .add_or_update_scheduled_run (run_id , cid , aid , run_time , due_time , roster , request . form ["name" ], scheduled_run_id ):
391+ if not db .add_or_update_scheduled_run (run_id , cid , aid , run_time , due_time , roster , form ["name" ], scheduled_run_id ):
355392 return util .error ("Failed to save the changes, please try again." )
356393 return util .success ("" )
357394
@@ -389,13 +426,8 @@ def staff_get_scheduled_run(netid, cid, aid, run_id):
389426 @auth .require_auth
390427 @auth .require_admin_status
391428 def staff_delete_scheduled_run (netid , cid , aid , run_id ):
392- sched_run = db .get_scheduled_run (cid , aid , run_id )
393- if sched_run is None :
394- return util .error ("Cannot find scheduled run" )
395- sched_api .delete_scheduled_run (sched_run ["scheduled_run_id" ])
396- if not db .delete_scheduled_run (cid , aid , run_id ):
397- return util .error ("Failed to delete scheduled run. Please try again" )
398- return util .success ("" )
399-
400-
401-
429+ try :
430+ wrap_delete_scheduled_run (cid , aid , run_id )
431+ return util .success ("" )
432+ except Exception as e :
433+ return util .error (str (e ))
0 commit comments