def select_rubric_item(submission_id: int, rubricitem_id: int) -> EmptyResponse: """Select a rubric item of the given submission (:class:`.models.Work`). .. :quickref: Submission; Select a rubric item. :param int submission_id: The id of the submission :param int rubricitem_id: The id of the rubric item :returns: Nothing. :raises APIException: If either the submission or rubric item with the given ids does not exist. (OBJECT_ID_NOT_FOUND) :raises APIException: If the assignment of the rubric is not the assignment of the submission. (INVALID_PARAM) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not grade the given submission (INCORRECT_PERMISSION) """ work = helpers.get_or_404(models.Work, submission_id) rubric_item = helpers.get_or_404(models.RubricItem, rubricitem_id) auth.ensure_permission('can_grade_work', work.assignment.course_id) if rubric_item.rubricrow.assignment_id != work.assignment_id: raise APIException( 'Rubric item selected does not match assignment', 'The rubric item with id {} does not match the assignment'.format( rubricitem_id), APICodes.INVALID_PARAM, 400) work.remove_selected_rubric_item(rubric_item.rubricrow_id) work.select_rubric_items([rubric_item], current_user, False) db.session.commit() return make_empty_response()
def delete_course_snippets(course_id: int, snippet_id: int) -> EmptyResponse: """Delete the :class:`.models.CourseSnippet` with the given id. .. :quickref: CourseSnippet; Delete a course snippet. :param int snippet_id: The id of the snippet :returns: An empty response with return code 204 :raises APIException: If the snippet with the given id does not exist. (OBJECT_ID_NOT_FOUND) :raises APIException: If the snippet does not belong the current user. (INCORRECT_PERMISSION) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not use snippets. (INCORRECT_PERMISSION) """ auth.ensure_permission(CPerm.can_manage_course_snippets, course_id) course = helpers.get_or_404(models.Course, course_id) snip = helpers.get_or_404( models.CourseSnippet, snippet_id, also_error=lambda snip: snip.course_id != course.id) db.session.delete(snip) db.session.commit() return make_empty_response()
def patch_course_snippet(course_id: int, snippet_id: int) -> EmptyResponse: """Modify the :class:`.models.CourseSnippet` with the given id. .. :quickref: CourseSnippet; Change a snippets key and value. :param int snippet_id: The id of the snippet to change. :returns: An empty response with return code 204. :>json str key: The new key of the snippet. :>json str value: The new value of the snippet. :raises APIException: If the parameters "key" and/or "value" were not in the request. (MISSING_REQUIRED_PARAM) :raises APIException: If the snippet does not belong to the current user. (INCORRECT_PERMISSION) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not use snippets. (INCORRECT_PERMISSION) :raises APIException: If another snippet with the same key already exists. (OBJECT_ALREADY_EXISTS) """ with helpers.get_from_request_transaction() as [get, _]: value = get('value', str) key = get('key', str) course = helpers.get_or_404(models.Course, course_id, with_for_update=True, with_for_update_of=models.Course) auth.CoursePermissions(course).ensure_may_edit_snippets() snip = helpers.get_or_404( models.CourseSnippet, snippet_id, also_error=lambda snip: snip.course_id != course.id) other = models.CourseSnippet.query.filter_by( course=course, key=key, ).one_or_none() if other is not None and other.id != snippet_id: raise APIException( 'A snippet with the same key already exists.', 'A snippet with key "{}" already exists for course "{}"'.format( key, course_id), APICodes.OBJECT_ALREADY_EXISTS, 400, ) snip.key = key snip.value = value db.session.commit() return make_empty_response()
def create_or_edit_registration_link( course_id: int) -> JSONResponse[models.CourseRegistrationLink]: """Create or edit a registration link. .. :quickref: Course; Create or edit a registration link for a course. :param course_id: The id of the course in which this link should enroll users. :>json id: The id of the link to edit, omit to create a new link. :>json role_id: The id of the role that users should get when registering with this link. :>json expiration_date: The date this link should stop working, this date should be in ISO8061 format without any timezone information, as it will be interpret as a UTC date. :returns: The created or edited link. """ course = helpers.get_or_404(models.Course, course_id, also_error=lambda c: c.virtual) auth.ensure_permission(CPerm.can_edit_course_users, course_id) with get_from_map_transaction( get_json_dict_from_request()) as [get, opt_get]: expiration_date = get('expiration_date', str) role_id = get('role_id', int) link_id = opt_get('id', str, default=None) if link_id is None: link = models.CourseRegistrationLink(course=course) db.session.add(link) else: link = helpers.filter_single_or_404( models.CourseRegistrationLink, models.CourseRegistrationLink.id == uuid.UUID(link_id), also_error=lambda l: l.course_id != course.id) link.course_role = helpers.get_or_404( models.CourseRole, role_id, also_error=lambda r: r.course_id != course.id) link.expiration_date = parsers.parse_datetime(expiration_date) if link.expiration_date < helpers.get_request_start_time(): helpers.add_warning('The link has already expired.', APIWarnings.ALREADY_EXPIRED) if link.course_role.has_permission(CPerm.can_edit_course_roles): helpers.add_warning( ('Users that register with this link will have the permission' ' to give themselves more permissions.'), APIWarnings.DANGEROUS_ROLE) db.session.commit() return jsonify(link)
def patch_course_snippet(course_id: int, snippet_id: int) -> EmptyResponse: """Modify the :class:`.models.CourseSnippet` with the given id. .. :quickref: CourseSnippet; Change a snippets key and value. :param int snippet_id: The id of the snippet to change. :returns: An empty response with return code 204. :<json str key: The new key of the snippet. :<json str value: The new value of the snippet. :raises APIException: If the parameters "key" and/or "value" were not in the request. (MISSING_REQUIRED_PARAM) :raises APIException: If the snippet does not belong to the current user. (INCORRECT_PERMISSION) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not use snippets. (INCORRECT_PERMISSION) :raises APIException: If another snippet with the same key already exists. (OBJECT_ALREADY_EXISTS) """ auth.ensure_permission(CPerm.can_manage_course_snippets, course_id) content = get_json_dict_from_request() ensure_keys_in_dict(content, [('key', str), ('value', str)]) key = t.cast(str, content['key']) value = t.cast(str, content['value']) course = helpers.get_or_404(models.Course, course_id) snip = helpers.get_or_404( models.CourseSnippet, snippet_id, also_error=lambda snip: snip.course_id != course.id) other = models.CourseSnippet.query.filter_by( course=course, key=key, ).first() if other is not None and other.id != snippet_id: raise APIException( 'A snippet with the same key already exists.', 'A snippet with key "{}" already exists for course "{}"'.format( key, course_id), APICodes.OBJECT_ALREADY_EXISTS, 400, ) snip.key = key snip.value = value db.session.commit() return make_empty_response()
def delete_submission(submission_id: int) -> EmptyResponse: """Delete a submission and all its files. .. :quickref: Submission; Delete a submission and all its files. .. warning:: This is irreversible, so make sure the user really wants this! :param submission_id: The submission to delete. :returns: Nothing """ submission = helpers.get_or_404(models.Work, submission_id) auth.ensure_permission('can_delete_submission', submission.assignment.course_id) for sub_file in db.session.query(models.File).filter_by( work_id=submission_id, is_directory=False).all(): try: sub_file.delete_from_disk() except FileNotFoundError: # pragma: no cover pass db.session.delete(submission) db.session.commit() return make_empty_response()
def delete_snippets(snippet_id: int) -> EmptyResponse: """Delete the :class:`.models.Snippet` with the given id. .. :quickref: Snippet; Delete a snippet. :param int snippet_id: The id of the snippet :returns: An empty response with return code 204 :raises APIException: If the snippet with the given id does not exist. (OBJECT_ID_NOT_FOUND) :raises APIException: If the snippet does not belong the current user. (INCORRECT_PERMISSION) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not use snippets. (INCORRECT_PERMISSION) """ snip: t.Optional[models.Snippet] snip = helpers.get_or_404(models.Snippet, snippet_id) snip = models.Snippet.query.get(snippet_id) assert snip is not None if snip.user_id != current_user.id: raise APIException( 'The given snippet is not your snippet', 'The snippet "{}" does not belong to user "{}"'.format( snip.id, current_user.id), APICodes.INCORRECT_PERMISSION, 403) else: db.session.delete(snip) db.session.commit() return make_empty_response()
def delete_rubric(assignment_id: int) -> EmptyResponse: """Delete the rubric for the given assignment. .. :quickref: Assignment; Delete the rubric of an assignment. :param assignment_id: The id of the :class:`.models.Assignment` whose rubric should be deleted. :returns: Nothing. :raises PermissionException: If the user does not have the ``manage_rubrics`` permission (INCORRECT_PERMISSION). :raises APIException: If the assignment has no rubric. (OBJECT_ID_NOT_FOUND) """ assig = helpers.get_or_404(models.Assignment, assignment_id) auth.ensure_permission('manage_rubrics', assig.course_id) if not assig.rubric_rows: raise APIException( 'Assignment has no rubric', 'The assignment with id "{}" has no rubric'.format(assignment_id), APICodes.OBJECT_ID_NOT_FOUND, 404 ) assig.rubric_rows = [] db.session.commit() return make_empty_response()
def get_assignment_rubric(assignment_id: int ) -> JSONResponse[t.Sequence[models.RubricRow]]: """Return the rubric corresponding to the given `assignment_id`. .. :quickref: Assignment; Get the rubric of an assignment. :param int assignment_id: The id of the assignment :returns: A list of JSON of :class:`.models.RubricRows` items :raises APIException: If no assignment with given id exists. (OBJECT_ID_NOT_FOUND) :raises APIException: If the assignment has no rubric. (OBJECT_ID_NOT_FOUND) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user is not allowed to see this is assignment. (INCORRECT_PERMISSION) """ assig = helpers.get_or_404(models.Assignment, assignment_id) auth.ensure_permission('can_see_assignments', assig.course_id) if not assig.rubric_rows: raise APIException( 'Assignment has no rubric', 'The assignment with id "{}" has no rubric'.format(assignment_id), APICodes.OBJECT_ID_NOT_FOUND, 404 ) return jsonify(assig.rubric_rows)
def unselect_rubric_item(submission_id: int, rubric_item_id: int) -> EmptyResponse: """Unselect the given rubric item for the given submission. .. :quickref: Submission; Unselect the given rubric item. :param submission_id: The submission to unselect the item for. :param rubric_item_id: The rubric items id to unselect. :returns: Nothing. """ submission = helpers.get_or_404(models.Work, submission_id) auth.ensure_permission('can_grade_work', submission.assignment.course_id) new_items = [ item for item in submission.selected_items if item.id != rubric_item_id ] if len(new_items) == len(submission.selected_items): raise APIException( 'Selected rubric item was not selected for this submission', f'The item {rubric_item_id} is not selected for {submission_id}', APICodes.INVALID_PARAM, 400) submission.selected_items = new_items db.session.commit() return make_empty_response()
def get_all_course_roles( course_id: int ) -> t.Union[JSONResponse[t.List[models.CourseRole]], JSONResponse[t.List[models.CourseRole.AsJSONWithPerms]], ]: """Get a list of all :class:`.models.CourseRole` objects of a given :class:`.models.Course`. .. :quickref: Course; Get all course roles for a single course. :param int course_id: The id of the course to get the roles for. :returns: An array of all course roles for the given course. :>jsonarr perms: All permissions this role has as returned by :py:meth:`.models.CourseRole.get_all_permissions`. :>jsonarrtype perms: :py:class:`t.Mapping[str, bool]` :>jsonarr bool own: True if the current course role is the current users course role. :>jsonarr ``**rest``: The course role as returned by :py:meth:`.models.CourseRole.__to_json__` :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not manage the course with the given id. (INCORRECT_PERMISSION) """ course = helpers.get_or_404(models.Course, course_id) auth.CoursePermissions(course).ensure_may_see_roles() course_roles = models.CourseRole.query.filter( models.CourseRole.course == course, ~models.CourseRole.hidden).order_by(models.CourseRole.name).all() if request.args.get('with_roles') == 'true': res = [r.__to_json_with_perms__() for r in course_roles] return jsonify(res) return jsonify(course_roles)
def create_new_assignment(course_id: int) -> JSONResponse[models.Assignment]: """Create a new course for the given assignment. .. :quickref: Course; Create a new assignment in a course. :param int course_id: The course to create an assignment in. :<json str name: The name of the new assignment. :returns: The newly created assignment. """ with get_from_map_transaction(get_json_dict_from_request()) as [get, _]: name = get('name', str) course = helpers.get_or_404( models.Course, course_id, also_error=lambda c: c.virtual, ) assig = models.Assignment( name=name, course=course, is_lti=False, ) auth.AssignmentPermissions(assig).ensure_may_add() db.session.add(assig) db.session.commit() return jsonify(assig)
def get_course_by_id(course_id: int) -> ExtendedJSONResponse[models.Course]: """Return course data for a given :class:`.models.Course`. .. :quickref: Course; Get data for a given course. :param int course_id: The id of the course :returns: A response containing the JSON serialized course :>json str role: The name of the role the current user has in this course. :>json ``**rest``: JSON serialization of :py:class:`psef.models.Course`. :raises APIException: If there is no course with the given id. (OBJECT_ID_NOT_FOUND) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) """ course = helpers.get_or_404(models.Course, course_id) auth.CoursePermissions(course).ensure_may_see() if not helpers.request_arg_true('no_role_name'): helpers.add_deprecate_warning( 'Getting the role of the current user in the requested course is' ' deprecated and will be removed in the next major version of' ' CodeGrade') helpers.jsonify_options.get_options().add_role_to_course = True return ExtendedJSONResponse.make(course, use_extended=models.Course)
def create_new_assignment(course_id: int) -> JSONResponse[models.Assignment]: """Create a new course for the given assignment. .. :quickref: Course; Create a new assignment in a course. :param int course_id: The course to create an assignment in. :<json str name: The name of the new assignment. :returns: The newly created assignment. :raises PermissionException: If the current user does not have the ``can_create_assignment`` permission (INCORRECT_PERMISSION). """ auth.ensure_permission('can_create_assignment', course_id) content = ensure_json_dict(request.get_json()) ensure_keys_in_dict(content, [('name', str)]) name = t.cast(str, content['name']) course = helpers.get_or_404(models.Course, course_id) if course.lti_course_id is not None: raise APIException('You cannot add assignments to a LTI course', f'The course "{course_id}" is a LTI course', APICodes.INVALID_STATE, 400) assig = models.Assignment(name=name, course=course, deadline=datetime.datetime.utcnow()) db.session.add(assig) db.session.commit() return jsonify(assig)
def get_all_course_assignments( course_id: int) -> JSONResponse[t.Sequence[models.Assignment]]: """Get all :class:`.models.Assignment` objects of the given :class:`.models.Course`. .. :quickref: Course; Get all assignments for single course. The returned assignments are sorted by deadline. :param int course_id: The id of the course :returns: A response containing the JSON serialized assignments sorted by deadline of the assignment. See :py:func:`.models.Assignment.__to_json__` for the way assignments are given. :raises APIException: If there is no course with the given id. (OBJECT_ID_NOT_FOUND) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not see assignments in the given course. (INCORRECT_PERMISSION) """ auth.ensure_permission(CPerm.can_see_assignments, course_id) course = helpers.get_or_404( models.Course, course_id, also_error=lambda c: c.virtual, ) return jsonify(course.get_all_visible_assignments())
def delete_role(course_id: int, role_id: int) -> EmptyResponse: """Remove a :class:`.models.CourseRole` from the given :class:`.models.Course`. .. :quickref: Course; Delete a course role from a course. :param int course_id: The id of the course :returns: An empty response with return code 204 :raises APIException: If the role with the given ids does not exist. (OBJECT_NOT_FOUND) :raises APIException: If there are still users with this role. (INVALID_PARAM) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not manage the course with the given id. (INCORRECT_PERMISSION) """ auth.ensure_permission(CPerm.can_edit_course_roles, course_id) course = helpers.get_or_404( models.Course, course_id, also_error=lambda c: c.virtual, ) role = helpers.filter_single_or_404( models.CourseRole, models.CourseRole.course_id == course_id, models.CourseRole.id == role_id, also_error=lambda r: r.hidden, ) if course.lti_provider is not None: if LTICourseRole.codegrade_role_name_used(role.name): lms = course.lti_provider.lms_name raise APIException( f'You cannot delete default {lms} roles', ('The course "{}" is an LTI course so it is impossible to ' 'delete role {}').format(course.id, role.id), APICodes.INCORRECT_PERMISSION, 403) users_with_role = db.session.query(models.user_course).filter( models.user_course.c.course_id == role_id).exists() if db.session.query(users_with_role).scalar(): raise APIException( 'There are still users with this role', 'There are still users with role {}'.format(role_id), APICodes.INVALID_PARAM, 400) links_with_role = db.session.query( models.CourseRegistrationLink).filter_by( course_role_id=role_id).exists() if db.session.query(links_with_role).scalar(): raise APIException( 'There are still registration links with this role', f'The role "{role_id}" cannot be deleted as it is still in use', APICodes.INVALID_PARAM, 400) db.session.delete(role) db.session.commit() return make_empty_response()
def user_patch_handle_reset_password() -> JSONResponse[t.Mapping[str, str]]: """Handle the ``reset_password`` type for the PATCH login route. :returns: A response with a jsonified mapping between ``access_token`` and a token which can be used to login. This is only key available. """ data = ensure_json_dict(request.get_json()) ensure_keys_in_dict(data, [('new_password', str), ('token', str), ('user_id', int)]) password = t.cast(str, data['new_password']) user_id = t.cast(int, data['user_id']) token = t.cast(str, data['token']) if password == '': raise APIException('Password should at least be 1 char', f'The password is {len(password)} chars long', APICodes.INVALID_PARAM, 400) user = helpers.get_or_404(models.User, user_id) user.reset_password(token, password) db.session.commit() return jsonify({ 'access_token': flask_jwt.create_access_token( identity=user.id, fresh=True, ) })
def user_patch_handle_reset_password() -> JSONResponse[t.Mapping[str, str]]: """Handle the ``reset_password`` type for the PATCH login route. :returns: A response with a jsonified mapping between ``access_token`` and a token which can be used to login. This is only key available. """ data = ensure_json_dict( request.get_json(), replace_log=lambda k, v: '<PASSWORD>' if 'password' in k else v ) ensure_keys_in_dict( data, [('new_password', str), ('token', str), ('user_id', int)] ) password = t.cast(str, data['new_password']) user_id = t.cast(int, data['user_id']) token = t.cast(str, data['token']) user = helpers.get_or_404(models.User, user_id) validate.ensure_valid_password(password, user=user) user.reset_password(token, password) db.session.commit() return jsonify( { 'access_token': flask_jwt.create_access_token( identity=user.id, fresh=True, ) } )
def select_rubric_items(submission_id: int, ) -> EmptyResponse: """Select the given rubric items for the given submission. .. :quickref: Submission; Select multiple rubric items. :param submission_id: The submission to unselect the item for. :>json array items: The ids of the rubric items you want to select. :returns: Nothing. :raises APIException: If the assignment of a given item does not belong to the assignment of the given submission. of the submission (INVALID_PARAM). :raises PermissionException: If the current user cannot grace work (INCORRECT_PERMISSION). """ submission = helpers.get_or_404(models.Work, submission_id) auth.ensure_permission('can_grade_work', submission.assignment.course_id) content = ensure_json_dict(request.get_json()) ensure_keys_in_dict(content, [('items', list)]) item_ids = t.cast(list, content['items']) items = [] for item_id in item_ids: items.append(helpers.get_or_404(models.RubricItem, item_id)) if any(item.rubricrow.assignment_id != submission.assignment_id for item in items): raise APIException( 'Selected rubric item is not coupled to the given submission', f'A given item of "{", ".join(str(i) for i in item_ids)}"' f' does not belong to assignment "{submission.assignment_id}"', APICodes.INVALID_PARAM, 400) submission.select_rubric_items(items, current_user, True) db.session.commit() return make_empty_response()
def delete_registration_link(course_id: int, link_id: uuid.UUID) -> EmptyResponse: """Delete the given registration link. .. :quickref: Course; The delete a registration link of the given course. :param course_id: The id of the course to which the registration link is connected. :param link_id: The id of the registration link. :returns: Nothing. """ course = helpers.get_or_404(models.Course, course_id, also_error=lambda c: c.virtual) auth.ensure_permission(CPerm.can_edit_course_users, course_id) link = helpers.get_or_404(models.CourseRegistrationLink, link_id, also_error=lambda l: l.course_id != course.id) db.session.delete(link) db.session.commit() return make_empty_response()
def update_course(course_id: int) -> ExtendedJSONResponse[models.Course]: """Update the given :class:`.models.Course` with new values. .. :quickref: Course; Update course data. :param int course_id: The id of the course you want to update. :returns: The updated course, in extended format. """ data = rqa.FixedMapping( rqa.OptionalArgument( 'name', rqa.SimpleValue.str, 'The new name of the course', ), rqa.OptionalArgument( 'state', rqa.EnumValue(models.CourseState), """ The new state of the course, currently you cannot set the state of a course to 'deleted' """, )).from_flask() course = helpers.get_or_404(models.Course, course_id) checker = auth.CoursePermissions(course) checker.ensure_may_see() if data.name.is_just: if course.is_lti: raise APIException( 'You cannot rename LTI courses', ('LTI courses get their name from the LMS, so renaming is' ' not possible'), APICodes.INVALID_PARAM, 400) if not data.name.value: raise APIException( 'The name of a course should contain at least one character', 'A course name cannot be empty', APICodes.INVALID_PARAM, 400) checker.ensure_may_edit_info() course.name = data.name.value if data.state.is_just: if data.state.value.is_deleted: raise APIException( 'It is not yet possible to delete a course', 'Deleting courses in the API is not yet possible', APICodes.INVALID_PARAM, 400) checker.ensure_may_edit_state() course.state = data.state.value db.session.commit() return ExtendedJSONResponse.make(course, use_extended=models.Course)
def get_group_sets( course_id: int) -> JSONResponse[t.Sequence[models.GroupSet]]: """Get the all the :class:`.models.GroupSet` objects in the given course. .. :quickref: Course; Get all group sets in the course. :param int course_id: The id of the course of which the group sets should be retrieved. :returns: A list of group sets. """ course = helpers.get_or_404(models.Course, course_id) auth.ensure_enrolled(course.id) return jsonify(course.group_sets)
def get_group_sets( course_id: int) -> JSONResponse[t.Sequence[models.GroupSet]]: """Get the all the group sets of a given course. .. :quickref: Course; Get all group sets in the course. :param int course_id: The id of the course of which the group sets should be retrieved. :returns: A list of group sets. """ course = helpers.get_or_404(models.Course, course_id) auth.CoursePermissions(course).ensure_may_see() return jsonify(course.group_sets)
def put_comment(code_id: int, line: int) -> EmptyResponse: """Create or change a single :class:`.models.Comment` of a code :class:`.models.File`. .. :quickref: Code; Add or change a comment. :param int code_id: The id of the code file :param int line: The line number of the comment :returns: An empty response with return code 204 :<json str comment: The comment to add to the given file on the given line. :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not can grade work in the attached course. (INCORRECT_PERMISSION) """ comment = db.session.query(models.Comment).filter( models.Comment.file_id == code_id, models.Comment.line == line ).one_or_none() def __get_comment() -> str: content = ensure_json_dict(request.get_json()) ensure_keys_in_dict(content, [('comment', str)]) return t.cast(str, content['comment']) if comment: auth.ensure_permission( 'can_grade_work', comment.file.work.assignment.course_id ) comment.comment = __get_comment() else: file = helpers.get_or_404(models.File, code_id) auth.ensure_permission( 'can_grade_work', file.work.assignment.course_id, ) db.session.add( models.Comment( file_id=code_id, user_id=current_user.id, line=line, comment=__get_comment(), ) ) db.session.commit() return make_empty_response()
def get_permissions_for_course( course_id: int, ) -> JSONResponse[t.Mapping[str, bool]]: """Get all the course :class:`.models.Permission` of the currently logged in :class:`.models.User` .. :quickref: Course; Get all the course permissions for the current user. :param int course_id: The id of the course of which the permissions should be retrieved. :returns: A mapping between the permission name and a boolean indicating if the currently logged in user has this permission. """ course = helpers.get_or_404(models.Course, course_id) return jsonify(current_user.get_all_permissions(course))
def _get_non_expired_link(course_id: int, link_id: uuid.UUID) -> models.CourseRegistrationLink: link = helpers.get_or_404( models.CourseRegistrationLink, link_id, also_error=lambda l: l.course_id != course_id or l.course.is_lti, ) if link.expiration_date < helpers.get_request_start_time(): raise APIException('This registration link has expired.', f'The registration link {link.id} has expired', APICodes.OBJECT_EXPIRED, 409) return link
def update_role(course_id: int, role_id: int) -> EmptyResponse: """Update the :class:`.models.Permission` of a given :class:`.models.CourseRole` in the given :class:`.models.Course`. .. :quickref: Course; Update a permission for a certain role. :param int course_id: The id of the course. :param int role_id: The id of the course role. :returns: An empty response with return code 204. :<json str permission: The name of the permission to change. :<json bool value: The value to set the permission to (``True`` means the specified role has the specified permission). :raises APIException: If the value or permission parameter are not in the request. (MISSING_REQUIRED_PARAM) :raises APIException: If the role with the given id does not exist or the permission with the given name does not exist. (OBJECT_NOT_FOUND) :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the user can not manage the course with the given id. (INCORRECT_PERMISSION) """ course = helpers.get_or_404(models.Course, course_id) auth.CoursePermissions(course).ensure_may_edit_roles() with helpers.get_from_request_transaction() as [get, _]: value = get('value', bool) permission = get('permission', CPerm) role = helpers.filter_single_or_404( models.CourseRole, models.CourseRole.course == course, models.CourseRole.id == role_id, also_error=lambda r: r.hidden, ) if (current_user.courses[course_id].id == role.id and permission == CPerm.can_edit_course_roles): raise APIException( 'You cannot remove this permission from your own role', ('The current user is in role {} which' ' cannot remove "can_edit_course_roles"').format(role.id), APICodes.INCORRECT_PERMISSION, 403) role.set_permission(permission, value) db.session.commit() return make_empty_response()
def update_submission_grader(submission_id: int) -> EmptyResponse: """Change the assigned grader of the given submission. .. :quickref: Submission; Update grader for the submission. :returns: Empty response and a 204 status. :>json int user_id: Id of the new grader. This is a required parameter. :raises PermissionException: If the logged in user cannot manage the course of the submission. (INCORRECT_PERMISSION) :raises APIException: If the new grader does not have the correct permission to grade this submission. (INCORRECT_PERMISSION) """ work = helpers.get_or_404(models.Work, submission_id) content = ensure_json_dict(request.get_json()) ensure_keys_in_dict(content, [('user_id', int)]) user_id = t.cast(int, content['user_id']) auth.ensure_permission('can_assign_graders', work.assignment.course_id) grader = helpers.get_or_404(models.User, user_id) if not grader.has_permission('can_grade_work', work.assignment.course_id): raise APIException( f'User "{grader.name}" doesn\'t have the required permission', f'User "{grader.name}" doesn\'t have permission "can_grade_work"', APICodes.INCORRECT_PERMISSION, 400) work.assignee = grader work.assignment.set_graders_to_not_done( [grader.id], send_mail=grader.id != current_user.id, ignore_errors=True, ) db.session.commit() return make_empty_response()
def get_all_works_for_assignment( assignment_id: int ) -> t.Union[JSONResponse[WorkList], ExtendedJSONResponse[WorkList]]: """Return all :class:`.models.Work` objects for the given :class:`.models.Assignment`. .. :quickref: Assignment; Get all works for an assignment. :qparam boolean extended: Whether to get extended or normal :class:`.models.Work` objects. The default value is ``false``, you can enable extended by passing ``true``, ``1`` or an empty string. :param int assignment_id: The id of the assignment :returns: A response containing the JSON serialized submissions. :raises PermissionException: If there is no logged in user. (NOT_LOGGED_IN) :raises PermissionException: If the assignment is hidden and the user is not allowed to view it. (INCORRECT_PERMISSION) """ assignment = helpers.get_or_404(models.Assignment, assignment_id) auth.ensure_permission('can_see_assignments', assignment.course_id) if assignment.is_hidden: auth.ensure_permission( 'can_see_hidden_assignments', assignment.course_id ) obj = models.Work.query.filter_by( assignment_id=assignment_id, ).options(joinedload( models.Work.selected_items, )).order_by(t.cast(t.Any, models.Work.created_at).desc()) if not current_user.has_permission( 'can_see_others_work', course_id=assignment.course_id ): obj = obj.filter_by(user_id=current_user.id) extended = request.args.get('extended', 'false').lower() if extended in {'true', '1', ''}: obj = obj.options(undefer(models.Work.comment)) return extended_jsonify( obj.all(), use_extended=lambda obj: isinstance(obj, models.Work), ) else: return jsonify(obj.all())
def get_registration_links( course_id: int ) -> JSONResponse[t.Sequence[models.CourseRegistrationLink]]: """Get the registration links for the given course. .. :quickref: Course; Get the registration links for this course. :param course_id: The course id for which to get the registration links. :returns: An array of registration links. """ course = helpers.get_or_404(models.Course, course_id, also_error=lambda c: c.virtual) auth.ensure_permission(CPerm.can_edit_course_users, course_id) return jsonify(course.registration_links)