prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  No way to restrict text classification labels to exactly one label to assign
  Most classification tasks require exactly one label for each instance. This is also true for most text classification tasks, for example with sentiment classificaiton, and the possible labels negative, neutral, positive, each instance should receive one of the three labels, assigning e.g. both neutral and positive would make not sense.
  
  Yet the text classification task in doccano still does not allow to restrict assignment to a single label, annotators are free to assign as many labels as they want, including all of them!
  
  This limits the use of doccano for text classification tasks rather severely. The option to allow for any number of labels (0 to all of them) would still be good to have for multilabel classification tasks (e.g. assigning topics), but that is a much rarer annotation task in general. 
  
  </issue>
  <code>
  [start of app/api/views/annotation.py]
  1 from django.shortcuts import get_object_or_404
  2 from rest_framework import generics, status
  3 from rest_framework.exceptions import ValidationError
  4 from rest_framework.permissions import IsAuthenticated
  5 from rest_framework.response import Response
  6 from rest_framework.views import APIView
  7 
  8 from ..models import Document, Project
  9 from ..permissions import (IsAnnotationApprover, IsInProjectOrAdmin,
  10                            IsOwnAnnotation, IsProjectAdmin)
  11 from ..serializers import ApproverSerializer
  12 
  13 
  14 class AnnotationList(generics.ListCreateAPIView):
  15     pagination_class = None
  16     permission_classes = [IsAuthenticated & IsInProjectOrAdmin]
  17     swagger_schema = None
  18 
  19     def get_serializer_class(self):
  20         project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  21         self.serializer_class = project.get_annotation_serializer()
  22         return self.serializer_class
  23 
  24     def get_queryset(self):
  25         project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  26         model = project.get_annotation_class()
  27         queryset = model.objects.filter(document=self.kwargs['doc_id'])
  28         if not project.collaborative_annotation:
  29             queryset = queryset.filter(user=self.request.user)
  30         return queryset
  31 
  32     def create(self, request, *args, **kwargs):
  33         self.check_single_class_classification(self.kwargs['project_id'], self.kwargs['doc_id'], request.user)
  34         request.data['document'] = self.kwargs['doc_id']
  35         return super().create(request, args, kwargs)
  36 
  37     def perform_create(self, serializer):
  38         serializer.save(document_id=self.kwargs['doc_id'], user=self.request.user)
  39 
  40     def delete(self, request, *args, **kwargs):
  41         queryset = self.get_queryset()
  42         queryset.all().delete()
  43         return Response(status=status.HTTP_204_NO_CONTENT)
  44 
  45     @staticmethod
  46     def check_single_class_classification(project_id, doc_id, user):
  47         project = get_object_or_404(Project, pk=project_id)
  48         if not project.single_class_classification:
  49             return
  50 
  51         model = project.get_annotation_class()
  52         annotations = model.objects.filter(document_id=doc_id)
  53         if not project.collaborative_annotation:
  54             annotations = annotations.filter(user=user)
  55 
  56         if annotations.exists():
  57             raise ValidationError('requested to create duplicate annotation for single-class-classification project')
  58 
  59 
  60 class AnnotationDetail(generics.RetrieveUpdateDestroyAPIView):
  61     lookup_url_kwarg = 'annotation_id'
  62     swagger_schema = None
  63 
  64     def get_permissions(self):
  65         project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  66         if project.collaborative_annotation:
  67             self.permission_classes = [IsAuthenticated & IsInProjectOrAdmin]
  68         else:
  69             self.permission_classes = [IsAuthenticated & IsInProjectOrAdmin & IsOwnAnnotation]
  70         return super().get_permissions()
  71 
  72     def get_serializer_class(self):
  73         project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  74         self.serializer_class = project.get_annotation_serializer()
  75         return self.serializer_class
  76 
  77     def get_queryset(self):
  78         project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  79         model = project.get_annotation_class()
  80         self.queryset = model.objects.all()
  81         return self.queryset
  82 
  83 
  84 class ApproveLabelsAPI(APIView):
  85     permission_classes = [IsAuthenticated & (IsAnnotationApprover | IsProjectAdmin)]
  86 
  87     def post(self, request, *args, **kwargs):
  88         approved = self.request.data.get('approved', True)
  89         document = get_object_or_404(Document, pk=self.kwargs['doc_id'])
  90         document.annotations_approved_by = self.request.user if approved else None
  91         document.save()
  92         return Response(ApproverSerializer(document).data)
  93
  [end of app/api/views/annotation.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/app/api/views/annotation.py b/app/api/views/annotation.py
  --- a/app/api/views/annotation.py
  +++ b/app/api/views/annotation.py
  @@ -1,6 +1,5 @@
   from django.shortcuts import get_object_or_404
   from rest_framework import generics, status
  -from rest_framework.exceptions import ValidationError
   from rest_framework.permissions import IsAuthenticated
   from rest_framework.response import Response
   from rest_framework.views import APIView
  @@ -16,21 +15,24 @@
       permission_classes = [IsAuthenticated & IsInProjectOrAdmin]
       swagger_schema = None
   
  +    @property
  +    def project(self):
  +        return get_object_or_404(Project, pk=self.kwargs['project_id'])
  +
       def get_serializer_class(self):
  -        project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  -        self.serializer_class = project.get_annotation_serializer()
  +        self.serializer_class = self.project.get_annotation_serializer()
           return self.serializer_class
   
       def get_queryset(self):
  -        project = get_object_or_404(Project, pk=self.kwargs['project_id'])
  -        model = project.get_annotation_class()
  +        model = self.project.get_annotation_class()
           queryset = model.objects.filter(document=self.kwargs['doc_id'])
  -        if not project.collaborative_annotation:
  +        if not self.project.collaborative_annotation:
               queryset = queryset.filter(user=self.request.user)
           return queryset
   
       def create(self, request, *args, **kwargs):
  -        self.check_single_class_classification(self.kwargs['project_id'], self.kwargs['doc_id'], request.user)
  +        if self.project.single_class_classification:
  +            self.get_queryset().delete()
           request.data['document'] = self.kwargs['doc_id']
           return super().create(request, args, kwargs)
   
  @@ -42,20 +44,6 @@
           queryset.all().delete()
           return Response(status=status.HTTP_204_NO_CONTENT)
   
  -    @staticmethod
  -    def check_single_class_classification(project_id, doc_id, user):
  -        project = get_object_or_404(Project, pk=project_id)
  -        if not project.single_class_classification:
  -            return
  -
  -        model = project.get_annotation_class()
  -        annotations = model.objects.filter(document_id=doc_id)
  -        if not project.collaborative_annotation:
  -            annotations = annotations.filter(user=user)
  -
  -        if annotations.exists():
  -            raise ValidationError('requested to create duplicate annotation for single-class-classification project')
  -
   
   class AnnotationDetail(generics.RetrieveUpdateDestroyAPIView):
       lookup_url_kwarg = 'annotation_id'
  
verification_info: {"golden_diff": "diff --git a/app/api/views/annotation.py b/app/api/views/annotation.py\n--- a/app/api/views/annotation.py\n+++ b/app/api/views/annotation.py\n@@ -1,6 +1,5 @@\n from django.shortcuts import get_object_or_404\n from rest_framework import generics, status\n-from rest_framework.exceptions import ValidationError\n from rest_framework.permissions import IsAuthenticated\n from rest_framework.response import Response\n from rest_framework.views import APIView\n@@ -16,21 +15,24 @@\n     permission_classes = [IsAuthenticated & IsInProjectOrAdmin]\n     swagger_schema = None\n \n+    @property\n+    def project(self):\n+        return get_object_or_404(Project, pk=self.kwargs['project_id'])\n+\n     def get_serializer_class(self):\n-        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n-        self.serializer_class = project.get_annotation_serializer()\n+        self.serializer_class = self.project.get_annotation_serializer()\n         return self.serializer_class\n \n     def get_queryset(self):\n-        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n-        model = project.get_annotation_class()\n+        model = self.project.get_annotation_class()\n         queryset = model.objects.filter(document=self.kwargs['doc_id'])\n-        if not project.collaborative_annotation:\n+        if not self.project.collaborative_annotation:\n             queryset = queryset.filter(user=self.request.user)\n         return queryset\n \n     def create(self, request, *args, **kwargs):\n-        self.check_single_class_classification(self.kwargs['project_id'], self.kwargs['doc_id'], request.user)\n+        if self.project.single_class_classification:\n+            self.get_queryset().delete()\n         request.data['document'] = self.kwargs['doc_id']\n         return super().create(request, args, kwargs)\n \n@@ -42,20 +44,6 @@\n         queryset.all().delete()\n         return Response(status=status.HTTP_204_NO_CONTENT)\n \n-    @staticmethod\n-    def check_single_class_classification(project_id, doc_id, user):\n-        project = get_object_or_404(Project, pk=project_id)\n-        if not project.single_class_classification:\n-            return\n-\n-        model = project.get_annotation_class()\n-        annotations = model.objects.filter(document_id=doc_id)\n-        if not project.collaborative_annotation:\n-            annotations = annotations.filter(user=user)\n-\n-        if annotations.exists():\n-            raise ValidationError('requested to create duplicate annotation for single-class-classification project')\n-\n \n class AnnotationDetail(generics.RetrieveUpdateDestroyAPIView):\n     lookup_url_kwarg = 'annotation_id'\n", "issue": "No way to restrict text classification labels to exactly one label to assign\nMost classification tasks require exactly one label for each instance. This is also true for most text classification tasks, for example with sentiment classificaiton, and the possible labels negative, neutral, positive, each instance should receive one of the three labels, assigning e.g. both neutral and positive would make not sense.\r\n\r\nYet the text classification task in doccano still does not allow to restrict assignment to a single label, annotators are free to assign as many labels as they want, including all of them!\r\n\r\nThis limits the use of doccano for text classification tasks rather severely. The option to allow for any number of labels (0 to all of them) would still be good to have for multilabel classification tasks (e.g. assigning topics), but that is a much rarer annotation task in general. \n", "before_files": [{"content": "from django.shortcuts import get_object_or_404\nfrom rest_framework import generics, status\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nfrom ..models import Document, Project\nfrom ..permissions import (IsAnnotationApprover, IsInProjectOrAdmin,\n                           IsOwnAnnotation, IsProjectAdmin)\nfrom ..serializers import ApproverSerializer\n\n\nclass AnnotationList(generics.ListCreateAPIView):\n    pagination_class = None\n    permission_classes = [IsAuthenticated & IsInProjectOrAdmin]\n    swagger_schema = None\n\n    def get_serializer_class(self):\n        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n        self.serializer_class = project.get_annotation_serializer()\n        return self.serializer_class\n\n    def get_queryset(self):\n        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n        model = project.get_annotation_class()\n        queryset = model.objects.filter(document=self.kwargs['doc_id'])\n        if not project.collaborative_annotation:\n            queryset = queryset.filter(user=self.request.user)\n        return queryset\n\n    def create(self, request, *args, **kwargs):\n        self.check_single_class_classification(self.kwargs['project_id'], self.kwargs['doc_id'], request.user)\n        request.data['document'] = self.kwargs['doc_id']\n        return super().create(request, args, kwargs)\n\n    def perform_create(self, serializer):\n        serializer.save(document_id=self.kwargs['doc_id'], user=self.request.user)\n\n    def delete(self, request, *args, **kwargs):\n        queryset = self.get_queryset()\n        queryset.all().delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    @staticmethod\n    def check_single_class_classification(project_id, doc_id, user):\n        project = get_object_or_404(Project, pk=project_id)\n        if not project.single_class_classification:\n            return\n\n        model = project.get_annotation_class()\n        annotations = model.objects.filter(document_id=doc_id)\n        if not project.collaborative_annotation:\n            annotations = annotations.filter(user=user)\n\n        if annotations.exists():\n            raise ValidationError('requested to create duplicate annotation for single-class-classification project')\n\n\nclass AnnotationDetail(generics.RetrieveUpdateDestroyAPIView):\n    lookup_url_kwarg = 'annotation_id'\n    swagger_schema = None\n\n    def get_permissions(self):\n        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n        if project.collaborative_annotation:\n            self.permission_classes = [IsAuthenticated & IsInProjectOrAdmin]\n        else:\n            self.permission_classes = [IsAuthenticated & IsInProjectOrAdmin & IsOwnAnnotation]\n        return super().get_permissions()\n\n    def get_serializer_class(self):\n        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n        self.serializer_class = project.get_annotation_serializer()\n        return self.serializer_class\n\n    def get_queryset(self):\n        project = get_object_or_404(Project, pk=self.kwargs['project_id'])\n        model = project.get_annotation_class()\n        self.queryset = model.objects.all()\n        return self.queryset\n\n\nclass ApproveLabelsAPI(APIView):\n    permission_classes = [IsAuthenticated & (IsAnnotationApprover | IsProjectAdmin)]\n\n    def post(self, request, *args, **kwargs):\n        approved = self.request.data.get('approved', True)\n        document = get_object_or_404(Document, pk=self.kwargs['doc_id'])\n        document.annotations_approved_by = self.request.user if approved else None\n        document.save()\n        return Response(ApproverSerializer(document).data)\n", "path": "app/api/views/annotation.py"}]}