Browse Source

#27642 annotation_relations.py get, post, delete

pull/1384/head
descansodj@hotmail.it 4 years ago
parent
commit
95f9b38329
7 changed files with 115 additions and 19 deletions
  1. 5
      app/api/exceptions.py
  2. 7
      app/api/migrations/0009_annotations_relations_20210421_1445.py
  3. 29
      app/api/models.py
  4. 14
      app/api/serializers.py
  5. 15
      app/api/urls.py
  6. 1
      app/api/views/__init__.py
  7. 63
      app/api/views/annotation_relations.py

5
app/api/exceptions.py

@ -41,6 +41,11 @@ class LabelValidationError(APIException):
default_detail = 'You cannot create a label with same name or shortcut key.' default_detail = 'You cannot create a label with same name or shortcut key.'
class AnnotationRelationValidationError(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = 'You cannot create an annotation relation between the same annotation.'
class RelationTypesValidationError(APIException): class RelationTypesValidationError(APIException):
status_code = status.HTTP_400_BAD_REQUEST status_code = status.HTTP_400_BAD_REQUEST
default_detail = 'You cannot create a relation type with same name or color.' default_detail = 'You cannot create a relation type with same name or color.'

7
app/api/migrations/0009_annotations_relations_20210421_1445.py

@ -26,9 +26,10 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('annotation_id_1', models.IntegerField()), ('annotation_id_1', models.IntegerField()),
('annotation_id_2', models.IntegerField()), ('annotation_id_2', models.IntegerField()),
('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.RelationTypes')),
('timestamp', models.DateTimeField(auto_now_add=True))
('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.RelationTypes')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('timestamp', models.DateTimeField(auto_now_add=True)),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='annotation_relations', to='api.Project'))
], ],
) )
] ]

29
app/api/models.py

@ -265,20 +265,6 @@ class SequenceAnnotation(Annotation):
unique_together = ('document', 'user', 'label', 'start_offset', 'end_offset') unique_together = ('document', 'user', 'label', 'start_offset', 'end_offset')
class AnnotationRelations(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
annotation_id_1 = models.IntegerField()
annotation_id_2 = models.IntegerField()
type = models.CharField(max_length=50)
def __str__(self):
return f"{self.annotation_id_1} - {self.annotation_id_2} - {type}"
class Meta:
unique_together = ('timestamp', 'user', 'annotation_id_1', 'annotation_id_2', 'type')
class RelationTypes(models.Model): class RelationTypes(models.Model):
color = models.TextField() color = models.TextField()
name = models.TextField() name = models.TextField()
@ -291,6 +277,21 @@ class RelationTypes(models.Model):
unique_together = ('color', 'name') unique_together = ('color', 'name')
class AnnotationRelations(models.Model):
annotation_id_1 = models.IntegerField()
annotation_id_2 = models.IntegerField()
type = models.ForeignKey(RelationTypes, related_name='annotation_relations', on_delete=models.CASCADE)
timestamp = models.DateTimeField()
user = models.ForeignKey(User, related_name='annotation_relations', on_delete=models.CASCADE)
project = models.ForeignKey(Project, related_name='annotation_relations', on_delete=models.CASCADE)
def __str__(self):
return self.__dict__.__str__()
class Meta:
unique_together = ('annotation_id_1', 'annotation_id_2', 'type', 'project')
class Seq2seqAnnotation(Annotation): class Seq2seqAnnotation(Annotation):
# Override AnnotationManager for custom functionality # Override AnnotationManager for custom functionality
objects = Seq2seqAnnotationManager() objects = Seq2seqAnnotationManager()

14
app/api/serializers.py

@ -10,7 +10,7 @@ from .models import (AutoLabelingConfig, Comment, Document, DocumentAnnotation,
Label, Project, Role, RoleMapping, Seq2seqAnnotation, Label, Project, Role, RoleMapping, Seq2seqAnnotation,
Seq2seqProject, SequenceAnnotation, Seq2seqProject, SequenceAnnotation,
SequenceLabelingProject, Speech2textAnnotation, SequenceLabelingProject, Speech2textAnnotation,
Speech2textProject, TextClassificationProject, RelationTypes)
Speech2textProject, TextClassificationProject, RelationTypes, AnnotationRelations)
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
@ -286,4 +286,14 @@ class RelationTypesSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = RelationTypes model = RelationTypes
fields = ('id', 'color', 'name')
fields = ('id', 'color', 'name')
class AnnotationRelationsSerializer(serializers.ModelSerializer):
def validate(self, attrs):
return super().validate(attrs)
class Meta:
model = AnnotationRelations
fields = ('id', 'annotation_id_1', 'annotation_id_2', 'type', 'user', 'timestamp')

15
app/api/urls.py

@ -38,6 +38,21 @@ urlpatterns_project = [
view=views.RelationTypesDetail.as_view(), view=views.RelationTypesDetail.as_view(),
name='relation_type_detail' name='relation_type_detail'
), ),
path(
route='annotation_relations',
view=views.AnnotationRelationsList.as_view(),
name='relation_types_list'
),
path(
route='annotation_relation-upload',
view=views.AnnotationRelationsUploadAPI.as_view(),
name='annotation_relation-upload'
),
path(
route='annotation_relations/<int:annotation_relation_id>',
view=views.AnnotationRelationsDetail.as_view(),
name='annotation_relation_detail'
),
path( path(
route='docs', route='docs',
view=views.DocumentList.as_view(), view=views.DocumentList.as_view(),

1
app/api/views/__init__.py

@ -10,3 +10,4 @@ from .role import *
from .statistics import * from .statistics import *
from .user import * from .user import *
from .relation_types import * from .relation_types import *
from .annotation_relations import *

63
app/api/views/annotation_relations.py

@ -0,0 +1,63 @@
import json
import logging
from django.db import IntegrityError, transaction
from django.shortcuts import get_object_or_404
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 ..exceptions import AnnotationRelationValidationError
from ..models import AnnotationRelations, Project
from ..permissions import IsInProjectReadOnlyOrAdmin, IsProjectAdmin
from ..serializers import AnnotationRelationsSerializer
class AnnotationRelationsList(generics.ListCreateAPIView):
serializer_class = AnnotationRelationsSerializer
pagination_class = None
permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin]
def get_queryset(self):
project = get_object_or_404(Project, pk=self.kwargs['project_id'])
return project.annotation_relations
def perform_create(self, serializer):
project = get_object_or_404(Project, pk=self.kwargs['project_id'])
serializer.save(project=project)
def delete(self, request, *args, **kwargs):
delete_ids = request.data['ids']
AnnotationRelations.objects.filter(pk__in=delete_ids).delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class AnnotationRelationsDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = AnnotationRelations.objects.all()
serializer_class = AnnotationRelationsSerializer
lookup_url_kwarg = 'annotation_relation_id'
permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin]
class AnnotationRelationsUploadAPI(APIView):
parser_classes = (MultiPartParser,)
permission_classes = [IsAuthenticated & IsProjectAdmin]
@transaction.atomic
def post(self, request, *args, **kwargs):
if 'file' not in request.data:
raise ParseError('Empty content')
project = get_object_or_404(Project, pk=kwargs['project_id'])
try:
annotation_relations = json.load(request.data)
serializer = AnnotationRelationsSerializer(data=annotation_relations, many=True)
serializer.is_valid(raise_exception=True)
serializer.save(project=project)
return Response(status=status.HTTP_201_CREATED)
except json.decoder.JSONDecodeError:
raise ParseError('The file format is invalid.')
except IntegrityError:
raise AnnotationRelationValidationError
Loading…
Cancel
Save