Loading portal/rest/courses/courses.py +9 −7 Original line number Diff line number Diff line Loading @@ -20,14 +20,14 @@ class CourseResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") return course_schema.dump(course) def delete(self, cid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") delete_entity(course) return '', 204 Loading @@ -35,7 +35,7 @@ class CourseResource(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") json_data = request.get_json() if not json_data: Loading @@ -50,7 +50,7 @@ class CourseResource(Resource): course.codename = data['codename'] # relationships are written elsewhere write_entity(course) log.debug(f"Course update successful. Course={course}") log.debug(f"Updated course {cid} to {course}.") return '', 204 Loading @@ -64,7 +64,8 @@ class CourseList(Resource): log.info(f"POST to {request.url}") json_data = request.get_json() if not json_data: abort(400, message='No data provided for course update.') abort(400, message='No data provided for course creation.') try: data = course_schema.load(json_data) except ValidationError as err: Loading @@ -73,6 +74,7 @@ class CourseList(Resource): name = data[0]['name'] codename = data[0]['codename'] new_course = Course(name=name, codename=codename) write_entity(new_course) log.debug(f"Created course={new_course}") return course_schema.dump(new_course)[0], 201 Loading @@ -82,13 +84,13 @@ class CourseNotesToken(Resource): def get(self, cid): course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") return course.notes_access_token def put(self, cid): course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") json_data = request.get_json() if not json_data: Loading portal/rest/projects/projects.py +39 −31 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ from flask import Blueprint, request from flask_restful import Api, Resource, abort from marshmallow import ValidationError from flask_jwt_extended import JWTManager, jwt_required, create_access_token, get_jwt_identity from datetime import timedelta from portal.rest import ProjectSchema, ProjectConfigSchema, SubmissionSchema,\ delete_entity, write_entity, find_course, find_project, find_user Loading @@ -26,20 +27,22 @@ class ProjectResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not find project {pid}: course with identifier {cid} not found.") role = find_project(course, pid) if not role: abort(404, message=f"Could not find project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not find project {pid} in course {cid}: project not found.") return project_schema.dump(role) return project_schema.dump(project) def delete(self, cid, pid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not delete project {pid}: course with identifier {cid} not found.") abort(404, message=f"Could not delete project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not delete project {pid} in course {cid}: project not found.") delete_entity(project) return '', 204 Loading @@ -47,7 +50,7 @@ class ProjectResource(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update project {pid}: course with identifier {cid} not found.") abort(404, message=f"Could not update project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not update project {pid} in course {cid}: project not found.") Loading @@ -63,7 +66,7 @@ class ProjectResource(Resource): project.name = data['name'] write_entity(project) log.debug(f"Project update successful. Project={project}") log.debug(f"Updated project {pid} to {project}.") return '', 204 Loading @@ -72,7 +75,7 @@ class ProjectsList(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not list projects: course with identifier {cid} not found.") abort(404, message=f"Could not list projects in course {cid}: course not found.") return projects_schema.dump(course.projects) Loading @@ -80,10 +83,11 @@ class ProjectsList(Resource): log.info(f"POST to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not create project: Course with identifier {cid} not found.") abort(404, message=f"Could not create project in course {cid}: course not found.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for project creation.') try: data = project_schema.load(json_data)[0] except ValidationError as err: Loading @@ -101,20 +105,21 @@ class ProjectConfigResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not get project configuration: course with identifier {cid} not found.") abort(404, message=f"Could not get configuration of project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not get project configuration: project {pid} not found in course {cid}") abort(404, message=f"Could not get configuration of project {pid} in course {cid}: project not found") return config_schema.dump(project.config) def put(self, cid, pid): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update project configuration: course with identifier {cid} not found.") abort(404, message=f"Could not update configuration of project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not update project configuration: project {pid} not found in course {cid}.") abort(404, message=f"Could not update configuration of project {pid} in course {cid}: project not found.") json_data = request.get_json() if not json_data: Loading @@ -126,7 +131,7 @@ class ProjectConfigResource(Resource): return abort(422, errors=err.messages) # TODO: flash, process errors project.set_config(data) # TODO: check - missing **? in role permissions too log.debug(f"Updated configuration for project {pid} in course {cid}.") log.debug(f"Updated configuration for project {pid} in course {cid} to {data}.") return '', 204 Loading @@ -136,11 +141,11 @@ class ProjectSubmissions(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not list project submissions: course with identifier {cid} not found.") abort(404, message=f"Could not list submissions of project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not list project submissions: project {pid} not found in course {cid}.") abort(404, message=f"Could not list submissions of project {pid} in course {cid}: project not found.") # TODO: user=<user_selector>` - filters submissions by user return submissions_schema.dump(project.submissions) @jwt_required Loading @@ -153,19 +158,23 @@ class ProjectSubmissions(Resource): course = find_course(cid) if not course: abort(404, message=f"Could not update project configuration: course with identifier {cid} not found.") abort(404, message=f"Could not create a new submission for user {user.username} in project {pid} " f"in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not update project configuration: project {pid} not found in course {cid}.") abort(404, message=f"Could not create a new submission for user {user.username} in project {pid} " f"in course {cid}: project not found.") # check if a new submission can be created by this user in this project if not can_create_submission(user, project): # TODO: configure conflict_timeout in project if not can_create_submission(user, project, conflict_timeout=timedelta(minutes=30)): # maybe use 409, 403 abort(429, message=f"New submission denied: you have an unfinished submission for this project.") abort(429, message=f"New submission for user {user.username} denied: a submission for this user " f"is already being processed.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for project configuration update.') abort(400, message='No data provided for submission creation.') try: data = config_schema.load(json_data)[0] except ValidationError as err: Loading @@ -178,21 +187,20 @@ class ProjectSubmissions(Resource): review=data['review']) write_entity(new_submission) # download relevant files into Storage # TODO # TODO storage return submission_schema.dump(new_submission) class ProjectTestFiles(Resource): def get(self, cid, pid): # Gets test files (feature) return "Not implemented." projects_api.add_resource(ProjectResource, '/<string:pid>') projects_api.add_resource(ProjectsList, '/') projects_api.add_resource(ProjectConfigResource, '/<string:pid>/config') projects_api.add_resource(ProjectSubmissions, '/<string:pid>/submissions') ''' ### Project test files * `[GET] /courses/{cid/projects/{id}/files` - Gets test files (feature) ### Project submissions * `[GET] /courses/{cid}/projects/{id}/submissions` - Read all submissions for project * `user=<user_selector>` - filters submissions by user * `[POST] /courses/{cid}/projects/{id}/submissions` - Create submission''' projects_api.add_resource(ProjectTestFiles, '/<string:pid>/files') portal/rest/roles/roles.py +45 −28 Original line number Diff line number Diff line Loading @@ -22,20 +22,20 @@ class RoleResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not find role: course with identifier {cid} not found.") abort(404, message=f"Could not find role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not find role {rid} in course {cid}.") abort(404, message=f"Could not find role {rid} in course {cid}: role not found.") return role_schema.dump(role) def delete(self, cid, rid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not delete role: course with identifier {cid} not found.") abort(404, message=f"Could not delete role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not delete role {rid} in course {cid}.") abort(404, message=f"Could not delete role {rid} in course {cid}: role not found.") delete_entity(role) return '', 204 Loading @@ -43,10 +43,10 @@ class RoleResource(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not update role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not update role {rid} in course {cid}: role not found.") json_data = request.get_json() if not json_data: Loading @@ -61,7 +61,7 @@ class RoleResource(Resource): role.name = data['name'] # updating course? write_entity(role) log.debug(f"Role update successful. Role={role}") log.debug(f"Updated role {rid} to {role}.") return '', 204 Loading @@ -70,14 +70,14 @@ class RoleList(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not list roles: course with identifier {cid} not found.") abort(404, message=f"Could not list roles in course {cid}: course not found.") return roles_schema.dump(course.roles) def post(self, cid): log.info(f"POST to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not create role: Course with identifier {cid} not found.") abort(404, message=f"Could not create role in course {cid}: course not found.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for role creation.') Loading @@ -90,7 +90,7 @@ class RoleList(Resource): new_role = Role(course=course, name=data['name']) new_role.description = data.get('description') or "" write_entity(new_role) log.debug(f"Created role={new_role} for course {course.codename}") log.debug(f"Created role={new_role} for course {cid}") return role_schema.dump(new_role)[0], 201 Loading @@ -99,24 +99,24 @@ class RolePermissions(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not get role permissions: course with identifier {cid} not found.") abort(404, message=f"Could not list permissions for role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update permissions for role{rid} in course {cid}.") abort(404, message=f"Could not list permissions for role {rid} in course {cid}: role not found.") return roles_schema.dump(role.permissions) def put(self, cid, rid): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not update permissions for role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not update permissions for role {rid} in course {cid}: role not found.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for role update.') abort(400, message='No data provided for update of role permissions.') try: data = role_schema.load(json_data)[0] except ValidationError as err: Loading @@ -124,7 +124,7 @@ class RolePermissions(Resource): return abort(422, errors=err.messages) # TODO: flash, process errors role.set_permissions(data) log.debug(f"Updated permissions for role {rid} in course {cid}.") log.debug(f"Updated permissions for role {rid} in course {cid} to {data}.") return '', 204 Loading @@ -133,10 +133,10 @@ class RoleUsersList(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not list users with role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not list users with role {rid} in course {cid}: role not found.") return users_schema.dump(role.users) Loading @@ -144,44 +144,61 @@ class RoleUsersList(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not update users with role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not update users with role {rid} in course {cid}: role not found") json_data = request.get_json() try: # TODO full entities?, check duplicates data = users_schema.load(json_data)[0] # presumes the json contains full entities; change to ids? except ValidationError as err: log.info(f"Validation failed on: {err.messages}") return abort(422, errors=err.messages) # TODO: flash, process errors role.users = data log.info(f"Updated users of role {rid} to {data}") log.info(f"Updated users of role {rid} to {data}.") return '', 204 class RoleUsers(Resource): def put(self, cid, rid, uid): log.info(f"PUT to {request.url}") pass course = find_course(cid) if not course: abort(404, message=f"Could not add user {uid} to role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not add user {uid} to role {rid} in course {cid}: role not found.") user = find_user(uid) if not user: abort(404, message=f"Could not add user {uid} to role {rid} in course {cid}: user not found.") if user not in role.users: role.users.append(user) log.info(f"Added user {uid} to role {rid} in course {cid}.") else: log.info(f"User {uid} is already in role {rid} in course {cid}: no change.") return '', 204 def delete(self, cid, rid, uid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not delete user {uid} from role: course with identifier {cid} not found.") abort(404, message=f"Could not remove user {uid} from role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not delete user {uid} from role: role {rid} not found in course {cid}.") abort(404, message=f"Could not remove user {uid} from role {rid} in course {cid}: role not found.") user = find_user(uid) if not user: abort(404, message=f"Could not delete user {uid} from role{rid} in course {cid}: user not found.") abort(404, message=f"Could not remove user {uid} from role {rid} in course {cid}: user not found.") try: role.users.remove(user) except ValueError as err: # the user was not in the role log.info(f"Did not remove user {user} from role {rid} of course {cid}: user was not in the role. ") except ValueError: abort(400, message=f"Could not remove user {uid} from role {rid} in course {cid}: " f"role does not contain user.") log.info(f"Removed user {uid} from role {rid} in course {cid}.") return '', 204 Loading portal/rest/users/users.py +13 −14 Original line number Diff line number Diff line Loading @@ -2,11 +2,11 @@ from flask import Blueprint, request from flask_restful import Api, Resource, abort from marshmallow import ValidationError from portal.rest import UserSchema, delete_entity, write_entity from portal.rest import UserSchema, delete_entity, write_entity, find_user from portal.database.models import User from portal.tools.logging import log users = Blueprint('users', __name__, url_prefix='/users/') users = Blueprint('users', __name__, url_prefix='/users') users_api = Api(users) user_schema = UserSchema() Loading @@ -16,21 +16,20 @@ users_schema = UserSchema(many=True, only=('id', 'username', 'uco', 'email')) class UserResource(Resource): def get(self, uid): log.info(f"GET to {request.url}") user = User.query.filter_by(id=uid).first() user = find_user(uid) if not user: abort(404, message=f"User with id={uid} doesn't exist.") abort(404, message=f"Could not find user {uid}.") return user_schema.dump(user) def put(self, uid): log.info(f"PUT to {request.url}") user = User.query.filter_by(id=uid).first() user = find_user(uid) if not user: log.debug(f"Did not find course with id={uid}. Aborting request processing.") abort(404, message="User with id={} not found.".format(uid)) abort(404, message=f"Could not find user {uid}.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for course update.') abort(400, message='No data provided for user update.') try: data = user_schema.load(json_data) except ValidationError as err: Loading @@ -44,14 +43,14 @@ class UserResource(Resource): user.is_admin = data['is_admin'] # TODO all; care for password write_entity(user) log.debug(f"User update successful. User={user}") log.debug(f"Updated user {uid} to {user}.") return '', 204 def delete(self, uid): log.info(f"DELETE to {request.url}") user = User.query.filter_by(id=uid).first() user = find_user(uid) if not user: abort(404, message=f"User with id={uid} doesn't exist.") abort(404, message=f"Could not find user {uid}.") delete_entity(user) return '', 204 Loading @@ -66,7 +65,7 @@ class UserList(Resource): log.info(f"POST to {request.url}") json_data = request.get_json() if not json_data: abort(400, message='No data provided for user update.') abort(400, message='No data provided for user creation.') try: data = user_schema.load(json_data)[0] except ValidationError as err: Loading @@ -76,10 +75,10 @@ class UserList(Resource): admin = data.get('is_admin') or False new_user = User(uco=data['uco'], email=data['email'], username=data['username'], is_admin=admin) write_entity(new_user) log.debug(f"Created User={User}") log.debug(f"Created user={User}") return user_schema.dump(new_user)[0], 201 users_api.add_resource(UserResource, '/<string:id>') users_api.add_resource(UserResource, '/<string:uid>') users_api.add_resource(UserList, '/') Loading
portal/rest/courses/courses.py +9 −7 Original line number Diff line number Diff line Loading @@ -20,14 +20,14 @@ class CourseResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") return course_schema.dump(course) def delete(self, cid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") delete_entity(course) return '', 204 Loading @@ -35,7 +35,7 @@ class CourseResource(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") json_data = request.get_json() if not json_data: Loading @@ -50,7 +50,7 @@ class CourseResource(Resource): course.codename = data['codename'] # relationships are written elsewhere write_entity(course) log.debug(f"Course update successful. Course={course}") log.debug(f"Updated course {cid} to {course}.") return '', 204 Loading @@ -64,7 +64,8 @@ class CourseList(Resource): log.info(f"POST to {request.url}") json_data = request.get_json() if not json_data: abort(400, message='No data provided for course update.') abort(400, message='No data provided for course creation.') try: data = course_schema.load(json_data) except ValidationError as err: Loading @@ -73,6 +74,7 @@ class CourseList(Resource): name = data[0]['name'] codename = data[0]['codename'] new_course = Course(name=name, codename=codename) write_entity(new_course) log.debug(f"Created course={new_course}") return course_schema.dump(new_course)[0], 201 Loading @@ -82,13 +84,13 @@ class CourseNotesToken(Resource): def get(self, cid): course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") return course.notes_access_token def put(self, cid): course = find_course(cid) if not course: abort(404, message=f"Course with identifier {cid} not found.") abort(404, message=f"Could not find course {cid}.") json_data = request.get_json() if not json_data: Loading
portal/rest/projects/projects.py +39 −31 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ from flask import Blueprint, request from flask_restful import Api, Resource, abort from marshmallow import ValidationError from flask_jwt_extended import JWTManager, jwt_required, create_access_token, get_jwt_identity from datetime import timedelta from portal.rest import ProjectSchema, ProjectConfigSchema, SubmissionSchema,\ delete_entity, write_entity, find_course, find_project, find_user Loading @@ -26,20 +27,22 @@ class ProjectResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not find project {pid}: course with identifier {cid} not found.") role = find_project(course, pid) if not role: abort(404, message=f"Could not find project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not find project {pid} in course {cid}: project not found.") return project_schema.dump(role) return project_schema.dump(project) def delete(self, cid, pid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not delete project {pid}: course with identifier {cid} not found.") abort(404, message=f"Could not delete project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not delete project {pid} in course {cid}: project not found.") delete_entity(project) return '', 204 Loading @@ -47,7 +50,7 @@ class ProjectResource(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update project {pid}: course with identifier {cid} not found.") abort(404, message=f"Could not update project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not update project {pid} in course {cid}: project not found.") Loading @@ -63,7 +66,7 @@ class ProjectResource(Resource): project.name = data['name'] write_entity(project) log.debug(f"Project update successful. Project={project}") log.debug(f"Updated project {pid} to {project}.") return '', 204 Loading @@ -72,7 +75,7 @@ class ProjectsList(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not list projects: course with identifier {cid} not found.") abort(404, message=f"Could not list projects in course {cid}: course not found.") return projects_schema.dump(course.projects) Loading @@ -80,10 +83,11 @@ class ProjectsList(Resource): log.info(f"POST to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not create project: Course with identifier {cid} not found.") abort(404, message=f"Could not create project in course {cid}: course not found.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for project creation.') try: data = project_schema.load(json_data)[0] except ValidationError as err: Loading @@ -101,20 +105,21 @@ class ProjectConfigResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not get project configuration: course with identifier {cid} not found.") abort(404, message=f"Could not get configuration of project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not get project configuration: project {pid} not found in course {cid}") abort(404, message=f"Could not get configuration of project {pid} in course {cid}: project not found") return config_schema.dump(project.config) def put(self, cid, pid): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update project configuration: course with identifier {cid} not found.") abort(404, message=f"Could not update configuration of project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not update project configuration: project {pid} not found in course {cid}.") abort(404, message=f"Could not update configuration of project {pid} in course {cid}: project not found.") json_data = request.get_json() if not json_data: Loading @@ -126,7 +131,7 @@ class ProjectConfigResource(Resource): return abort(422, errors=err.messages) # TODO: flash, process errors project.set_config(data) # TODO: check - missing **? in role permissions too log.debug(f"Updated configuration for project {pid} in course {cid}.") log.debug(f"Updated configuration for project {pid} in course {cid} to {data}.") return '', 204 Loading @@ -136,11 +141,11 @@ class ProjectSubmissions(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not list project submissions: course with identifier {cid} not found.") abort(404, message=f"Could not list submissions of project {pid} in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not list project submissions: project {pid} not found in course {cid}.") abort(404, message=f"Could not list submissions of project {pid} in course {cid}: project not found.") # TODO: user=<user_selector>` - filters submissions by user return submissions_schema.dump(project.submissions) @jwt_required Loading @@ -153,19 +158,23 @@ class ProjectSubmissions(Resource): course = find_course(cid) if not course: abort(404, message=f"Could not update project configuration: course with identifier {cid} not found.") abort(404, message=f"Could not create a new submission for user {user.username} in project {pid} " f"in course {cid}: course not found.") project = find_project(course, pid) if not project: abort(404, message=f"Could not update project configuration: project {pid} not found in course {cid}.") abort(404, message=f"Could not create a new submission for user {user.username} in project {pid} " f"in course {cid}: project not found.") # check if a new submission can be created by this user in this project if not can_create_submission(user, project): # TODO: configure conflict_timeout in project if not can_create_submission(user, project, conflict_timeout=timedelta(minutes=30)): # maybe use 409, 403 abort(429, message=f"New submission denied: you have an unfinished submission for this project.") abort(429, message=f"New submission for user {user.username} denied: a submission for this user " f"is already being processed.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for project configuration update.') abort(400, message='No data provided for submission creation.') try: data = config_schema.load(json_data)[0] except ValidationError as err: Loading @@ -178,21 +187,20 @@ class ProjectSubmissions(Resource): review=data['review']) write_entity(new_submission) # download relevant files into Storage # TODO # TODO storage return submission_schema.dump(new_submission) class ProjectTestFiles(Resource): def get(self, cid, pid): # Gets test files (feature) return "Not implemented." projects_api.add_resource(ProjectResource, '/<string:pid>') projects_api.add_resource(ProjectsList, '/') projects_api.add_resource(ProjectConfigResource, '/<string:pid>/config') projects_api.add_resource(ProjectSubmissions, '/<string:pid>/submissions') ''' ### Project test files * `[GET] /courses/{cid/projects/{id}/files` - Gets test files (feature) ### Project submissions * `[GET] /courses/{cid}/projects/{id}/submissions` - Read all submissions for project * `user=<user_selector>` - filters submissions by user * `[POST] /courses/{cid}/projects/{id}/submissions` - Create submission''' projects_api.add_resource(ProjectTestFiles, '/<string:pid>/files')
portal/rest/roles/roles.py +45 −28 Original line number Diff line number Diff line Loading @@ -22,20 +22,20 @@ class RoleResource(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not find role: course with identifier {cid} not found.") abort(404, message=f"Could not find role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not find role {rid} in course {cid}.") abort(404, message=f"Could not find role {rid} in course {cid}: role not found.") return role_schema.dump(role) def delete(self, cid, rid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not delete role: course with identifier {cid} not found.") abort(404, message=f"Could not delete role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not delete role {rid} in course {cid}.") abort(404, message=f"Could not delete role {rid} in course {cid}: role not found.") delete_entity(role) return '', 204 Loading @@ -43,10 +43,10 @@ class RoleResource(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not update role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not update role {rid} in course {cid}: role not found.") json_data = request.get_json() if not json_data: Loading @@ -61,7 +61,7 @@ class RoleResource(Resource): role.name = data['name'] # updating course? write_entity(role) log.debug(f"Role update successful. Role={role}") log.debug(f"Updated role {rid} to {role}.") return '', 204 Loading @@ -70,14 +70,14 @@ class RoleList(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not list roles: course with identifier {cid} not found.") abort(404, message=f"Could not list roles in course {cid}: course not found.") return roles_schema.dump(course.roles) def post(self, cid): log.info(f"POST to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not create role: Course with identifier {cid} not found.") abort(404, message=f"Could not create role in course {cid}: course not found.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for role creation.') Loading @@ -90,7 +90,7 @@ class RoleList(Resource): new_role = Role(course=course, name=data['name']) new_role.description = data.get('description') or "" write_entity(new_role) log.debug(f"Created role={new_role} for course {course.codename}") log.debug(f"Created role={new_role} for course {cid}") return role_schema.dump(new_role)[0], 201 Loading @@ -99,24 +99,24 @@ class RolePermissions(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not get role permissions: course with identifier {cid} not found.") abort(404, message=f"Could not list permissions for role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update permissions for role{rid} in course {cid}.") abort(404, message=f"Could not list permissions for role {rid} in course {cid}: role not found.") return roles_schema.dump(role.permissions) def put(self, cid, rid): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not update permissions for role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not update permissions for role {rid} in course {cid}: role not found.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for role update.') abort(400, message='No data provided for update of role permissions.') try: data = role_schema.load(json_data)[0] except ValidationError as err: Loading @@ -124,7 +124,7 @@ class RolePermissions(Resource): return abort(422, errors=err.messages) # TODO: flash, process errors role.set_permissions(data) log.debug(f"Updated permissions for role {rid} in course {cid}.") log.debug(f"Updated permissions for role {rid} in course {cid} to {data}.") return '', 204 Loading @@ -133,10 +133,10 @@ class RoleUsersList(Resource): log.info(f"GET to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not list users with role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not list users with role {rid} in course {cid}: role not found.") return users_schema.dump(role.users) Loading @@ -144,44 +144,61 @@ class RoleUsersList(Resource): log.info(f"PUT to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not update role: course with identifier {cid} not found.") abort(404, message=f"Could not update users with role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not update role {rid} in course {cid}.") abort(404, message=f"Could not update users with role {rid} in course {cid}: role not found") json_data = request.get_json() try: # TODO full entities?, check duplicates data = users_schema.load(json_data)[0] # presumes the json contains full entities; change to ids? except ValidationError as err: log.info(f"Validation failed on: {err.messages}") return abort(422, errors=err.messages) # TODO: flash, process errors role.users = data log.info(f"Updated users of role {rid} to {data}") log.info(f"Updated users of role {rid} to {data}.") return '', 204 class RoleUsers(Resource): def put(self, cid, rid, uid): log.info(f"PUT to {request.url}") pass course = find_course(cid) if not course: abort(404, message=f"Could not add user {uid} to role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not add user {uid} to role {rid} in course {cid}: role not found.") user = find_user(uid) if not user: abort(404, message=f"Could not add user {uid} to role {rid} in course {cid}: user not found.") if user not in role.users: role.users.append(user) log.info(f"Added user {uid} to role {rid} in course {cid}.") else: log.info(f"User {uid} is already in role {rid} in course {cid}: no change.") return '', 204 def delete(self, cid, rid, uid): log.info(f"DELETE to {request.url}") course = find_course(cid) if not course: abort(404, message=f"Could not delete user {uid} from role: course with identifier {cid} not found.") abort(404, message=f"Could not remove user {uid} from role {rid} in course {cid}: course not found.") role = find_role(course, rid) if not role: abort(404, message=f"Could not delete user {uid} from role: role {rid} not found in course {cid}.") abort(404, message=f"Could not remove user {uid} from role {rid} in course {cid}: role not found.") user = find_user(uid) if not user: abort(404, message=f"Could not delete user {uid} from role{rid} in course {cid}: user not found.") abort(404, message=f"Could not remove user {uid} from role {rid} in course {cid}: user not found.") try: role.users.remove(user) except ValueError as err: # the user was not in the role log.info(f"Did not remove user {user} from role {rid} of course {cid}: user was not in the role. ") except ValueError: abort(400, message=f"Could not remove user {uid} from role {rid} in course {cid}: " f"role does not contain user.") log.info(f"Removed user {uid} from role {rid} in course {cid}.") return '', 204 Loading
portal/rest/users/users.py +13 −14 Original line number Diff line number Diff line Loading @@ -2,11 +2,11 @@ from flask import Blueprint, request from flask_restful import Api, Resource, abort from marshmallow import ValidationError from portal.rest import UserSchema, delete_entity, write_entity from portal.rest import UserSchema, delete_entity, write_entity, find_user from portal.database.models import User from portal.tools.logging import log users = Blueprint('users', __name__, url_prefix='/users/') users = Blueprint('users', __name__, url_prefix='/users') users_api = Api(users) user_schema = UserSchema() Loading @@ -16,21 +16,20 @@ users_schema = UserSchema(many=True, only=('id', 'username', 'uco', 'email')) class UserResource(Resource): def get(self, uid): log.info(f"GET to {request.url}") user = User.query.filter_by(id=uid).first() user = find_user(uid) if not user: abort(404, message=f"User with id={uid} doesn't exist.") abort(404, message=f"Could not find user {uid}.") return user_schema.dump(user) def put(self, uid): log.info(f"PUT to {request.url}") user = User.query.filter_by(id=uid).first() user = find_user(uid) if not user: log.debug(f"Did not find course with id={uid}. Aborting request processing.") abort(404, message="User with id={} not found.".format(uid)) abort(404, message=f"Could not find user {uid}.") json_data = request.get_json() if not json_data: abort(400, message='No data provided for course update.') abort(400, message='No data provided for user update.') try: data = user_schema.load(json_data) except ValidationError as err: Loading @@ -44,14 +43,14 @@ class UserResource(Resource): user.is_admin = data['is_admin'] # TODO all; care for password write_entity(user) log.debug(f"User update successful. User={user}") log.debug(f"Updated user {uid} to {user}.") return '', 204 def delete(self, uid): log.info(f"DELETE to {request.url}") user = User.query.filter_by(id=uid).first() user = find_user(uid) if not user: abort(404, message=f"User with id={uid} doesn't exist.") abort(404, message=f"Could not find user {uid}.") delete_entity(user) return '', 204 Loading @@ -66,7 +65,7 @@ class UserList(Resource): log.info(f"POST to {request.url}") json_data = request.get_json() if not json_data: abort(400, message='No data provided for user update.') abort(400, message='No data provided for user creation.') try: data = user_schema.load(json_data)[0] except ValidationError as err: Loading @@ -76,10 +75,10 @@ class UserList(Resource): admin = data.get('is_admin') or False new_user = User(uco=data['uco'], email=data['email'], username=data['username'], is_admin=admin) write_entity(new_user) log.debug(f"Created User={User}") log.debug(f"Created user={User}") return user_schema.dump(new_user)[0], 201 users_api.add_resource(UserResource, '/<string:id>') users_api.add_resource(UserResource, '/<string:uid>') users_api.add_resource(UserList, '/')