diff --git a/back-end/src/lms/serializers/import_schedule.py b/back-end/src/lms/serializers/import_schedule.py new file mode 100644 index 00000000..eecf09e2 --- /dev/null +++ b/back-end/src/lms/serializers/import_schedule.py @@ -0,0 +1,25 @@ +from django.db import transaction +from rest_framework import serializers, status, viewsets, generics + +from lms.models.lessons import Lesson +from lms.serializers.lessons import LessonParsedSerializer + + +class ParseScheduleSerializer(serializers.Serializer): + content = serializers.FileField(write_only=True) + + +class ImportParsedSerializer(serializers.Serializer): + parsed = LessonParsedSerializer(many=True) + + def create(self, validated_data): + parsed_data = validated_data.get("parsed") + + with transaction.atomic(): + lesson_parsed_objects = [] + + for item in parsed_data: + lesson_parsed_objects.append(Lesson(**item)) + Lesson.objects.bulk_create(lesson_parsed_objects) + + return validated_data diff --git a/back-end/src/lms/serializers/lessons.py b/back-end/src/lms/serializers/lessons.py index 38e16a8a..4da470ba 100644 --- a/back-end/src/lms/serializers/lessons.py +++ b/back-end/src/lms/serializers/lessons.py @@ -44,6 +44,12 @@ class Meta: fields = "__all__" +class LessonParsedSerializer(serializers.ModelSerializer): + class Meta: + model = Lesson + fields = "__all__" + + class LessonJournalQuerySerializer(serializers.Serializer): milgroup = serializers.IntegerField( required=True, diff --git a/back-end/src/lms/urls.py b/back-end/src/lms/urls.py index 3c39b7f7..ced0cf03 100644 --- a/back-end/src/lms/urls.py +++ b/back-end/src/lms/urls.py @@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter from lms.views.achievements import AchievementViewSet -from lms.views.import_schedule import ImportScheduleViewSet +from lms.views.import_schedule import ParseScheduleView, ImportParsedView from lms.views.personnel import SearchPersonnelUsersViewSet from lms.views.subjects import LessonSubjectViewSet from lms.views.uniforms import UniformViewSet @@ -98,7 +98,7 @@ routers.register("skills", SkillViewSet) routers.register("subjects", LessonSubjectViewSet) routers.register("uniforms", UniformViewSet) -routers.register("import-schedule", ImportScheduleViewSet, basename="ImportSchedule") + choices = [ path("absence-excuses/", AbsenceExcuseChoicesList.as_view()), @@ -125,6 +125,8 @@ path("milgroup-leaders/", MilgroupLeadersView.as_view()), path("birthdays/students", StudentBirthdayAlertView.as_view()), path("birthdays/teachers", TeacherBirthdayAlertView.as_view()), + path("import-schedule/parse-schedule", ParseScheduleView.as_view()), + path("import-schedule/import-parsed", ImportParsedView.as_view()), # Choices lists. path("choices/", include(choices)), ] diff --git a/back-end/src/lms/views/import_schedule.py b/back-end/src/lms/views/import_schedule.py index 81dffc2e..0845910f 100644 --- a/back-end/src/lms/views/import_schedule.py +++ b/back-end/src/lms/views/import_schedule.py @@ -1,11 +1,10 @@ -from django.http import HttpResponseBadRequest -from drf_spectacular.utils import extend_schema, inline_serializer -from rest_framework import serializers, status -from rest_framework.decorators import action +from drf_spectacular.utils import extend_schema +from rest_framework import status, generics from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.viewsets import GenericViewSet +from auth.models import Permission +from auth.permissions import BasePermission from common.models.subjects import Subject from common.parsers import MultiPartWithJSONParser import datetime @@ -17,10 +16,13 @@ from lms.models.lessons import Room, Lesson from lms.models.teachers import Teacher +from lms.serializers.import_schedule import ( + ImportParsedSerializer, + ParseScheduleSerializer, +) +from lms.serializers.lessons import LessonParsedSerializer from lms.views.lessons import LessonPermission -CUR_YEAR = 2023 - month_to_num = { "января": 1, "февраля": 2, @@ -182,25 +184,21 @@ def parse_timetable(timetable_path: str): return final -ImportScheduleSerializerForSwagger = inline_serializer( - name="ImportSchedule", - fields={ - "content": serializers.FileField(write_only=True), - }, -) +class ImportSchedulePermission(BasePermission): + permission_class = "import-permission" + view_name_rus = "Импорт расписания из файла" + scopes = [ + Permission.Scope.ALL, + ] -@extend_schema(request=ImportScheduleSerializerForSwagger, tags=["import-schedule"]) -class ImportScheduleViewSet(GenericViewSet): +@extend_schema(tags=["import-schedule"]) +class ParseScheduleView(generics.GenericAPIView): parser_classes = [MultiPartWithJSONParser] + permission_classes = [LessonPermission] + serializer_class = ParseScheduleSerializer - @action( - methods=["post"], - url_path="parse-schedule", - detail=False, - permission_classes=[LessonPermission], - ) - def parse_schedule(self, request: Request): + def post(self, request: Request): if "content" not in request.data: return Response( {"detail": "Bad request: no file passed"}, @@ -243,10 +241,7 @@ def parse_schedule(self, request: Request): teachers_cache[teacher] = teacher_filtered[0] parsed_lessons_list = [] for elem in lessons_list: - lesson = { - "input": {}, - "parsed": {} - } + lesson = {"input": {}, "parsed": {}} lesson_name_input = elem["subject"] lesson["input"]["lesson_name_input"] = lesson_name_input @@ -303,3 +298,24 @@ def parse_schedule(self, request: Request): parsed_lessons_list.append(lesson) return Response(parsed_lessons_list) + + +@extend_schema(tags=["import-schedule"]) +class ImportParsedView(generics.GenericAPIView): + permission_classes = [ImportSchedulePermission] + serializer_class = ImportParsedSerializer + + def post(self, request: Request): + valid_data = [] + if "parsed" not in request.data: + return Response(status=status.HTTP_400_BAD_REQUEST) + + for data in request.data["parsed"]: + serializer = LessonParsedSerializer(data=data) + if serializer.is_valid(): + valid_data.append(data) + request.data["parsed"] = valid_data + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response({"created": len(valid_data)}, status=status.HTTP_201_CREATED)