From b4d5fa34e67df45e07a8e18a39a3857ac8ce6ee3 Mon Sep 17 00:00:00 2001 From: Hironsan Date: Tue, 21 Dec 2021 08:49:01 +0900 Subject: [PATCH] Divide Label model into two separated model --- .../api/migrations/0020_auto_20211220_2327.py | 36 +++++++++++++++++++ .../api/migrations/0020_label_task_type.py | 18 ---------- .../api/migrations/0021_auto_20211209_0644.py | 36 ------------------- .../api/migrations/0022_auto_20211210_0052.py | 21 ----------- backend/api/models.py | 19 +++++----- backend/api/serializers.py | 1 - backend/api/tests/api/test_label.py | 9 +---- backend/api/tests/test_models.py | 10 ++---- backend/api/views/label.py | 1 - backend/api/views/upload/label.py | 18 +++++----- backend/api/views/upload/writers.py | 2 +- 11 files changed, 59 insertions(+), 112 deletions(-) create mode 100644 backend/api/migrations/0020_auto_20211220_2327.py delete mode 100644 backend/api/migrations/0020_label_task_type.py delete mode 100644 backend/api/migrations/0021_auto_20211209_0644.py delete mode 100644 backend/api/migrations/0022_auto_20211210_0052.py diff --git a/backend/api/migrations/0020_auto_20211220_2327.py b/backend/api/migrations/0020_auto_20211220_2327.py new file mode 100644 index 00000000..a3030946 --- /dev/null +++ b/backend/api/migrations/0020_auto_20211220_2327.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.8 on 2021-12-20 23:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0019_auto_20211124_0506'), + ] + + operations = [ + migrations.CreateModel( + name='DocType', + fields=[ + ('label_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.label')), + ], + bases=('api.label',), + ), + migrations.CreateModel( + name='SpanType', + fields=[ + ('label_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='api.label')), + ], + bases=('api.label',), + ), + migrations.AlterUniqueTogether( + name='label', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='label', + constraint=models.UniqueConstraint(fields=('project', 'text'), name='unique_label'), + ), + ] diff --git a/backend/api/migrations/0020_label_task_type.py b/backend/api/migrations/0020_label_task_type.py deleted file mode 100644 index 7689f67b..00000000 --- a/backend/api/migrations/0020_label_task_type.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.8 on 2021-12-09 06:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0019_auto_20211124_0506'), - ] - - operations = [ - migrations.AddField( - model_name='label', - name='task_type', - field=models.CharField(choices=[('Category', 'Category'), ('Span', 'Span'), ('Relation', 'Relation')], default='Category', max_length=30), - ), - ] diff --git a/backend/api/migrations/0021_auto_20211209_0644.py b/backend/api/migrations/0021_auto_20211209_0644.py deleted file mode 100644 index 7b44e879..00000000 --- a/backend/api/migrations/0021_auto_20211209_0644.py +++ /dev/null @@ -1,36 +0,0 @@ -from django.db import migrations - - -def update_label_type(apps, schema_editor): - Label = apps.get_model('api', 'Label') - for label in Label.objects.all(): - project_type = label.project.project_type - if project_type.endswith('Classification'): - label.task_type = 'Category' - else: - label.task_type = 'Span' - label.save() - - -# def move_relation_type_to_label(apps, schema_editor): -# Label = apps.get_model('api', 'Label') -# RelationTypes = apps.get_model('api', 'RelationTypes') -# for relation_type in RelationTypes.objects.all(): -# Label.objects.create( -# text=relation_type.name, -# project=relation_type.project, -# background_color=relation_type.color, -# task_type='Relation' -# ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0020_label_task_type'), - ] - - operations = [ - migrations.RunPython(update_label_type), - # migrations.RunPython(move_relation_type_to_label), - ] diff --git a/backend/api/migrations/0022_auto_20211210_0052.py b/backend/api/migrations/0022_auto_20211210_0052.py deleted file mode 100644 index a8bd942d..00000000 --- a/backend/api/migrations/0022_auto_20211210_0052.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.8 on 2021-12-10 00:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0021_auto_20211209_0644'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='label', - unique_together=set(), - ), - migrations.AddConstraint( - model_name='label', - constraint=models.UniqueConstraint(fields=('project', 'text', 'task_type'), name='unique_label'), - ), - ] diff --git a/backend/api/models.py b/backend/api/models.py index 81b6b05d..b4485597 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -126,15 +126,6 @@ class Label(models.Model): ) background_color = models.CharField(max_length=7, default=generate_random_hex_color) text_color = models.CharField(max_length=7, default='#ffffff') - task_type = models.CharField( - max_length=30, - choices=( - ('Category', 'Category'), - ('Span', 'Span'), - ('Relation', 'Relation') - ), - default='Category' - ) created_at = models.DateTimeField(auto_now_add=True, db_index=True) updated_at = models.DateTimeField(auto_now=True) @@ -159,13 +150,21 @@ class Label(models.Model): class Meta: constraints = [ models.UniqueConstraint( - fields=['project', 'text', 'task_type'], + fields=['project', 'text'], name='unique_label' ) ] ordering = ['created_at'] +class DocType(Label): + pass + + +class SpanType(Label): + pass + + class Example(models.Model): objects = ExampleManager() diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 8f6ba68a..b41036b3 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -67,7 +67,6 @@ class LabelSerializer(serializers.ModelSerializer): 'suffix_key', 'background_color', 'text_color', - 'task_type', ) diff --git a/backend/api/tests/api/test_label.py b/backend/api/tests/api/test_label.py index dd5a127b..4ffa65c5 100644 --- a/backend/api/tests/api/test_label.py +++ b/backend/api/tests/api/test_label.py @@ -43,17 +43,10 @@ class TestLabelSearch(CRUDMixin): def setUp(self): self.project = prepare_project() - make_label(self.project.item, task_type='Category') - make_label(self.project.item, task_type='Span') + make_label(self.project.item) self.url = reverse(viewname='label_list', args=[self.project.item.id]) def test_search(self): - for member in self.project.users: - response = self.assert_fetch(member, status.HTTP_200_OK) - self.assertEqual(len(response.data), 2) - - def test_search_label_by_type(self): - self.url = f'{self.url}?task_type=Category' for member in self.project.users: response = self.assert_fetch(member, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) diff --git a/backend/api/tests/test_models.py b/backend/api/tests/test_models.py index 013b6338..5bb302b1 100644 --- a/backend/api/tests/test_models.py +++ b/backend/api/tests/test_models.py @@ -58,14 +58,10 @@ class TestSpeech2textProject(TestCase): class TestLabel(TestCase): - def test_allow_creating_same_text_different_type(self): - label = mommy.make('Label', task_type='Category') - mommy.make('Label', project=label.project, text=label.text, task_type='Span') - - def test_deny_creating_same_text_same_type(self): - label = mommy.make('Label', task_type='Category') + def test_deny_creating_same_text(self): + label = mommy.make('Label') with self.assertRaises(IntegrityError): - mommy.make('Label', project=label.project, text=label.text, task_type='Category') + mommy.make('Label', project=label.project, text=label.text) def test_keys_uniqueness(self): label = mommy.make('Label', prefix_key='ctrl', suffix_key='a') diff --git a/backend/api/views/label.py b/backend/api/views/label.py index 23304f71..575a0984 100644 --- a/backend/api/views/label.py +++ b/backend/api/views/label.py @@ -28,7 +28,6 @@ def camel_to_snake_dict(d): class LabelList(generics.ListCreateAPIView): filter_backends = [DjangoFilterBackend] - filterset_fields = ['task_type'] serializer_class = LabelSerializer pagination_class = None permission_classes = [IsAuthenticated & IsInProjectReadOnlyOrAdmin] diff --git a/backend/api/views/upload/label.py b/backend/api/views/upload/label.py index 815a0ce4..8e25f5d4 100644 --- a/backend/api/views/upload/label.py +++ b/backend/api/views/upload/label.py @@ -1,11 +1,11 @@ import abc -from typing import Any, Optional, Union +from typing import Any, Dict, Optional, Union from pydantic import BaseModel, validator -from ...models import Category +from ...models import Category, DocType from ...models import Label as LabelModel -from ...models import Project, Span +from ...models import Project, Span, SpanType from ...models import TextLabel as TL @@ -63,13 +63,13 @@ class CategoryLabel(Label): raise TypeError(f'{obj} is not str.') def create(self, project: Project) -> Optional[LabelModel]: - return LabelModel(text=self.label, project=project, task_type='Category') + return DocType(text=self.label, project=project) - def create_annotation(self, user, example, mapping): + def create_annotation(self, user, example, mapping: Dict[str, LabelModel]): return Category( user=user, example=example, - label=mapping[(self.label, 'Category')] + label=mapping[self.label] ) @@ -97,15 +97,15 @@ class SpanLabel(Label): raise TypeError(f'{obj} is invalid type.') def create(self, project: Project) -> Optional[LabelModel]: - return LabelModel(text=self.label, project=project, task_type='Span') + return SpanType(text=self.label, project=project) - def create_annotation(self, user, example, mapping): + def create_annotation(self, user, example, mapping: Dict[str, LabelModel]): return Span( user=user, example=example, start_offset=self.start_offset, end_offset=self.end_offset, - label=mapping[(self.label, 'Span')] + label=mapping[self.label] ) diff --git a/backend/api/views/upload/writers.py b/backend/api/views/upload/writers.py index 5b662353..75afe765 100644 --- a/backend/api/views/upload/writers.py +++ b/backend/api/views/upload/writers.py @@ -64,7 +64,7 @@ class Examples: return Example.objects.bulk_create(examples) def save_annotation(self, project: Project, user, examples): - mapping = {(label.text, label.task_type): label for label in project.labels.all()} + mapping = {label.text: label for label in project.labels.all()} annotations = list(itertools.chain.from_iterable([ data.create_annotation(user, example, mapping) for data, example in zip(self.buffer, examples) ]))