From e9552e691723e102516ded890d08978655c955ae Mon Sep 17 00:00:00 2001 From: Topvennie Date: Sat, 11 May 2024 18:26:09 +0200 Subject: [PATCH] feat: download log files --- .../api/permissions/submission_permissions.py | 59 +++++++++++++++++++ backend/api/urls.py | 6 +- backend/api/views/submission_view.py | 44 ++++++++++++-- 3 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 backend/api/permissions/submission_permissions.py diff --git a/backend/api/permissions/submission_permissions.py b/backend/api/permissions/submission_permissions.py new file mode 100644 index 00000000..7bc7f642 --- /dev/null +++ b/backend/api/permissions/submission_permissions.py @@ -0,0 +1,59 @@ +from typing import cast + +from api.models.submission import (ExtraCheckResult, StructureCheckResult, + Submission) +from api.permissions.role_permissions import (is_assistant, is_student, + is_teacher) +from authentication.models import User +from rest_framework.permissions import SAFE_METHODS, BasePermission +from rest_framework.request import Request +from rest_framework.views import APIView + + +class SubmissionPermission(BasePermission): + def has_permission(self, request: Request, view: APIView) -> bool: + if request.method not in SAFE_METHODS: + return False + + user: User = cast(User, request.user) + + return user.is_staff or is_teacher(user) or is_assistant(user) + + def has_object_permission(self, request: Request, view: APIView, obj: Submission) -> bool: + if request.method not in SAFE_METHODS: + return False + + user: User = cast(User, request.user) + + if user.is_staff: + return True + + if is_teacher(user) or is_assistant(user): + return True + + return obj.group.students.filter(id=user.id).exists() + + +class StructureCheckResultPermission(SubmissionPermission): + def has_object_permission(self, request: Request, view: APIView, obj: StructureCheckResult) -> bool: + return super().has_object_permission(request, view, obj.submission) + + +class ExtraCheckResultPermission(SubmissionPermission): + def has_object_permission(self, request: Request, view: APIView, obj: ExtraCheckResult) -> bool: + return super().has_object_permission(request, view, obj.submission) + + +class ExtraCheckResultLogPermission(ExtraCheckResultPermission): + def has_object_permission(self, request: Request, view: APIView, obj: ExtraCheckResult) -> bool: + result = super().has_object_permission(request, view, obj) + + if not result: + return False + + user: User = cast(User, request.user) + + if is_student(user): + return obj.extra_check.show_log + + return True diff --git a/backend/api/urls.py b/backend/api/urls.py index 74c4da89..8887847d 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -8,7 +8,9 @@ from api.views.group_view import GroupViewSet from api.views.project_view import ProjectViewSet from api.views.student_view import StudentViewSet -from api.views.submission_view import SubmissionViewSet +from api.views.submission_view import (ExtraCheckResultViewSet, + StructureCheckResultViewSet, + SubmissionViewSet) from api.views.teacher_view import TeacherViewSet from api.views.user_view import UserViewSet from django.urls import include, path @@ -29,6 +31,8 @@ router.register(r"file-extensions", FileExtensionViewSet, basename="file-extension") router.register(r"faculties", FacultyViewSet, basename="faculty") router.register(r"docker-images", DockerImageViewSet, basename="docker-image") +router.register(r"structure-check-results", StructureCheckResultViewSet, basename="structure-check-results") +router.register(r"extra-check-results", ExtraCheckResultViewSet, basename="extra-check-results") urlpatterns = [ path("", include(router.urls)), diff --git a/backend/api/views/submission_view.py b/backend/api/views/submission_view.py index e53d4408..1e373f76 100644 --- a/backend/api/views/submission_view.py +++ b/backend/api/views/submission_view.py @@ -1,19 +1,51 @@ +from api.models.submission import (ExtraCheckResult, StructureCheckResult, + Submission) +from api.permissions.submission_permissions import ( + ExtraCheckResultLogPermission, ExtraCheckResultPermission, + StructureCheckResultPermission, SubmissionPermission) +from api.serializers.submission_serializer import ( + ExtraCheckResultSerializer, StructureCheckResultSerializer, + SubmissionSerializer) from django.http import FileResponse -from rest_framework import viewsets +from django.utils.translation import gettext as _ from rest_framework.decorators import action from rest_framework.mixins import RetrieveModelMixin - -from ..models.submission import Submission -from ..serializers.submission_serializer import SubmissionSerializer +from rest_framework.response import Response +from rest_framework.viewsets import GenericViewSet # TODO: Permission to ask for logs -class SubmissionViewSet(RetrieveModelMixin, viewsets.GenericViewSet): +class SubmissionViewSet(RetrieveModelMixin, GenericViewSet): queryset = Submission.objects.all() serializer_class = SubmissionSerializer + permission_classes = [SubmissionPermission] @action(detail=True) - def zip(self, request, **_): + def zip(self, request, **__): submission: Submission = self.get_object() + if not submission.zip: + return Response({"message": _("submission.download.zip")}, status=404) + return FileResponse(open(submission.zip.path, "rb"), as_attachment=True) + + +class StructureCheckResultViewSet(RetrieveModelMixin, GenericViewSet): + queryset = StructureCheckResult.objects.all() + serializer_class = StructureCheckResultSerializer + permission_classes = [StructureCheckResultPermission] + + +class ExtraCheckResultViewSet(RetrieveModelMixin, GenericViewSet): + queryset = ExtraCheckResult.objects.all() + serializer_class = ExtraCheckResultSerializer + permission_classes = [ExtraCheckResultPermission] + + @action(detail=True, permission_classes=[ExtraCheckResultLogPermission]) + def log(self, request, **__): + extra_check_result: ExtraCheckResult = self.get_object() + + if not extra_check_result.log_file: + return Response({"message": _("extra_check_result.download.log")}, status=404) + + return FileResponse(open(extra_check_result.log_file.path, "rb"), as_attachment=True)