def test_action_find_by_action_distinguish_actions(db, assignment_tree, user_johaannes): # Add a fetched action orm_action = Action( user_id=user_johaannes.id, assignment_id=assignment_tree.id, action=AssignmentActions.fetched, location="/some/random/path/to/a/file.tzg", ) db.add(orm_action) db.commit() found_by_pk = Action.find_by_pk(db, 1) # Without an action, we just get the last action found_recent = Action.find_most_recent_action(db, found_by_pk.assignment_id) assert found_recent.action == AssignmentActions.fetched # We can get different entries if we define the action found_recent_a = Action.find_most_recent_action(db, found_by_pk.assignment_id, AssignmentActions.fetched) assert found_recent_a.action == AssignmentActions.fetched found_recent_b = Action.find_most_recent_action(db, found_by_pk.assignment_id, AssignmentActions.released) assert found_recent_b.action == AssignmentActions.released assert found_recent_a.id != found_recent_b.id
def test_feedback_find_all_for_student_again(db, assignment_tree, user_johaannes, user_kaylee): notebook = Notebook.find_by_name(db, "Exam 2", assignment_tree.id) released = Action.find_most_recent_action(db, assignment_tree.id, AssignmentActions.fetched) orm_feedback = Feedback( notebook_id=notebook.id, instructor_id=user_kaylee.id, student_id=user_johaannes.id, location=released.location, checksum="234567890abcdef1", timestamp=released.timestamp, ) db.add(orm_feedback) db.commit() # Note this this swaps instructor & user, so should *not* be included orm_feedback_2 = Feedback( notebook_id=notebook.id, instructor_id=user_johaannes.id, student_id=user_kaylee.id, location=released.location, checksum="34567890abcdef12", timestamp=released.timestamp, ) db.add(orm_feedback_2) db.commit() feedback = Feedback.find_all_for_student(db, user_johaannes.id, assignment_tree.id) assert len(feedback) == 2
def test_action_object_creation_errors(db, course_strange, assignment_tree, user_johaannes): role = "instructor" release_file = "/some/random/path/to/a/file.tzg" orm_subscription = Subscription(user_id=user_johaannes.id, course_id=course_strange.id, role=role) db.add(orm_subscription) db.commit() action = Action( user_id=user_johaannes.id, assignment_id=assignment_tree.id, location=release_file, ) db.add(action) with pytest.raises(IntegrityError): db.commit() db.rollback() # #### Why won't you work in github Actions, you bar steward # action = Action( # user_id=user_johaannes.id, # assignment_id=assignment_tree.id, # location="/some/random/path/to/a/file.tzg", # action="foo", # ) # db.add(action) # with pytest.raises(Exception): # db.commit() # db.rollback() orm_action = Action( action=AssignmentActions.released, location="/some/random/path/to/a/file.tzg", ) # # Why does that work?? db.add(orm_action) db.commit() orm_action.user_id = user_johaannes.id orm_action.assignment_id = assignment_tree.id db.commit()
def test_action_base_mathods_and_find_by_pk(db, assignment_tree, user_johaannes): # subscription set up earlier release_file = "/some/random/path/to/a/file.tzg" with pytest.raises(TypeError): found_by_pk = Action.find_by_pk() with pytest.raises(TypeError): found_by_pk = Action.find_by_pk(db) with pytest.raises(ValueError): found_by_pk = Action.find_by_pk(db, None) with pytest.raises(TypeError): found_by_pk = Action.find_by_pk(db, "abc") found_by_pk = Action.find_by_pk(db, 1) assert found_by_pk.id == 1 assert found_by_pk.action == AssignmentActions.released assert found_by_pk.location == release_file # check the relationships assert found_by_pk.user.name == user_johaannes.name assert found_by_pk.assignment.assignment_code == assignment_tree.assignment_code found_by_pk = Action.find_by_pk(db, 11) assert found_by_pk is None
def test_action_find_by_action(db): # subscription & action set up earlier with pytest.raises(TypeError): found_by_pk = Action.find_most_recent_action(db, None) with pytest.raises(TypeError): found_by_pk = Action.find_most_recent_action(db, "abc") with pytest.raises(TypeError): found_by_pk = Action.find_most_recent_action(db, 1, dict()) found_by_pk = Action.find_by_pk(db, 1) # released found_recent = Action.find_most_recent_action(db, found_by_pk.assignment_id, found_by_pk.action) assert found_recent.action == found_by_pk.action found_recent = Action.find_most_recent_action(db, found_by_pk.assignment_id, "released") assert found_recent.action == found_by_pk.action found_recent = Action.find_most_recent_action(db, found_by_pk.assignment_id, AssignmentActions.released) assert found_recent.action == found_by_pk.action found_recent = Action.find_most_recent_action(db, found_by_pk.assignment_id, AssignmentActions.feedback_fetched) assert found_recent is None
def test_feedback_base_mathods_and_find_by_pk(db, assignment_tree, user_kaylee, user_johaannes): # previous subscriptions & notebooks still in the db notebook = Notebook.find_by_name(db, "Exam 2", assignment_tree.id) released = Action.find_most_recent_action(db, assignment_tree.id, AssignmentActions.released) orm_feedback = Feedback( notebook_id=notebook.id, instructor_id=user_kaylee.id, student_id=user_johaannes.id, location=released.location, checksum="1234567890abcdef", timestamp=released.timestamp, ) db.add(orm_feedback) db.commit() with pytest.raises(TypeError): found_by_pk = Feedback.find_by_pk() with pytest.raises(TypeError): found_by_pk = Feedback.find_by_pk(db) with pytest.raises(ValueError): found_by_pk = Feedback.find_by_pk(db, None) with pytest.raises(TypeError): found_by_pk = Feedback.find_by_pk(db, "abc") found_by_pk = Feedback.find_by_pk(db, orm_feedback.id) assert found_by_pk.id == orm_feedback.id assert ( str(found_by_pk) == f"Feedback<Notebook-{found_by_pk.notebook_id}/Student-{found_by_pk.student_id}/{found_by_pk.checksum}>" ) assert found_by_pk.notebook_id == notebook.id assert found_by_pk.instructor_id == user_kaylee.id assert found_by_pk.student_id == user_johaannes.id # relationships assert found_by_pk.notebook.name == notebook.name assert found_by_pk.instructor.name == user_kaylee.name assert found_by_pk.student.name == user_johaannes.name found_by_pk = Feedback.find_by_pk(db, orm_feedback.id + 10) assert found_by_pk is None
def test_all_the_unicode(db, assignment_a2ovi, user_rur, course_strange): # subscribe user to course # add assignment to course role = "instructor" release_file = "/some/random/path/to/a/file.tzg" orm_subscription = Subscription(user_id=user_rur.id, course_id=course_strange.id, role=role) db.add(orm_subscription) assignment_a2ovi.course_id = course_strange.id db.commit() found_by_pk = Subscription.find_by_pk(db, orm_subscription.id) assert found_by_pk.id == orm_subscription.id assert orm_subscription.course.course_title == "Damnation Alley" # release orm_action = Action( action=AssignmentActions.released, location=release_file, ) db.add(orm_action) db.commit() orm_action.user_id = user_rur.id orm_action.assignment_id = assignment_a2ovi.id db.commit() # fetch orm_action = Action( user_id=user_rur.id, assignment_id=assignment_a2ovi.id, action=AssignmentActions.fetched, location=release_file, ) db.add(orm_action) db.commit() found = Action.find_most_recent_action(db, assignment_a2ovi.id) assert found.user.name == user_rur.name
def get(self): [course_id, assignment_id] = self.get_params(["course_id", "assignment_id"]) if not assignment_id or not course_id: note = "Feedback call requires an assignment id and a course id" self.log.info(note) self.finish({"success": False, "note": note}) return self.log.debug( f"checking for feedback for {assignment_id} on {course_id}") this_user = self.nbex_user with scoped_session() as session: course = Course.find_by_code(db=session, code=course_id, org_id=this_user["org_id"], log=self.log) if not course: note = f"Course {course_id} not found" self.log.info(note) # self.finish({"success": False, "note": note, "value": []}) # return raise web.HTTPError(404, note) assignment = AssignmentModel.find_by_code(db=session, code=assignment_id, course_id=course.id, log=self.log) if not assignment: note = f"Assignment {assignment_id} for Course {course_id} not found" self.log.info(note) # self.finish({"success": False, "note": note, "value": []}) # return raise web.HTTPError(404, note) student = User.find_by_name(db=session, name=this_user["name"], log=self.log) res = Feedback.find_all_for_student( db=session, student_id=student.id, assignment_id=assignment.id, log=self.log, ) feedbacks = [] for r in res: f = {} notebook = Notebook.find_by_pk(db=session, pk=r.notebook_id, log=self.log) if notebook is not None: feedback_name = "{0}.html".format(notebook.name) else: feedback_name = os.path.basename(r.location) with open(r.location, "r+b") as fp: f["content"] = base64.b64encode(fp.read()).decode("utf-8") f["filename"] = feedback_name # This matches self.timestamp_format f["timestamp"] = r.timestamp.strftime( "%Y-%m-%d %H:%M:%S.%f %Z") f["checksum"] = r.checksum feedbacks.append(f) # Add action action = Action( user_id=this_user["id"], assignment_id=assignment.id, action=AssignmentActions.feedback_fetched, location=r.location, ) session.add(action) self.finish({"success": True, "feedback": feedbacks})
def post(self): """ This endpoint accepts feedback files for a notebook. It requires a notebook id, student id, feedback timestamp and a checksum. The endpoint return {'success': true} for all successful feedback releases. """ [ course_id, assignment_id, notebook_id, student_id, timestamp, checksum, ] = self.get_params([ "course_id", "assignment_id", "notebook", "student", "timestamp", "checksum", ]) if not (course_id and assignment_id and notebook_id and student_id and timestamp and checksum): note = "Feedback call requires a course id, assignment id, notebook name, student id, checksum and timestamp." self.log.debug(note) self.finish({"success": False, "note": note}) return this_user = self.nbex_user if course_id not in this_user["courses"]: note = f"User not subscribed to course {course_id}" self.log.info(note) self.finish({"success": False, "note": note}) return if ("instructor" != this_user["current_role"].casefold() ): # we may need to revisit this note = f"User not an instructor to course {course_id}" self.log.info(note) self.finish({"success": False, "note": note}) return with scoped_session() as session: # Start building feedback object course = Course.find_by_code(db=session, code=course_id, org_id=this_user["org_id"], log=self.log) if not course: self.log.info( f"Could not find requested resource course {course_id}") raise web.HTTPError( 404, f"Could not find requested resource course {course_id}") assignment = AssignmentModel.find_by_code( db=session, code=assignment_id, course_id=course.id, action=AssignmentActions.released.value, ) if not assignment: note = f"Could not find requested resource assignment {assignment_id}" self.log.info(note) raise web.HTTPError(404, note) notebook = Notebook.find_by_name(db=session, name=notebook_id, assignment_id=assignment.id, log=self.log) if not notebook: note = f"Could not find requested resource notebook {notebook_id}" self.log.info(note) raise web.HTTPError(404, note) student = User.find_by_name(db=session, name=student_id, log=self.log) if not student: note = f"Could not find requested resource student {student_id}" self.log.info(note) raise web.HTTPError(404, note) # # raise Exception(f"{res}") # self.log.info(f"Notebook: {notebook}") # self.log.info(f"Student: {student}") # self.log.info(f"Instructor: {this_user}") # TODO: check access. Is the user an instructor on the course to which the notebook belongs # Check whether there is an HTML file attached to the request if not self.request.files: self.log.warning(f"Error: No file supplied in upload" ) # TODO: improve error message raise web.HTTPError(412) # precondition failed try: # Grab the file file_info = self.request.files["feedback"][0] filename, content_type = ( file_info["filename"], file_info["content_type"], ) note = f"Received file {filename}, of type {content_type}" self.log.info(note) fbfile = tempfile.NamedTemporaryFile() fbfile.write(file_info["body"]) fbfile.seek(0) except Exception as e: # Could not grab the feedback file self.log.error(f"Error: {e}") raise web.HTTPError(412) # TODO: should we check the checksum? # unique_key = make_unique_key( # course_id, # assignment_id, # notebook_id, # student_id, # str(timestamp).strip(), # ) # check_checksum = notebook_hash(fbfile.name, unique_key) # # if check_checksum != checksum: # self.log.info(f"Checksum {checksum} does not match {check_checksum}") # raise web.HTTPError(403, f"Checksum {checksum} does not match {check_checksum}") # TODO: What is file of the original notebook we are getting the feedback for? # assignment_dir = "collected/student_id/assignment_name" # nbfile = os.path.join(assignment_dir, "{}.ipynb".format(notebook.name)) # calc_checksum = notebook_hash(nbfile.name, unique_key) # if calc_checksum != checksum: # self.log.info(f"Mismatched checksums {calc_checksum} and {checksum}.") # raise web.HTTPError(412) location = "/".join([ self.base_storage_location, str(this_user["org_id"]), "feedback", notebook.assignment.course.course_code, notebook.assignment.assignment_code, str(int(time.time())), ]) # This should be abstracted, so it can be overloaded to store in other manners (eg AWS) feedback_file = location + "/" + checksum + ".html" try: # Ensure the directory exists os.makedirs(os.path.dirname(feedback_file), exist_ok=True) with open(feedback_file, "w+b") as handle: handle.write(file_info["body"]) except Exception as e: self.log.error(f"Could not save file. \n {e}") raise web.HTTPError(500) feedback = Feedback( notebook_id=notebook.id, checksum=checksum, location=feedback_file, student_id=student.id, instructor_id=this_user.get("id"), timestamp=parser.parse(timestamp), ) session.add(feedback) # Add action action = Action( user_id=this_user["id"], assignment_id=notebook.assignment.id, action=AssignmentActions.feedback_released, location=feedback_file, ) session.add(action) self.finish({"success": True, "note": "Feedback released"})
def post(self): # Do a content-length check, before we go any further if "Content-Length" in self.request.headers and int( self.request.headers["Content-Length"] ) > int(self.max_buffer_size): note = "File upload oversize, and rejected. Please reduce the contents of the assignment, re-generate, and re-release" self.log.info(note) self.finish({"success": False, "note": note}) return [course_code, assignment_code] = self.get_params(["course_id", "assignment_id"]) self.log.debug( f"Called POST /assignment with arguments: course {course_code} and assignment {assignment_code}" ) if not (course_code and assignment_code): note = f"Posting an Assigment requires a course code and an assignment code" self.log.info(note) self.finish({"success": False, "note": note}) return this_user = self.nbex_user if not course_code in this_user["courses"]: note = f"User not subscribed to course {course_code}" self.log.info(note) self.finish({"success": False, "note": note}) return if ( not "instructor" == this_user["current_role"].casefold() ): # we may need to revisit this note = f"User not an instructor to course {course_code}" self.log.info(note) self.finish({"success": False, "note": note}) return # The course will exist: the user object creates it if it doesn't exist # - and we know the user is subscribed to the course as an instructor (above) with scoped_session() as session: course = Course.find_by_code( db=session, code=course_code, org_id=this_user["org_id"], log=self.log ) # We need to find this assignment, or make a new one. assignment = AssignmentModel.find_by_code( db=session, code=assignment_code, course_id=course.id ) if assignment is None: # Look for inactive assignments assignment = AssignmentModel.find_by_code( db=session, code=assignment_code, course_id=course.id, active=False ) if assignment is None: self.log.info( f"New Assignment details: assignment_code:{assignment_code}, course_id:{course.id}" ) # defaults active assignment = AssignmentModel( assignment_code=assignment_code, course_id=course.id ) session.add(assignment) # deliberately no commit: we need to be able to roll-back if there's no data! # Set assignment to active assignment.active = True # storage is dynamically in $path/release/$course_code/$assignment_code/<timestamp>/ # Note - this means we can have multiple versions of the same release on the system release_file = "/".join( [ self.base_storage_location, str(this_user["org_id"]), AssignmentActions.released.value, course_code, assignment_code, str(int(time.time())), ] ) if not self.request.files: self.log.warning( f"Error: No file supplies in upload" ) # TODO: improve error message raise web.HTTPError(412) # precondition failed try: # Write the uploaded file to the desired location file_info = self.request.files["assignment"][0] filename, content_type = ( file_info["filename"], file_info["content_type"], ) note = f"Received file {filename}, of type {content_type}" self.log.info(note) extn = os.path.splitext(filename)[1] cname = str(uuid.uuid4()) + extn # store to disk. # This should be abstracted, so it can be overloaded to store in other manners (eg AWS) release_file = release_file + "/" + cname # Ensure the directory exists os.makedirs(os.path.dirname(release_file), exist_ok=True) with open(release_file, "w+b") as handle: handle.write(file_info["body"]) except Exception as e: # TODO: exception handling self.log.warning(f"Error: {e}") # TODO: improve error message self.log.info(f"Upload failed") # error 500?? raise Exception # Check the file exists on disk if not ( os.path.exists(release_file) and os.access(release_file, os.R_OK) and os.path.getsize(release_file) > 0 ): note = "File upload failed." self.log.info(note) self.finish({"success": False, "note": note}) return # We shouldn't get here, but a double-check is good if os.path.getsize(release_file) > self.max_buffer_size: os.remove(release_file) note = "File upload oversize, and rejected. Please reduce the contents of the assignment, re-generate, and re-release" self.log.info(note) self.finish({"success": False, "note": note}) return # now commit the assignment, and get it back to find the id assignment = AssignmentModel.find_by_code( db=session, code=assignment_code, course_id=course.id ) # Record the notebooks associated with this assignment notebooks = self.get_arguments("notebooks") for notebook in notebooks: self.log.debug(f"Adding notebook {notebook}") new_notebook = Notebook(name=notebook) assignment.notebooks.append(new_notebook) # Record the action. # Note we record the path to the files. self.log.info( f"Adding action {AssignmentActions.released.value} for user {this_user['id']} against assignment {assignment.id}" ) action = Action( user_id=this_user["id"], assignment_id=assignment.id, action=AssignmentActions.released, location=release_file, ) session.add(action) self.finish({"success": True, "note": "Released"})
def get(self): # def get(self, course_code, assignment_code=None): [course_code, assignment_code] = self.get_params(["course_id", "assignment_id"]) if not (course_code and assignment_code): note = "Assigment call requires both a course code and an assignment code!!" self.log.info(note) self.finish({"success": False, "note": note}) return this_user = self.nbex_user if not course_code in this_user["courses"]: note = f"User not subscribed to course {course_code}" self.log.info(note) self.finish({"success": False, "note": note}) return # Find the course being referred to with scoped_session() as session: course = Course.find_by_code( db=session, code=course_code, org_id=this_user["org_id"], log=self.log ) if course is None: note = f"Course {course_code} does not exist" self.log.info(note) self.finish({"success": False, "note": note}) return # needs a proper 'fail' here note = "" self.log.debug(f"Course:{course_code} assignment:{assignment_code}") # The location for the data-object is actually held in the 'released' action for the given assignment # We want the last one... assignment = AssignmentModel.find_by_code( db=session, code=assignment_code, course_id=course.id, action=AssignmentActions.released.value, ) if assignment is None: note = f"Assignment {assignment_code} does not exist" self.log.info(note) self.finish({"success": False, "note": note}) return # needs a proper 'fail' here self._headers = httputil.HTTPHeaders( { "Content-Type": "application/gzip", "Date": httputil.format_timestamp(time.time()), } ) data = b"" release_file = None action = Action.find_most_recent_action( db=session, assignment_id=assignment.id, action=AssignmentActions.released, log=self.log, ) release_file = action.location if release_file: try: with open(release_file, "r+b") as handle: data = handle.read() except Exception as e: # TODO: exception handling self.log.warning(f"Error: {e}") # TODO: improve error message self.log.info(f"Unable to open file") # error 500?? raise Exception self.log.info( f"Adding action {AssignmentActions.fetched.value} for user {this_user['id']} against assignment {assignment.id}" ) action = Action( user_id=this_user["id"], assignment_id=assignment.id, action=AssignmentActions.fetched, location=release_file, ) session.add(action) self.log.info("record of fetch action committed") self.finish(data) else: self.log.info("no release file found") raise Exception
def post(self): if "Content-Length" in self.request.headers and int( self.request.headers["Content-Length"]) > int( self.max_buffer_size): note = "File upload oversize, and rejected. Please reduce the files in your submission and try again." self.log.info(note) self.finish({"success": False, "note": note}) return [course_code, assignment_code] = self.get_params(["course_id", "assignment_id"]) self.log.debug( f"Called POST /submission with arguments: course {course_code} and assignment {assignment_code}" ) if not (course_code and assignment_code): note = f"Submission call requires both a course code and an assignment code" self.log.info(note) self.finish({"success": False, "note": note}) return this_user = self.nbex_user if not course_code in this_user["courses"]: note = f"User not subscribed to course {course_code}" self.log.info(note) self.finish({"success": False, "note": note}) return # The course will exist: the user object creates it if it doesn't exist # - and we know the user is subscribed to the course as an instructor (above) with scoped_session() as session: course = Course.find_by_code(db=session, code=course_code, org_id=this_user["org_id"], log=self.log) # We need to find this assignment, or make a new one. assignment = Assignment.find_by_code(db=session, code=assignment_code, course_id=course.id) if assignment is None: note = f"User not fetched assignment {assignment_code}" self.log.info(note) self.finish({"success": False, "note": note}) return # storage is dynamically in $path/submitted/$course_code/$assignment_code/$username/<timestamp>/ # Note - this means that a user can submit multiple times, and we have all copies release_file = "/".join([ self.base_storage_location, str(this_user["org_id"]), AssignmentActions.submitted.value, course_code, assignment_code, this_user["name"], str(int(time.time())), ]) if not self.request.files: self.log.warning(f"Error: No file supplies in upload" ) # TODO: improve error message raise web.HTTPError(412) # precondition failed try: # Write the uploaded file to the desired location file_info = self.request.files["assignment"][0] filename, content_type = ( file_info["filename"], file_info["content_type"], ) note = f"Received file {filename}, of type {content_type}" self.log.info(note) extn = os.path.splitext(filename)[1] cname = str(uuid.uuid4()) + extn # store to disk. # This should be abstracted, so it can be overloaded to store in other manners (eg AWS) release_file = release_file + "/" + cname # Ensure the directory exists os.makedirs(os.path.dirname(release_file), exist_ok=True) with open(release_file, "w+b") as handle: handle.write(file_info["body"]) except Exception as e: # TODO: exception handling self.log.warning(f"Error: {e}") # TODO: improve error message self.log.info(f"Upload failed") # error 500?? raise web.HTTPError(418) # Check the file exists on disk if not (os.path.exists(release_file) and os.access(release_file, os.R_OK) and os.path.getsize(release_file) > 0): note = "File upload failed." self.log.info(note) self.finish({"success": False, "note": note}) return # We shouldn't need this, but it's good to double-check if os.path.getsize(release_file) > self.max_buffer_size: os.remove(release_file) note = "File upload oversize, and rejected. Please reduce the files in your submission and try again." self.log.info(note) self.finish({"success": False, "note": note}) return # now commit the assignment, and get it back to find the id assignment = Assignment.find_by_code(db=session, code=assignment_code, course_id=course.id) # Record the action. # Note we record the path to the files. self.log.info( f"Adding action {AssignmentActions.submitted.value} for user {this_user['id']} against assignment {assignment.id}" ) action = Action( user_id=this_user["id"], assignment_id=assignment.id, action=AssignmentActions.submitted, location=release_file, ) session.add(action) self.finish({"success": True, "note": "Submitted"})
def get(self): [course_code, assignment_code, path] = self.get_params(["course_id", "assignment_id", "path"]) if not (course_code and assignment_code and path): note = "Collection call requires a course code, an assignment code, and a path" self.log.info(note) self.finish({"success": False, "note": note}) return # Who is my user? this_user = self.nbex_user self.log.debug(f"User: {this_user.get('name')}") # For what course do we want to see the assignments? self.log.debug(f"Course: {course_code}") # Is our user subscribed to this course? if course_code not in this_user["courses"]: note = f"User not subscribed to course {course_code}" self.log.info(note) self.finish({"success": False, "note": note}) return self.log.info(f"user: {this_user}") if not "instructor" == this_user["current_role"].casefold( ): # we may need to revisit this note = f"User not an instructor to course {course_code}" self.log.info(note) self.finish({"success": False, "note": note}) return # Find the course being referred to with scoped_session() as session: course = Course.find_by_code(db=session, code=course_code, org_id=this_user["org_id"], log=self.log) if not course: note = f"Course {course_code} does not exist" self.log.info(note) self.finish({"success": False, "note": note}) return # We need to key off the assignment, but we're actually looking # for the action with a action and a specific path assignments = AssignmentModel.find_for_course( db=session, course_id=course.id, log=self.log, action=AssignmentActions.submitted.value, path=path, ) self.set_header("Content-Type", "application/gzip") # I do not want to assume there will just be one. for assignment in assignments: self.log.debug(f"Assignment: {assignment}") try: with open(path, "r+b") as handle: data = handle.read() except Exception as e: # TODO: exception handling self.log.warning( f"Error: {e}") # TODO: improve error message # error 500?? raise Exception self.log.info( f"Adding action {AssignmentActions.collected.value} for user {this_user['id']} against assignment {assignment.id}" # noqa: E501 ) action = Action( user_id=this_user["id"], assignment_id=assignment.id, action=AssignmentActions.collected, location=path, ) session.add(action) self.finish(data) return
def test_action_relationships(db, user_johaannes): found_by_pk = Action.find_by_pk(db, 1) assert found_by_pk.user.name == user_johaannes.name assert found_by_pk.assignment.assignment_code == "tree 1" assert found_by_pk.assignment.course.course_code == "Strange"