Commit f4628e35 authored by Barbora Kompišová's avatar Barbora Kompišová
Browse files

submission API, tests; missing ZIP

parent dff4ede5
Loading
Loading
Loading
Loading
+6 −2
Original line number Original line Diff line number Diff line
@@ -19,7 +19,7 @@ from portal.tools import time
def _repr(instance):
def _repr(instance):
    result = f"{instance.__class__.__name__}: "
    result = f"{instance.__class__.__name__}: "
    for key, value in vars(instance).items():
    for key, value in vars(instance).items():
        if not key.startswith("_") and key != 'password_hash':
        if not key.startswith("_") and key not in ('password_hash', 'review', 'course', 'user', 'project', 'group', 'role', 'review_items'):
            result += f"{key}={value} "
            result += f"{key}={value} "
    return result
    return result


@@ -328,6 +328,9 @@ class SubmissionState(enum.Enum):
    QUEUED = 3
    QUEUED = 3
    IN_PROGRESS = 4
    IN_PROGRESS = 4
    FINISHED = 5
    FINISHED = 5
    CANCELLED = 6
    ABORTED = 7
    ARCHIVED = 8




class Submission(db.Model, EntityBase):
class Submission(db.Model, EntityBase):
@@ -393,11 +396,12 @@ class ReviewItem(db.Model, EntityBase):
    file = db.Column(db.String(100), nullable=False)
    file = db.Column(db.String(100), nullable=False)
    line = db.Column(db.Integer, nullable=False)
    line = db.Column(db.Integer, nullable=False)


    def __init__(self, user, review, file, line):
    def __init__(self, user, review, file, line, content):
        self.review = review
        self.review = review
        self.user = user
        self.user = user
        self.file = file
        self.file = file
        self.line = line
        self.line = line
        self.content = content


    def __repr__(self):
    def __repr__(self):
        return _repr(self)
        return _repr(self)
+1 −0
Original line number Original line Diff line number Diff line
@@ -186,6 +186,7 @@ submission_create_schema = SubmissionCreateSchema()
submission_state_schema = SubmissionSchema(only=('state',))
submission_state_schema = SubmissionSchema(only=('state',))


reviews_schema = ReviewSchema(many=True)
reviews_schema = ReviewSchema(many=True)
review_schema = ReviewSchema()


role_schema = RoleSchema(strict=True)
role_schema = RoleSchema(strict=True)
roles_schema = RoleSchema(many=True, only=('id', 'name', 'description', 'course'))
roles_schema = RoleSchema(many=True, only=('id', 'name', 'description', 'course'))
+1 −2
Original line number Original line Diff line number Diff line
@@ -122,10 +122,9 @@ class ProjectSubmissions(Resource):
    @jwt_required
    @jwt_required
    def post(self, cid, pid):
    def post(self, cid, pid):
        log.info(f"POST to {request.url}")
        log.info(f"POST to {request.url}")
        # TODO FIX: do not use find_user but autorize_user
        user = service.find_user(get_jwt_identity())
        user = service.find_user(get_jwt_identity())
        if not user:
        if not user:
            abort(401, message="Invalid access token: user not found.")
            raise PortalAPIError(401, message="Invalid access token: user not found.")
        # authorization here
        # authorization here


        course = service.find_course(cid)
        course = service.find_course(cid)
+54 −6
Original line number Original line Diff line number Diff line
from flask import Blueprint, request
from flask import Blueprint, request
from flask_jwt_extended import jwt_required, get_jwt_identity
from flask_restful import Api, Resource
from flask_restful import Api, Resource


from portal.rest import submission_schema, submission_state_schema
from portal.rest import submission_schema, submission_state_schema, review_schema, reviews_schema
from portal.tools.decorators import error_handler
from portal.tools.decorators import error_handler
from portal.tools.logging import log
from portal.tools.logging import log
from portal.service import service
from portal.service import service
@@ -16,9 +17,15 @@ class SubmissionResource(Resource):
    def get(self, sid):
    def get(self, sid):
        log.info(f"GET to {request.url}")
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        submission = service.find_submission(sid)
        # TODO: add stuff to the schema, needs testing
        return submission_schema.dump(submission)
        return submission_schema.dump(submission)


    @error_handler
    def delete(self, sid):
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        service.delete_entity(submission)
        return '', 204



class SubmissionState(Resource):
class SubmissionState(Resource):
    @error_handler
    @error_handler
@@ -26,7 +33,7 @@ class SubmissionState(Resource):
        log.info(f"GET to {request.url}")
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        submission = service.find_submission(sid)
        # returning the enum value as a string should also be okay
        # returning the enum value as a string should also be okay
        return submission_state_schema.dump(submission.state)
        return submission_state_schema.dump(submission)[0], 200


    @error_handler
    @error_handler
    def put(self, sid):
    def put(self, sid):
