Browse Source

Add Span API

pull/1592/head
Hironsan 2 years ago
parent
commit
dfb2966096
3 changed files with 103 additions and 9 deletions
  1. 87
      backend/api/tests/api/test_annotation.py
  2. 12
      backend/api/urls.py
  3. 13
      backend/api/views/tasks/span.py

87
backend/api/tests/api/test_annotation.py

@ -1,12 +1,14 @@
from rest_framework import status
from rest_framework.reverse import reverse
from ...models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, Category
from ...models import (DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, Category,
Span)
from .utils import (CRUDMixin, make_annotation, make_doc, make_label,
make_user, prepare_project)
class TestAnnotationList(CRUDMixin):
model = Category
task = DOCUMENT_CLASSIFICATION
view_name = 'annotation_list'
@ -16,9 +18,13 @@ class TestAnnotationList(CRUDMixin):
cls.non_member = make_user()
doc = make_doc(cls.project.item)
for member in cls.project.users:
make_annotation(task=cls.task, doc=doc, user=member)
cls.make_annotation(doc, member)
cls.url = reverse(viewname=cls.view_name, args=[cls.project.item.id, doc.id])
@classmethod
def make_annotation(cls, doc, member):
make_annotation(cls.task, doc=doc, user=member)
def test_allows_project_member_to_fetch_annotation(self):
for member in self.project.users:
response = self.assert_fetch(member, status.HTTP_200_OK)
@ -32,15 +38,28 @@ class TestAnnotationList(CRUDMixin):
def test_allows_project_member_to_bulk_delete_annotation(self):
self.assert_delete(self.project.users[0], status.HTTP_204_NO_CONTENT)
count = Category.objects.count()
count = self.model.objects.count()
self.assertEqual(count, 2) # delete only own annotation
class TestCategoryList(TestAnnotationList):
model = Category
task = DOCUMENT_CLASSIFICATION
view_name = 'category_list'
class TestSpanList(TestAnnotationList):
model = Span
task = SEQUENCE_LABELING
view_name = 'span_list'
@classmethod
def make_annotation(cls, doc, member):
make_annotation(cls.task, doc=doc, user=member, start_offset=0, end_offset=1)
class TestSharedAnnotationList(CRUDMixin):
model = Category
task = DOCUMENT_CLASSIFICATION
view_name = 'annotation_list'
@ -49,9 +68,13 @@ class TestSharedAnnotationList(CRUDMixin):
cls.project = prepare_project(task=cls.task, collaborative_annotation=True)
doc = make_doc(cls.project.item)
for member in cls.project.users:
make_annotation(task=cls.task, doc=doc, user=member)
cls.make_annotation(doc, member)
cls.url = reverse(viewname=cls.view_name, args=[cls.project.item.id, doc.id])
@classmethod
def make_annotation(cls, doc, member):
make_annotation(cls.task, doc=doc, user=member)
def test_allows_project_member_to_fetch_all_annotation(self):
for member in self.project.users:
response = self.assert_fetch(member, status.HTTP_200_OK)
@ -59,14 +82,34 @@ class TestSharedAnnotationList(CRUDMixin):
def test_allows_project_member_to_bulk_delete_annotation(self):
self.assert_delete(self.project.users[0], status.HTTP_204_NO_CONTENT)
count = Category.objects.count()
count = self.model.objects.count()
self.assertEqual(count, 0) # delete all annotation in the doc
class TestSharedCategoryList(TestSharedAnnotationList):
model = Category
task = DOCUMENT_CLASSIFICATION
view_name = 'category_list'
class TestSharedSpanList(TestSharedCategoryList):
model = Span
task = SEQUENCE_LABELING
view_name = 'span_list'
start_offset = 0
@classmethod
def make_annotation(cls, doc, member):
make_annotation(
cls.task,
doc=doc,
user=member,
start_offset=cls.start_offset,
end_offset=cls.start_offset + 1
)
cls.start_offset += 1
class TestAnnotationCreation(CRUDMixin):
task = DOCUMENT_CLASSIFICATION
view_name = 'annotation_list'
@ -75,10 +118,13 @@ class TestAnnotationCreation(CRUDMixin):
self.project = prepare_project(task=self.task)
self.non_member = make_user()
doc = make_doc(self.project.item)
label = make_label(self.project.item)
self.data = {'label': label.id}
self.data = self.create_data()
self.url = reverse(viewname=self.view_name, args=[self.project.item.id, doc.id])
def create_data(self):
label = make_label(self.project.item)
return {'label': label.id}
def test_allows_project_member_to_annotate(self):
for member in self.project.users:
self.assert_create(member, status.HTTP_201_CREATED)
@ -94,6 +140,15 @@ class TestCategoryCreation(TestAnnotationCreation):
view_name = 'category_list'
class TestSpanCreation(TestAnnotationCreation):
task = SEQUENCE_LABELING
view_name = 'span_list'
def create_data(self):
label = make_label(self.project.item)
return {'label': label.id, 'start_offset': 0, 'end_offset': 1}
class TestAnnotationDetail(CRUDMixin):
task = SEQUENCE_LABELING
view_name = 'annotation_detail'
@ -158,6 +213,11 @@ class TestCategoryDetail(TestAnnotationDetail):
return make_annotation(task=self.task, doc=doc, user=self.project.users[0])
class TestSpanDetail(TestAnnotationDetail):
task = SEQUENCE_LABELING
view_name = 'span_detail'
class TestSharedAnnotationDetail(CRUDMixin):
task = DOCUMENT_CLASSIFICATION
view_name = 'annotation_detail'
@ -165,11 +225,14 @@ class TestSharedAnnotationDetail(CRUDMixin):
def setUp(self):
self.project = prepare_project(task=self.task, collaborative_annotation=True)
doc = make_doc(self.project.item)
annotation = make_annotation(task=self.task, doc=doc, user=self.project.users[0])
annotation = self.make_annotation(doc, self.project.users[0])
label = make_label(self.project.item)
self.data = {'label': label.id}
self.url = reverse(viewname=self.view_name, args=[self.project.item.id, doc.id, annotation.id])
def make_annotation(self, doc, member):
return make_annotation(self.task, doc=doc, user=member)
def test_allows_any_member_to_get_annotation(self):
for member in self.project.users:
self.assert_fetch(member, status.HTTP_200_OK)
@ -184,3 +247,11 @@ class TestSharedAnnotationDetail(CRUDMixin):
class TestSharedCategoryDetail(TestSharedAnnotationDetail):
view_name = 'category_detail'
class TestSharedSpanDetail(TestSharedAnnotationDetail):
task = SEQUENCE_LABELING
view_name = 'span_detail'
def make_annotation(self, doc, member):
return make_annotation(self.task, doc=doc, user=member, start_offset=0, end_offset=1)

12
backend/api/urls.py

@ -4,7 +4,7 @@ from .views import (annotation, annotation_relations, auto_labeling, comment,
example, example_state, export_dataset, health,
import_dataset, import_export, label, project,
relation_types, role, statistics, tag, task, user)
from .views.tasks import category
from .views.tasks import category, span
urlpatterns_project = [
path(
@ -123,6 +123,16 @@ urlpatterns_project = [
view=category.CategoryDetailAPI.as_view(),
name='category_detail'
),
path(
route='examples/<int:example_id>/spans',
view=span.SpanListAPI.as_view(),
name='span_list'
),
path(
route='examples/<int:example_id>/spans/<int:annotation_id>',
view=span.SpanDetailAPI.as_view(),
name='span_detail'
),
path(
route='tags',
view=tag.TagList.as_view(),

13
backend/api/views/tasks/span.py

@ -0,0 +1,13 @@
from ...models import Span
from ...serializers import SpanSerializer
from .base import BaseDetailAPI, BaseListAPI
class SpanListAPI(BaseListAPI):
annotation_class = Span
serializer_class = SpanSerializer
class SpanDetailAPI(BaseDetailAPI):
queryset = Span.objects.all()
serializer_class = SpanSerializer
Loading…
Cancel
Save