|
|
import json import re
from django.db import IntegrityError, transaction from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics, status from rest_framework.exceptions import ParseError from rest_framework.parsers import MultiPartParser from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView
from members.permissions import IsInProjectReadOnlyOrAdmin, IsProjectAdmin
from ..exceptions import LabelValidationError from ..models import CategoryType, Label, RelationTypes, SpanType from ..serializers import (CategoryTypeSerializer, LabelSerializer, RelationTypesSerializer, SpanTypeSerializer)
def camel_to_snake(name): name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
def camel_to_snake_dict(d): return {camel_to_snake(k): v for k, v in d.items()}
class LabelList(generics.ListCreateAPIView): model = Label filter_backends = [DjangoFilterBackend] serializer_class = LabelSerializer pagination_class = None permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin]
def get_queryset(self): return self.model.objects.filter(project=self.kwargs['project_id'])
def perform_create(self, serializer): serializer.save(project_id=self.kwargs['project_id'])
def delete(self, request, *args, **kwargs): delete_ids = request.data['ids'] self.model.objects.filter(pk__in=delete_ids).delete() return Response(status=status.HTTP_204_NO_CONTENT)
class CategoryTypeList(LabelList): model = CategoryType serializer_class = CategoryTypeSerializer
class CategoryTypeDetail(generics.RetrieveUpdateDestroyAPIView): queryset = CategoryType.objects.all() serializer_class = CategoryTypeSerializer lookup_url_kwarg = 'label_id' permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin]
class SpanTypeList(LabelList): model = SpanType serializer_class = SpanTypeSerializer
class SpanTypeDetail(generics.RetrieveUpdateDestroyAPIView): queryset = SpanType.objects.all() serializer_class = SpanTypeSerializer lookup_url_kwarg = 'label_id' permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin]
class RelationTypeList(LabelList): model = RelationTypes serializer_class = RelationTypesSerializer
class RelationTypeDetail(generics.RetrieveUpdateDestroyAPIView): queryset = RelationTypes.objects.all() serializer_class = RelationTypesSerializer lookup_url_kwarg = 'relation_type_id' permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin]
class LabelUploadAPI(APIView): parser_classes = (MultiPartParser,) permission_classes = [IsAuthenticated & IsProjectAdmin] serializer_class = LabelSerializer
@transaction.atomic def post(self, request, *args, **kwargs): if 'file' not in request.data: raise ParseError('Empty content') try: labels = json.load(request.data['file']) labels = list(map(camel_to_snake_dict, labels)) serializer = self.serializer_class(data=labels, many=True) serializer.is_valid(raise_exception=True) serializer.save(project_id=kwargs['project_id']) return Response(status=status.HTTP_201_CREATED) except json.decoder.JSONDecodeError: raise ParseError('The file format is invalid.') except IntegrityError: raise LabelValidationError
class CategoryTypeUploadAPI(LabelUploadAPI): serializer_class = CategoryTypeSerializer
class SpanTypeUploadAPI(LabelUploadAPI): serializer_class = SpanTypeSerializer
class RelationTypeUploadAPI(LabelUploadAPI): serializer_class = RelationTypesSerializer
|