@@ -91,12 +98,51 @@ class SubmissionResubmit(Resource):
    def post(self, sid):
    def post(self, sid):
        log.info(f"POST to {request.url}")
        log.info(f"POST to {request.url}")
        source_submission = service.find_submission(sid)
        source_submission = service.find_submission(sid)

        json_data = request.get_json()
        if not json_data:
            raise PortalAPIError(400, message='No data provided for submission resubmit.')

        data = submission_schema.load(json_data)[0]
        # create new submission by copying files from the source submission in storage
        # create new submission by copying files from the source submission in storage
        # TODO: add to service
        new_submission = service.copy_submission(source_submission, note=data['note'])
        return submission_schema.dump(new_submission), 201




submissions_api.add_resource(SubmissionResource, '/<string:sid>')  # needs extension
class SubmissionReview(Resource):
submissions_api.add_resource(SubmissionState, '/<string:sid>/state')  # OK
    @error_handler
    def get(self, sid):
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        return review_schema.dump(submission.review)[0], 200

    @error_handler
    @jwt_required
    def post(self, sid):
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        user = service.find_user(get_jwt_identity())

        if not user:
            raise PortalAPIError(401, message="Invalid access token: user not found.")

        if not submission.review:
            service.create_review(submission)
        json_data = request.get_json()
        if not json_data:
            raise PortalAPIError(400, message='No data provided for group creation.')

        data = review_schema.load(json_data)[0]

        # also writes to db
        service.create_review_items(review=submission.review, items=data['review_items'], author=user)
        log.info(f"Added review items {data['review_items']} to review {submission.review.id} "
                 f"for submission {submission.id}")
        return review_schema.dump(submission.review)[0], 201


submissions_api.add_resource(SubmissionResource, '/<string:sid>')
submissions_api.add_resource(SubmissionState, '/<string:sid>/state')
submissions_api.add_resource(SubmissionResult, '/<string:sid>/result')
submissions_api.add_resource(SubmissionResult, '/<string:sid>/result')
submissions_api.add_resource(SubmissionResubmit, '/<string:sid>/resubmit')
submissions_api.add_resource(SubmissionResubmit, '/<string:sid>/resubmit')


@@ -106,3 +152,5 @@ submissions_api.add_resource(SubmissionResultFiles, '/<string:sid>/files/results


submissions_api.add_resource(SubmissionTestFiles, '/<string:sid>/test_files')
submissions_api.add_resource(SubmissionTestFiles, '/<string:sid>/test_files')
submissions_api.add_resource(SubmissionSources, '/<string:sid>/sources')
submissions_api.add_resource(SubmissionSources, '/<string:sid>/sources')

submissions_api.add_resource(SubmissionReview, '/<string:sid>/review')
+19 −1
Original line number Original line Diff line number Diff line
from portal.service.errors import ResourceNotFoundError
from portal.service.errors import ResourceNotFoundError
from portal.tools.logging import log
from portal.tools.logging import log
from portal import db
from portal import db
from portal.database.models import Course, Role, User, Project, Group, Submission, SubmissionState
from portal.database.models import Course, Role, User, Project, Group, Submission, SubmissionState, Review, ReviewItem
from portal import storage
from portal import storage
import copy
import copy


@@ -208,6 +208,14 @@ def create_submission(user, project, file_params, project_params_string) -> Subm
    return new_submission
    return new_submission




def copy_submission(source: Submission, note: str):
    new_submission = Submission(user=source.user, project=source.project, parameters=source.parameters)
    new_submission.note = note
    write_entity(new_submission)
    storage.submissions.clone(source.id, new_submission.id)
    return new_submission


def copy_role(source: Role, target: Course, with_users: str):
def copy_role(source: Role, target: Course, with_users: str):
    new_role = Role(target, source.name)
    new_role = Role(target, source.name)
    new_role.set_permissions(**vars(source.permissions))
    new_role.set_permissions(**vars(source.permissions))
@@ -255,3 +263,13 @@ def copy_course(source: Course, target: Course, config: dict):
def find_users(ids: list) -> list:
def find_users(ids: list) -> list:
    return [find_user(i) for i in ids]
    return [find_user(i) for i in ids]



def create_review_items(review: Review, author: User, items: list):
    for item in items:
        new_item = ReviewItem(user=author, review=review, file=item['file'], line=item['line'], content=item['content'])
    write_entity(review)


def create_review(submission: Submission):
    r = Review(submission=submission)
    write_entity(r)
Loading