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 Diff line number Diff line
@@ -19,7 +19,7 @@ from portal.tools import time
def _repr(instance):
    result = f"{instance.__class__.__name__}: "
    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} "
    return result

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


class Submission(db.Model, EntityBase):
@@ -393,11 +396,12 @@ class ReviewItem(db.Model, EntityBase):
    file = db.Column(db.String(100), 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.user = user
        self.file = file
        self.line = line
        self.content = content

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

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

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

        course = service.find_course(cid)
+54 −6
Original line number Diff line number Diff line
from flask import Blueprint, request
from flask_jwt_extended import jwt_required, get_jwt_identity
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.logging import log
from portal.service import service
@@ -16,9 +17,15 @@ class SubmissionResource(Resource):
    def get(self, sid):
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        # TODO: add stuff to the schema, needs testing
        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):
    @error_handler
@@ -26,7 +33,7 @@ class SubmissionState(Resource):
        log.info(f"GET to {request.url}")
        submission = service.find_submission(sid)
        # 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
    def put(self, sid):
@@ -91,12 +98,51 @@ class SubmissionResubmit(Resource):
    def post(self, sid):
        log.info(f"POST to {request.url}")
        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
        # 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
submissions_api.add_resource(SubmissionState, '/<string:sid>/state')  # OK
class SubmissionReview(Resource):
    @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(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(SubmissionSources, '/<string:sid>/sources')

submissions_api.add_resource(SubmissionReview, '/<string:sid>/review')
+19 −1
Original line number Diff line number Diff line
from portal.service.errors import ResourceNotFoundError
from portal.tools.logging import log
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
import copy

@@ -208,6 +208,14 @@ def create_submission(user, project, file_params, project_params_string) -> Subm
    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):
    new_role = Role(target, source.name)
    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:
    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