@@ -63,7 +63,7 @@ class AssignmentBaseHandler(GraderBaseHandler):
6363
6464 route: /lectures/{lecture_id}/assignments."""
6565
66- @authorize ([Scope .student , Scope .tutor , Scope .instructor ])
66+ @authorize ([Scope .student , Scope .tutor , Scope .instructor , Scope . admin ])
6767 async def get (self , lecture_id : int ):
6868 """Returns all assignments of a lecture.
6969
@@ -73,35 +73,44 @@ async def get(self, lecture_id: int):
7373 """
7474 lecture_id = parse_ids (lecture_id )
7575 self .validate_parameters ("include-submissions" )
76- role = self .get_role (lecture_id )
77- if role .lecture .deleted == DeleteState .deleted :
78- raise HTTPError (HTTPStatus .NOT_FOUND , reason = "Lecture not found" )
7976
80- if role .role == Scope .student : # students do not get assignments that are created
81- assignments = (
82- self .session .query (Assignment )
83- .filter (
84- Assignment .lectid == role .lecture .id ,
85- Assignment .deleted == DeleteState .active ,
86- Assignment .status != "created" ,
87- Assignment .status != "pushed" ,
77+ if not self .user .is_admin :
78+ role = self .get_role (lecture_id )
79+ if role .lecture .deleted == DeleteState .deleted :
80+ raise HTTPError (HTTPStatus .NOT_FOUND , reason = "Lecture not found" )
81+
82+ if role .role == Scope .student : # students do not get assignments that are created
83+ assignments = (
84+ self .session .query (Assignment )
85+ .filter (
86+ Assignment .lectid == role .lecture .id ,
87+ Assignment .deleted == DeleteState .active ,
88+ Assignment .status != "created" ,
89+ Assignment .status != "pushed" ,
90+ )
91+ .all ()
8892 )
89- .all ()
90- )
91-
93+ else :
94+ assignments = [a for a in role .lecture .assignments if (a .deleted == DeleteState .active )]
9295 else :
93- assignments = [a for a in role .lecture .assignments if (a .deleted == DeleteState .active )]
96+ lecture = self .get_lecture (lecture_id = lecture_id )
97+ if lecture is None :
98+ raise HTTPError (HTTPStatus .NOT_FOUND , reason = "Lecture not found" )
99+ assignments = lecture .assignments
94100
95101 # Handle the case that the user wants to include submissions
96102 include_submissions = self .get_argument ("include-submissions" , "true" ) == "true"
97103 if include_submissions :
98104 assignids = [a .id for a in assignments ]
99- user_id = self .user .id
100- results = (
101- self .session .query (Submission )
102- .filter (Submission .assignid .in_ (assignids ), Submission .user_id == user_id )
103- .all ()
104- )
105+ if not self .user .is_admin :
106+ user_id = self .user .id
107+ results = (
108+ self .session .query (Submission )
109+ .filter (Submission .assignid .in_ (assignids ), Submission .user_id == user_id )
110+ .all ()
111+ )
112+ else :
113+ results = self .session .query (Submission ).filter (Submission .assignid .in_ (assignids )).all ()
105114 # Create a combined list of assignments and submissions
106115 assignments = [a .serialize () for a in assignments ]
107116 submissions = [s .serialize () for s in results ]
@@ -242,7 +251,7 @@ async def put(self, lecture_id: int, assignment_id: int):
242251 self .session .commit ()
243252 self .write_json (assignment )
244253
245- @authorize ([Scope .student , Scope .tutor , Scope .instructor ])
254+ @authorize ([Scope .student , Scope .tutor , Scope .instructor , Scope . admin ])
246255 async def get (self , lecture_id : int , assignment_id : int ):
247256 """Returns a specific assignment of a lecture.
248257
@@ -256,9 +265,9 @@ async def get(self, lecture_id: int, assignment_id: int):
256265 assignment = self .get_assignment (lecture_id = lecture_id , assignment_id = assignment_id )
257266 self .write_json (assignment )
258267
259- @authorize ([Scope .instructor ])
268+ @authorize ([Scope .instructor , Scope . admin ])
260269 async def delete (self , lecture_id : int , assignment_id : int ):
261- """Soft-Deletes a specific assignment.
270+ """Soft or Hard -Deletes a specific assignment.
262271
263272 :param lecture_id: id of the lecture
264273 :type lecture_id: int
@@ -270,26 +279,46 @@ async def delete(self, lecture_id: int, assignment_id: int):
270279 self .validate_parameters ()
271280 assignment = self .get_assignment (lecture_id , assignment_id )
272281
273- if assignment .status in ["released" , "complete" ]:
274- msg = "Cannot delete released or completed assignments!"
275- raise HTTPError (HTTPStatus .CONFLICT , reason = msg )
282+ if not self .user .is_admin :
283+ if assignment .status in ["released" , "complete" ]:
284+ msg = "Cannot delete released or completed assignments!"
285+ raise HTTPError (HTTPStatus .CONFLICT , reason = msg )
276286
277- previously_deleted = (
278- self .session .query (Assignment )
279- .filter (
280- Assignment .lectid == lecture_id ,
281- Assignment .name == assignment .name ,
282- Assignment .deleted == DeleteState .deleted ,
287+ previously_deleted = (
288+ self .session .query (Assignment )
289+ .filter (
290+ Assignment .lectid == lecture_id ,
291+ Assignment .name == assignment .name ,
292+ Assignment .deleted == DeleteState .deleted ,
293+ )
294+ .one_or_none ()
283295 )
284- .one_or_none ()
285- )
286- if previously_deleted is not None :
287- self .session .delete (previously_deleted )
296+ if previously_deleted is not None :
297+ if len (previously_deleted .submissions ) > 0 :
298+ msg = "Assignment cannot be deleted: submissions still exist. Delete submissions first!"
299+ raise HTTPError (HTTPStatus .CONFLICT , reason = msg )
300+
301+ self .delete_assignment_files (previously_deleted )
302+ self .session .delete (previously_deleted )
303+ self .session .commit ()
304+
305+ assignment .deleted = DeleteState .deleted
288306 self .session .commit ()
307+ else :
308+ if len (assignment .submissions ) > 0 :
309+ msg = "Assignment cannot be deleted: submissions still exist. Delete submissions first!"
310+ raise HTTPError (HTTPStatus .CONFLICT , reason = msg )
289311
290- assignment .deleted = DeleteState .deleted
291- self .session .commit ()
312+ self .delete_assignment_files (assignment )
313+ self .session .delete (assignment )
314+ self .session .commit ()
292315
316+ def delete_assignment_files (self , assignment ):
317+ # delete all associated directories of the assignment
318+ assignment_path = os .path .abspath (os .path .join (self .gitbase , assignment .lecture .code , str (assignment .id )))
319+ tmp_assignment_path = os .path .abspath (os .path .join (self .tmpbase , assignment .lecture .code , str (assignment .id )))
320+ shutil .rmtree (assignment_path , ignore_errors = True )
321+ shutil .rmtree (tmp_assignment_path , ignore_errors = True )
293322
294323@register_handler (
295324 path = r"\/api\/lectures\/(?P<lecture_id>\d*)\/assignments\/(?P<assignment_id>\d*)\/reset\/?" ,
0 commit comments