Browse Source

Move project models to projects app

pull/1671/head
Hironsan 2 years ago
parent
commit
99f77b2bd2
64 changed files with 806 additions and 371 deletions
  1. 23
      backend/api/admin.py
  2. 54
      backend/api/migrations/0036_auto_20220204_0201.py
  3. 81
      backend/api/migrations/0037_auto_20220204_0201.py
  4. 157
      backend/api/models.py
  5. 95
      backend/api/serializers.py
  6. 7
      backend/api/tests/api/utils.py
  7. 28
      backend/api/urls.py
  8. 2
      backend/app/urls.py
  9. 2
      backend/auto_labeling/migrations/0003_fill_task_type.py
  10. 26
      backend/auto_labeling/migrations/0004_alter_autolabelingconfig_project.py
  11. 2
      backend/auto_labeling/models.py
  12. 2
      backend/auto_labeling/pipeline/labels.py
  13. 2
      backend/auto_labeling/tests/test_views.py
  14. 2
      backend/auto_labeling/views.py
  15. 2
      backend/data_export/celery_tasks.py
  16. 5
      backend/data_export/pipeline/catalog.py
  17. 5
      backend/data_export/pipeline/factories.py
  18. 2
      backend/data_export/pipeline/repositories.py
  19. 2
      backend/data_export/tests/test_repositories.py
  20. 2
      backend/data_export/tests/test_views.py
  21. 2
      backend/data_export/views.py
  22. 2
      backend/data_import/celery_tasks.py
  23. 5
      backend/data_import/pipeline/catalog.py
  24. 2
      backend/data_import/pipeline/cleaners.py
  25. 2
      backend/data_import/pipeline/data.py
  26. 5
      backend/data_import/pipeline/factories.py
  27. 2
      backend/data_import/pipeline/labels.py
  28. 2
      backend/data_import/pipeline/writers.py
  29. 5
      backend/data_import/tests/test_tasks.py
  30. 2
      backend/data_import/tests/test_views.py
  31. 3
      backend/data_import/views.py
  32. 29
      backend/examples/migrations/0002_alter_example_project.py
  33. 2
      backend/examples/models.py
  34. 2
      backend/examples/tests/test_document.py
  35. 2
      backend/examples/tests/test_models.py
  36. 2
      backend/examples/views/example.py
  37. 2
      backend/examples/views/example_state.py
  38. 36
      backend/label_types/migrations/0003_auto_20220204_0201.py
  39. 2
      backend/label_types/models.py
  40. 2
      backend/label_types/tests/test_views.py
  41. 26
      backend/labels/migrations/0005_alter_relation_project.py
  42. 2
      backend/labels/models.py
  43. 2
      backend/labels/tests/test_category.py
  44. 2
      backend/labels/tests/test_span.py
  45. 2
      backend/labels/tests/test_text_label.py
  46. 2
      backend/labels/tests/test_views.py
  47. 2
      backend/labels/views.py
  48. 2
      backend/metrics/tests.py
  49. 18
      backend/projects/admin.py
  50. 2
      backend/projects/apps.py
  51. 2
      backend/projects/management/commands/create_member.py
  52. 155
      backend/projects/migrations/0002_auto_20220204_0201.py
  53. 49
      backend/projects/migrations/0003_auto_20220204_0551.py
  54. 157
      backend/projects/models.py
  55. 91
      backend/projects/serializers.py
  56. 2
      backend/projects/signals.py
  57. 0
      backend/projects/tests/__init__.py
  58. 0
      backend/projects/tests/test_member.py
  59. 2
      backend/projects/tests/test_project.py
  60. 2
      backend/projects/tests/test_tag.py
  61. 29
      backend/projects/urls.py
  62. 8
      backend/projects/views/member.py
  63. 4
      backend/projects/views/project.py
  64. 4
      backend/projects/views/tag.py

23
backend/api/admin.py

@ -1,23 +0,0 @@
from django.contrib import admin
from .models import (Project, Seq2seqProject, SequenceLabelingProject, Tag,
TextClassificationProject)
class ProjectAdmin(admin.ModelAdmin):
list_display = ('name', 'description', 'project_type', 'random_order', 'collaborative_annotation')
ordering = ('project_type',)
search_fields = ('name',)
class TagAdmin(admin.ModelAdmin):
list_display = ('project', 'text', )
ordering = ('project', 'text', )
search_fields = ('text',)
admin.site.register(Project, ProjectAdmin)
admin.site.register(TextClassificationProject, ProjectAdmin)
admin.site.register(SequenceLabelingProject, ProjectAdmin)
admin.site.register(Seq2seqProject, ProjectAdmin)
admin.site.register(Tag, TagAdmin)

54
backend/api/migrations/0036_auto_20220204_0201.py

@ -0,0 +1,54 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('api', '0035_auto_20220128_0246'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.RemoveField(
model_name='imageclassificationproject',
name='project_ptr',
),
migrations.RemoveField(
model_name='intentdetectionandslotfillingproject',
name='project_ptr',
),
migrations.RemoveField(
model_name='project',
name='created_by',
),
migrations.RemoveField(
model_name='project',
name='polymorphic_ctype',
),
migrations.RemoveField(
model_name='seq2seqproject',
name='project_ptr',
),
migrations.RemoveField(
model_name='sequencelabelingproject',
name='project_ptr',
),
migrations.RemoveField(
model_name='speech2textproject',
name='project_ptr',
),
migrations.RemoveField(
model_name='tag',
name='project',
),
migrations.RemoveField(
model_name='textclassificationproject',
name='project_ptr',
),
],
database_operations=[]
)
]

81
backend/api/migrations/0037_auto_20220204_0201.py

@ -0,0 +1,81 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('examples', '0002_alter_example_project'),
('labels', '0005_alter_relation_project'),
('projects', '0002_auto_20220204_0201'),
('auto_labeling', '0004_alter_autolabelingconfig_project'),
('api', '0036_auto_20220204_0201'),
('label_types', '0003_auto_20220204_0201'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.DeleteModel(
name='ImageClassificationProject',
),
migrations.DeleteModel(
name='IntentDetectionAndSlotFillingProject',
),
migrations.DeleteModel(
name='Project',
),
migrations.DeleteModel(
name='Seq2seqProject',
),
migrations.DeleteModel(
name='SequenceLabelingProject',
),
migrations.DeleteModel(
name='Speech2textProject',
),
migrations.DeleteModel(
name='Tag',
),
migrations.DeleteModel(
name='TextClassificationProject',
),
],
database_operations=[
migrations.AlterModelTable(
name='ImageClassificationProject',
table='projects_imageclassificationproject'
),
migrations.AlterModelTable(
name='IntentDetectionAndSlotFillingProject',
table='projects_intentdetectionandslotfillingproject'
),
migrations.AlterModelTable(
name='Project',
table='projects_project'
),
migrations.AlterModelTable(
name='Seq2seqProject',
table='projects_seq2seqproject'
),
migrations.AlterModelTable(
name='SequenceLabelingProject',
table='projects_sequencelabelingproject'
),
migrations.AlterModelTable(
name='Speech2textProject',
table='projects_speech2textproject'
),
migrations.AlterModelTable(
name='Tag',
table='projects_tag'
),
migrations.AlterModelTable(
name='TextClassificationProject',
table='projects_textclassificationproject'
),
]
)
]

157
backend/api/models.py

@ -1,157 +0,0 @@
import abc
from django.contrib.auth.models import User
from django.db import models
from polymorphic.models import PolymorphicModel
DOCUMENT_CLASSIFICATION = 'DocumentClassification'
SEQUENCE_LABELING = 'SequenceLabeling'
SEQ2SEQ = 'Seq2seq'
SPEECH2TEXT = 'Speech2text'
IMAGE_CLASSIFICATION = 'ImageClassification'
INTENT_DETECTION_AND_SLOT_FILLING = 'IntentDetectionAndSlotFilling'
PROJECT_CHOICES = (
(DOCUMENT_CLASSIFICATION, 'document classification'),
(SEQUENCE_LABELING, 'sequence labeling'),
(SEQ2SEQ, 'sequence to sequence'),
(INTENT_DETECTION_AND_SLOT_FILLING, 'intent detection and slot filling'),
(SPEECH2TEXT, 'speech to text'),
(IMAGE_CLASSIFICATION, 'image classification')
)
class Project(PolymorphicModel):
name = models.CharField(max_length=100)
description = models.TextField(default='')
guideline = models.TextField(default='', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
)
project_type = models.CharField(max_length=30, choices=PROJECT_CHOICES)
random_order = models.BooleanField(default=False)
collaborative_annotation = models.BooleanField(default=False)
single_class_classification = models.BooleanField(default=False)
@property
@abc.abstractmethod
def is_text_project(self) -> bool:
return False
@property
def can_define_label(self) -> bool:
"""Whether or not the project can define label(ignoring the type of label)"""
return False
@property
def can_define_relation(self) -> bool:
"""Whether or not the project can define relation."""
return False
@property
def can_define_category(self) -> bool:
"""Whether or not the project can define category."""
return False
@property
def can_define_span(self) -> bool:
"""Whether or not the project can define span."""
return False
def __str__(self):
return self.name
class TextClassificationProject(Project):
@property
def is_text_project(self) -> bool:
return True
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_category(self) -> bool:
return True
class SequenceLabelingProject(Project):
allow_overlapping = models.BooleanField(default=False)
grapheme_mode = models.BooleanField(default=False)
@property
def is_text_project(self) -> bool:
return True
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_span(self) -> bool:
return True
class Seq2seqProject(Project):
@property
def is_text_project(self) -> bool:
return True
class IntentDetectionAndSlotFillingProject(Project):
@property
def is_text_project(self) -> bool:
return True
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_category(self) -> bool:
return True
@property
def can_define_span(self) -> bool:
return True
class Speech2textProject(Project):
@property
def is_text_project(self) -> bool:
return False
class ImageClassificationProject(Project):
@property
def is_text_project(self) -> bool:
return False
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_category(self) -> bool:
return True
class Tag(models.Model):
text = models.TextField()
project = models.ForeignKey(
to=Project,
on_delete=models.CASCADE,
related_name='tags'
)
def __str__(self):
return self.text

95
backend/api/serializers.py

@ -1,95 +0,0 @@
from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
from .models import (ImageClassificationProject,
IntentDetectionAndSlotFillingProject, Project,
Seq2seqProject, SequenceLabelingProject,
Speech2textProject, Tag, TextClassificationProject)
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('id', 'project', 'text', )
read_only_fields = ('id', 'project')
class ProjectSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, required=False)
class Meta:
model = Project
fields = (
'id',
'name',
'description',
'guideline',
'project_type',
'updated_at',
'random_order',
'created_by',
'collaborative_annotation',
'single_class_classification',
'is_text_project',
'can_define_label',
'can_define_relation',
'can_define_category',
'can_define_span',
'tags'
)
read_only_fields = (
'updated_at',
'is_text_project',
'can_define_label',
'can_define_relation',
'can_define_category',
'can_define_span',
'tags'
)
class TextClassificationProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = TextClassificationProject
class SequenceLabelingProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = SequenceLabelingProject
fields = ProjectSerializer.Meta.fields + ('allow_overlapping', 'grapheme_mode')
class Seq2seqProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = Seq2seqProject
class IntentDetectionAndSlotFillingProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = IntentDetectionAndSlotFillingProject
class Speech2textProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = Speech2textProject
class ImageClassificationProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = ImageClassificationProject
class ProjectPolymorphicSerializer(PolymorphicSerializer):
model_serializer_mapping = {
Project: ProjectSerializer,
**{
cls.Meta.model: cls for cls in ProjectSerializer.__subclasses__()
}
}

7
backend/api/tests/api/utils.py

@ -7,10 +7,9 @@ from model_mommy import mommy
from rest_framework import status
from rest_framework.test import APITestCase
from api.models import (DOCUMENT_CLASSIFICATION, IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, SPEECH2TEXT)
from projects.models import Member
from projects.models import (DOCUMENT_CLASSIFICATION, IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, SPEECH2TEXT, Member)
from roles.models import Role
DATA_DIR = os.path.join(os.path.dirname(__file__), '../../../data_import/tests/data')

28
backend/api/urls.py

@ -1,35 +1,11 @@
from django.urls import include, path
from django.urls import path
from .views import project, tag, task
urlpatterns_project = [
path(
route='tags',
view=tag.TagList.as_view(),
name='tag_list'
),
path(
route='tags/<int:tag_id>',
view=tag.TagDetail.as_view(),
name='tag_detail'
),
]
from .views import task
urlpatterns = [
path(
route='projects',
view=project.ProjectList.as_view(),
name='project_list'
),
path(
route='tasks/status/<task_id>',
view=task.TaskStatus.as_view(),
name='task_status'
),
path(
route='projects/<int:project_id>',
view=project.ProjectDetail.as_view(),
name='project_detail'
),
path('projects/<int:project_id>/', include(urlpatterns_project))
]

2
backend/app/urls.py

@ -55,7 +55,7 @@ urlpatterns += [
path('v1/', include('users.urls')),
path('v1/', include('data_import.urls')),
path('v1/', include('data_export.urls')),
path('v1/projects/<int:project_id>/', include('projects.urls')),
path('v1/', include('projects.urls')),
path('v1/projects/<int:project_id>/metrics/', include('metrics.urls')),
path('v1/projects/<int:project_id>/', include('auto_labeling.urls')),
path('v1/projects/<int:project_id>/', include('examples.urls')),

2
backend/auto_labeling/migrations/0003_fill_task_type.py

@ -1,6 +1,6 @@
from django.db import migrations
from api.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, SPEECH2TEXT, IMAGE_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, SPEECH2TEXT, IMAGE_CLASSIFICATION
def fill_task_type(apps, schema_editor):

26
backend/auto_labeling/migrations/0004_alter_autolabelingconfig_project.py

@ -0,0 +1,26 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('projects', '0002_auto_20220204_0201'),
('auto_labeling', '0003_fill_task_type'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='autolabelingconfig',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
related_name='auto_labeling_config', to='projects.project'),
),
],
database_operations=[]
)
]

2
backend/auto_labeling/models.py

@ -2,7 +2,7 @@ from auto_labeling_pipeline.models import RequestModelFactory
from django.core.exceptions import ValidationError
from django.db import models
from api.models import Project
from projects.models import Project
class AutoLabelingConfig(models.Model):

2
backend/auto_labeling/pipeline/labels.py

@ -4,7 +4,7 @@ from typing import List
from auto_labeling_pipeline.labels import Labels
from django.contrib.auth.models import User
from api.models import Project
from projects.models import Project
from examples.models import Example
from label_types.models import CategoryType, SpanType
from labels.models import Label, Category, Span, TextLabel

2
backend/auto_labeling/tests/test_views.py

@ -7,7 +7,7 @@ from model_mommy import mommy
from rest_framework import status
from rest_framework.reverse import reverse
from api.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ
from labels.models import Category, Span, TextLabel
from api.tests.api.utils import CRUDMixin, make_doc, prepare_project
from auto_labeling.pipeline.labels import Categories, Spans, Texts

2
backend/auto_labeling/views.py

@ -15,7 +15,7 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from api.models import Project
from projects.models import Project
from projects.permissions import IsProjectMember, IsProjectAdmin
from .pipeline.execution import execute_pipeline, get_label_collection
from .exceptions import AWSTokenError, SampleDataException, TemplateMappingError, URLConnectionError

2
backend/data_export/celery_tasks.py

@ -3,7 +3,7 @@ from celery.utils.log import get_task_logger
from django.conf import settings
from django.shortcuts import get_object_or_404
from api.models import Project
from projects.models import Project
from .pipeline.factories import create_repository, create_writer
from .pipeline.services import ExportApplicationService

5
backend/data_export/pipeline/catalog.py

@ -4,9 +4,8 @@ from typing import Dict, List, Type
from pydantic import BaseModel
from typing_extensions import Literal
from api.models import (DOCUMENT_CLASSIFICATION, IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, SPEECH2TEXT)
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, SPEECH2TEXT, IMAGE_CLASSIFICATION, \
INTENT_DETECTION_AND_SLOT_FILLING
from . import examples

5
backend/data_export/pipeline/factories.py

@ -1,8 +1,7 @@
from typing import Type
from api.models import (DOCUMENT_CLASSIFICATION, IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, SPEECH2TEXT)
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, SPEECH2TEXT, IMAGE_CLASSIFICATION, \
INTENT_DETECTION_AND_SLOT_FILLING
from . import catalog, repositories, writers

2
backend/data_export/pipeline/repositories.py

@ -3,7 +3,7 @@ import itertools
from collections import defaultdict
from typing import Dict, Iterator, List
from api.models import Project
from projects.models import Project
from examples.models import Example
from .data import Record

2
backend/data_export/tests/test_repositories.py

@ -2,7 +2,7 @@ import unittest
from model_mommy import mommy
from api.models import INTENT_DETECTION_AND_SLOT_FILLING
from projects.models import INTENT_DETECTION_AND_SLOT_FILLING
from api.tests.api.utils import prepare_project
from ..pipeline.repositories import IntentDetectionSlotFillingRepository

2
backend/data_export/tests/test_views.py

@ -1,7 +1,7 @@
from rest_framework import status
from rest_framework.reverse import reverse
from api.models import DOCUMENT_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION
from api.tests.api.utils import CRUDMixin, prepare_project

2
backend/data_export/views.py

@ -6,7 +6,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from api.models import Project
from projects.models import Project
from projects.permissions import IsProjectAdmin
from .celery_tasks import export_dataset
from .pipeline.catalog import Options

2
backend/data_import/celery_tasks.py

@ -3,7 +3,7 @@ from django.conf import settings
from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from api.models import Project
from projects.models import Project
from .pipeline.factories import create_parser, create_builder, create_cleaner
from .pipeline.readers import Reader
from .pipeline.writers import BulkWriter

5
backend/data_import/pipeline/catalog.py

@ -4,9 +4,8 @@ from typing import Dict, List, Type
from pydantic import BaseModel
from typing_extensions import Literal
from api.models import (DOCUMENT_CLASSIFICATION, IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, SPEECH2TEXT)
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, SPEECH2TEXT, IMAGE_CLASSIFICATION, \
INTENT_DETECTION_AND_SLOT_FILLING
from . import examples
encodings = Literal[

2
backend/data_import/pipeline/cleaners.py

@ -1,6 +1,6 @@
from typing import List
from api.models import Project
from projects.models import Project
from .labels import CategoryLabel, Label, SpanLabel

2
backend/data_import/pipeline/data.py

@ -4,7 +4,7 @@ from typing import Any, Dict
from pydantic import BaseModel, validator
from api.models import Project
from projects.models import Project
from examples.models import Example

5
backend/data_import/pipeline/factories.py

@ -1,6 +1,5 @@
from api.models import (DOCUMENT_CLASSIFICATION, IMAGE_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, SPEECH2TEXT)
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, SPEECH2TEXT, IMAGE_CLASSIFICATION, \
INTENT_DETECTION_AND_SLOT_FILLING
from . import builders, catalog, cleaners, data, labels, parsers, readers

2
backend/data_import/pipeline/labels.py

@ -3,7 +3,7 @@ from typing import Any, Dict, Optional, Union
from pydantic import BaseModel, validator
from api.models import Project
from projects.models import Project
from label_types.models import LabelType, CategoryType, SpanType
from labels.models import Category, Span, TextLabel as TL

2
backend/data_import/pipeline/writers.py

@ -5,7 +5,7 @@ from typing import Any, Dict, List
from django.conf import settings
from api.models import Project
from projects.models import Project
from examples.models import Example
from label_types.models import CategoryType, SpanType
from .exceptions import FileParseException

5
backend/data_import/tests/test_tasks.py

@ -3,9 +3,8 @@ import pathlib
from django.test import TestCase
from data_import.celery_tasks import import_dataset
from api.models import (DOCUMENT_CLASSIFICATION,
INTENT_DETECTION_AND_SLOT_FILLING, SEQ2SEQ,
SEQUENCE_LABELING, IMAGE_CLASSIFICATION)
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ, IMAGE_CLASSIFICATION, \
INTENT_DETECTION_AND_SLOT_FILLING
from examples.models import Example
from label_types.models import CategoryType, SpanType
from labels.models import Category, Span

2
backend/data_import/tests/test_views.py

@ -1,7 +1,7 @@
from rest_framework import status
from rest_framework.reverse import reverse
from api.models import DOCUMENT_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION
from api.tests.api.utils import CRUDMixin, prepare_project

3
backend/data_import/views.py

@ -8,8 +8,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from api.models import Project
from projects.models import Project
from projects.permissions import IsProjectAdmin
from .celery_tasks import import_dataset
from .pipeline.catalog import Options

29
backend/examples/migrations/0002_alter_example_project.py

@ -0,0 +1,29 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('projects', '0002_auto_20220204_0201'),
('examples', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='example',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='examples',
to='projects.project'),
),
],
database_operations=[
]
)
]

2
backend/examples/models.py

@ -3,7 +3,7 @@ import uuid
from django.contrib.auth.models import User
from django.db import models
from api.models import Project
from projects.models import Project
from .managers import ExampleManager, ExampleStateManager

2
backend/examples/tests/test_document.py

@ -3,7 +3,7 @@ from django.utils.http import urlencode
from rest_framework import status
from rest_framework.reverse import reverse
from api.models import DOCUMENT_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION
from api.tests.api.utils import (CRUDMixin, assign_user_to_role, make_doc,
make_example_state, make_user, prepare_project)

2
backend/examples/tests/test_models.py

@ -1,7 +1,7 @@
from django.test import TestCase
from model_mommy import mommy
from api.models import IMAGE_CLASSIFICATION, SEQUENCE_LABELING
from projects.models import SEQUENCE_LABELING, IMAGE_CLASSIFICATION
from api.tests.api.utils import prepare_project
from examples.models import ExampleState

2
backend/examples/views/example.py

@ -7,7 +7,7 @@ from rest_framework import filters, generics, status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from api.models import Project
from projects.models import Project
from examples.filters import ExampleFilter
from examples.models import Example
from examples.serializers import ExampleSerializer

2
backend/examples/views/example_state.py

@ -2,7 +2,7 @@ from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from api.models import Project
from projects.models import Project
from examples.models import Example, ExampleState
from examples.serializers import ExampleStateSerializer
from projects.permissions import IsProjectMember

36
backend/label_types/migrations/0003_auto_20220204_0201.py

@ -0,0 +1,36 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('projects', '0002_auto_20220204_0201'),
('label_types', '0002_rename_relationtypes_relationtype'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='categorytype',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.project'),
),
migrations.AlterField(
model_name='relationtype',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='relation_types',
to='projects.project'),
),
migrations.AlterField(
model_name='spantype',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.project'),
),
],
database_operations=[]
)
]

2
backend/label_types/models.py

@ -4,7 +4,7 @@ import string
from django.core.exceptions import ValidationError
from django.db import models
from api.models import Project
from projects.models import Project
def generate_random_hex_color():

2
backend/label_types/tests/test_views.py

@ -5,7 +5,7 @@ from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase
from api.models import DOCUMENT_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION
from api.tests.api.utils import (DATA_DIR, CRUDMixin, make_label, make_project, make_user, prepare_project)

26
backend/labels/migrations/0005_alter_relation_project.py

@ -0,0 +1,26 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('projects', '0002_auto_20220204_0201'),
('labels', '0004_auto_20220128_0246'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='relation',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
related_name='annotation_relations', to='projects.project'),
),
],
database_operations=[]
)
]

2
backend/labels/models.py

@ -3,7 +3,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from .managers import LabelManager, CategoryManager, SpanManager, TextLabelManager
from api.models import Project
from projects.models import Project
from examples.models import Example
from label_types.models import CategoryType, SpanType, RelationType

2
backend/labels/tests/test_category.py

@ -4,7 +4,7 @@ from django.db import IntegrityError
from django.test import TestCase
from model_mommy import mommy
from api.models import DOCUMENT_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION
from labels.models import Category
from api.tests.api.utils import prepare_project

2
backend/labels/tests/test_span.py

@ -5,7 +5,7 @@ from django.db import IntegrityError
from django.test import TestCase
from model_mommy import mommy
from api.models import SEQUENCE_LABELING
from projects.models import SEQUENCE_LABELING
from label_types.models import SpanType
from labels.models import Span
from api.tests.api.utils import prepare_project

2
backend/labels/tests/test_text_label.py

@ -4,7 +4,7 @@ from django.db import IntegrityError
from django.test import TestCase
from model_mommy import mommy
from api.models import SEQ2SEQ
from projects.models import SEQ2SEQ
from labels.models import TextLabel
from api.tests.api.utils import prepare_project

2
backend/labels/tests/test_views.py

@ -1,7 +1,7 @@
from rest_framework import status
from rest_framework.reverse import reverse
from api.models import (DOCUMENT_CLASSIFICATION, SEQ2SEQ, SEQUENCE_LABELING)
from projects.models import DOCUMENT_CLASSIFICATION, SEQUENCE_LABELING, SEQ2SEQ
from labels.models import Category, Span, TextLabel
from api.tests.api.utils import (CRUDMixin, make_annotation, make_doc, make_label,
make_user, prepare_project)

2
backend/labels/views.py

@ -6,7 +6,7 @@ from rest_framework import generics, status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from api.models import Project
from projects.models import Project
from labels.models import Category, Span, TextLabel, Relation
from projects.permissions import IsProjectMember
from .permissions import CanEditLabel

2
backend/metrics/tests.py

@ -3,7 +3,7 @@ from rest_framework import status
from rest_framework.reverse import reverse
from api.tests.api.utils import CRUDMixin, prepare_project, make_doc, make_label
from api.models import DOCUMENT_CLASSIFICATION
from projects.models import DOCUMENT_CLASSIFICATION
class TestMemberProgress(CRUDMixin):

18
backend/projects/admin.py

@ -1,6 +1,7 @@
from django.contrib import admin
from .models import Member
from .models import Project, TextClassificationProject, SequenceLabelingProject, Seq2seqProject, Tag
class MemberAdmin(admin.ModelAdmin):
@ -9,4 +10,21 @@ class MemberAdmin(admin.ModelAdmin):
search_fields = ('user__username',)
class ProjectAdmin(admin.ModelAdmin):
list_display = ('name', 'description', 'project_type', 'random_order', 'collaborative_annotation')
ordering = ('project_type',)
search_fields = ('name',)
class TagAdmin(admin.ModelAdmin):
list_display = ('project', 'text', )
ordering = ('project', 'text', )
search_fields = ('text',)
admin.site.register(Member, MemberAdmin)
admin.site.register(Project, ProjectAdmin)
admin.site.register(TextClassificationProject, ProjectAdmin)
admin.site.register(SequenceLabelingProject, ProjectAdmin)
admin.site.register(Seq2seqProject, ProjectAdmin)
admin.site.register(Tag, TagAdmin)

2
backend/projects/apps.py

@ -10,7 +10,7 @@ class ProjectsConfig(AppConfig):
def ready(self):
importlib.import_module('projects.signals')
from api.models import Project
from projects.models import Project
from .signals import add_administrator_on_project_creation
# Registering signals with the subclasses of project.

2
backend/projects/management/commands/create_member.py

@ -1,7 +1,7 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from api.models import Project
from models import Project
from roles.models import Role
from ...models import Member

155
backend/projects/migrations/0002_auto_20220204_0201.py

@ -0,0 +1,155 @@
# Generated by Django 3.2.11 on 2022-02-04 02:01
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0002_remove_content_type_name'),
('projects', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name='Project',
fields=[
('id',
models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField(default='')),
('guideline', models.TextField(blank=True, default='')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('project_type', models.CharField(
choices=[('DocumentClassification', 'document classification'),
('SequenceLabeling', 'sequence labeling'), ('Seq2seq', 'sequence to sequence'),
('IntentDetectionAndSlotFilling', 'intent detection and slot filling'),
('Speech2text', 'speech to text'),
('ImageClassification', 'image classification')], max_length=30)),
('random_order', models.BooleanField(default=False)),
('collaborative_annotation', models.BooleanField(default=False)),
('single_class_classification', models.BooleanField(default=False)),
('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL)),
('polymorphic_ctype',
models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='polymorphic_projects.project_set+',
to='contenttypes.contenttype')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='ImageClassificationProject',
fields=[
('project_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False,
to='projects.project')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('projects.project',),
),
migrations.CreateModel(
name='IntentDetectionAndSlotFillingProject',
fields=[
('project_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False,
to='projects.project')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('projects.project',),
),
migrations.CreateModel(
name='Seq2seqProject',
fields=[
('project_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False,
to='projects.project')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('projects.project',),
),
migrations.CreateModel(
name='SequenceLabelingProject',
fields=[
('project_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False,
to='projects.project')),
('allow_overlapping', models.BooleanField(default=False)),
('grapheme_mode', models.BooleanField(default=False)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('projects.project',),
),
migrations.CreateModel(
name='Speech2textProject',
fields=[
('project_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False,
to='projects.project')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('projects.project',),
),
migrations.CreateModel(
name='TextClassificationProject',
fields=[
('project_ptr',
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
parent_link=True, primary_key=True, serialize=False,
to='projects.project')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('projects.project',),
),
migrations.CreateModel(
name='Tag',
fields=[
('id',
models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tags',
to='projects.project')),
],
),
migrations.AlterField(
model_name='member',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='role_mappings',
to='projects.project'),
),
],
database_operations=[]
)
]

49
backend/projects/migrations/0003_auto_20220204_0551.py

@ -0,0 +1,49 @@
# Generated by Django 3.2.11 on 2022-02-04 05:51
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('projects', '0002_auto_20220204_0201'),
]
operations = [
migrations.RunSQL(
sql="""
UPDATE
projects_project
SET
polymorphic_ctype_id = (
SELECT
t2.id
from
django_content_type t1
INNER JOIN django_content_type t2 ON t1.model = t2.model
WHERE
t1.app_label = 'api'
AND t2.app_label = 'projects'
AND t1.model LIKE '%project'
AND t1.id = projects_project.polymorphic_ctype_id
);
""",
reverse_sql="""
UPDATE
projects_project
SET
polymorphic_ctype_id = (
SELECT
t1.id
from
django_content_type t1
INNER JOIN django_content_type t2 ON t1.model = t2.model
WHERE
t1.app_label = 'api'
AND t2.app_label = 'projects'
AND t1.model LIKE '%project'
AND t2.id = projects_project.polymorphic_ctype_id
);
"""
)
]

157
backend/projects/models.py

@ -1,13 +1,166 @@
import abc
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Manager
from polymorphic.models import PolymorphicModel
from api.models import Project
from roles.models import Role
DOCUMENT_CLASSIFICATION = 'DocumentClassification'
SEQUENCE_LABELING = 'SequenceLabeling'
SEQ2SEQ = 'Seq2seq'
SPEECH2TEXT = 'Speech2text'
IMAGE_CLASSIFICATION = 'ImageClassification'
INTENT_DETECTION_AND_SLOT_FILLING = 'IntentDetectionAndSlotFilling'
PROJECT_CHOICES = (
(DOCUMENT_CLASSIFICATION, 'document classification'),
(SEQUENCE_LABELING, 'sequence labeling'),
(SEQ2SEQ, 'sequence to sequence'),
(INTENT_DETECTION_AND_SLOT_FILLING, 'intent detection and slot filling'),
(SPEECH2TEXT, 'speech to text'),
(IMAGE_CLASSIFICATION, 'image classification')
)
class Project(PolymorphicModel):
name = models.CharField(max_length=100)
description = models.TextField(default='')
guideline = models.TextField(default='', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
)
project_type = models.CharField(max_length=30, choices=PROJECT_CHOICES)
random_order = models.BooleanField(default=False)
collaborative_annotation = models.BooleanField(default=False)
single_class_classification = models.BooleanField(default=False)
@property
@abc.abstractmethod
def is_text_project(self) -> bool:
return False
@property
def can_define_label(self) -> bool:
"""Whether or not the project can define label(ignoring the type of label)"""
return False
@property
def can_define_relation(self) -> bool:
"""Whether or not the project can define relation."""
return False
@property
def can_define_category(self) -> bool:
"""Whether or not the project can define category."""
return False
@property
def can_define_span(self) -> bool:
"""Whether or not the project can define span."""
return False
def __str__(self):
return self.name
class TextClassificationProject(Project):
@property
def is_text_project(self) -> bool:
return True
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_category(self) -> bool:
return True
class SequenceLabelingProject(Project):
allow_overlapping = models.BooleanField(default=False)
grapheme_mode = models.BooleanField(default=False)
@property
def is_text_project(self) -> bool:
return True
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_span(self) -> bool:
return True
class Seq2seqProject(Project):
@property
def is_text_project(self) -> bool:
return True
class IntentDetectionAndSlotFillingProject(Project):
@property
def is_text_project(self) -> bool:
return True
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_category(self) -> bool:
return True
@property
def can_define_span(self) -> bool:
return True
class Speech2textProject(Project):
@property
def is_text_project(self) -> bool:
return False
class ImageClassificationProject(Project):
@property
def is_text_project(self) -> bool:
return False
@property
def can_define_label(self) -> bool:
return True
@property
def can_define_category(self) -> bool:
return True
class Tag(models.Model):
text = models.TextField()
project = models.ForeignKey(
to=Project,
on_delete=models.CASCADE,
related_name='tags'
)
def __str__(self):
return self.text
class MemberManager(Manager):

91
backend/projects/serializers.py

@ -1,5 +1,8 @@
from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
from .models import Tag, Project, TextClassificationProject, SequenceLabelingProject, Seq2seqProject, \
IntentDetectionAndSlotFillingProject, Speech2textProject, ImageClassificationProject
from .models import Member
@ -20,3 +23,91 @@ class MemberSerializer(serializers.ModelSerializer):
class Meta:
model = Member
fields = ('id', 'user', 'role', 'username', 'rolename')
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('id', 'project', 'text', )
read_only_fields = ('id', 'project')
class ProjectSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, required=False)
class Meta:
model = Project
fields = (
'id',
'name',
'description',
'guideline',
'project_type',
'updated_at',
'random_order',
'created_by',
'collaborative_annotation',
'single_class_classification',
'is_text_project',
'can_define_label',
'can_define_relation',
'can_define_category',
'can_define_span',
'tags'
)
read_only_fields = (
'updated_at',
'is_text_project',
'can_define_label',
'can_define_relation',
'can_define_category',
'can_define_span',
'tags'
)
class TextClassificationProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = TextClassificationProject
class SequenceLabelingProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = SequenceLabelingProject
fields = ProjectSerializer.Meta.fields + ('allow_overlapping', 'grapheme_mode')
class Seq2seqProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = Seq2seqProject
class IntentDetectionAndSlotFillingProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = IntentDetectionAndSlotFillingProject
class Speech2textProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = Speech2textProject
class ImageClassificationProjectSerializer(ProjectSerializer):
class Meta(ProjectSerializer.Meta):
model = ImageClassificationProject
class ProjectPolymorphicSerializer(PolymorphicSerializer):
model_serializer_mapping = {
Project: ProjectSerializer,
**{
cls.Meta.model: cls for cls in ProjectSerializer.__subclasses__()
}
}

2
backend/projects/signals.py

@ -1,6 +1,6 @@
from django.conf import settings
from api.models import Project
from projects.models import Project
from roles.models import Role
from .models import Member

0
backend/projects/tests/__init__.py

backend/projects/tests.py → backend/projects/tests/test_member.py

backend/api/tests/api/test_project.py → backend/projects/tests/test_project.py

@ -1,7 +1,7 @@
from rest_framework import status
from rest_framework.reverse import reverse
from .utils import CRUDMixin, create_default_roles, make_user, prepare_project
from api.tests.api.utils import CRUDMixin, create_default_roles, make_user, prepare_project
class TestProjectList(CRUDMixin):

backend/api/tests/api/test_tag.py → backend/projects/tests/test_tag.py

@ -1,7 +1,7 @@
from rest_framework import status
from rest_framework.reverse import reverse
from .utils import CRUDMixin, make_tag, make_user, prepare_project
from api.tests.api.utils import CRUDMixin, make_tag, make_user, prepare_project
class TestTagList(CRUDMixin):

29
backend/projects/urls.py

@ -1,15 +1,38 @@
from django.urls import path
from .views import MemberList, MemberDetail
from .views.member import MemberList, MemberDetail
from .views.tag import TagList, TagDetail
from .views.project import ProjectList, ProjectDetail
urlpatterns = [
path(
route='members',
route='projects',
view=ProjectList.as_view(),
name='project_list'
),
path(
route='projects/<int:project_id>',
view=ProjectDetail.as_view(),
name='project_detail'
),
path(
route='projects/<int:project_id>/tags',
view=TagList.as_view(),
name='tag_list'
),
path(
route='projects/<int:project_id>/tags/<int:tag_id>',
view=TagDetail.as_view(),
name='tag_detail'
),
path(
route='projects/<int:project_id>/members',
view=MemberList.as_view(),
name='member_list'
),
path(
route='members/<int:member_id>',
route='projects/<int:project_id>/members/<int:member_id>',
view=MemberDetail.as_view(),
name='member_detail'
)

backend/projects/views.py → backend/projects/views/member.py

@ -4,10 +4,10 @@ from rest_framework import generics, status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .permissions import IsProjectAdmin
from .serializers import MemberSerializer
from .exceptions import RoleAlreadyAssignedException, RoleConstraintException
from .models import Member
from projects.permissions import IsProjectAdmin
from projects.serializers import MemberSerializer
from projects.exceptions import RoleAlreadyAssignedException, RoleConstraintException
from projects.models import Member
class MemberList(generics.ListCreateAPIView):

backend/api/views/project.py → backend/projects/views/project.py

@ -6,8 +6,8 @@ from rest_framework.response import Response
from projects.permissions import IsProjectAdmin, IsProjectStaffAndReadOnly
from ..models import Project
from ..serializers import ProjectPolymorphicSerializer
from projects.models import Project
from projects.serializers import ProjectPolymorphicSerializer
class ProjectList(generics.ListCreateAPIView):

backend/api/views/tag.py → backend/projects/views/tag.py

@ -3,8 +3,8 @@ from rest_framework.permissions import IsAuthenticated
from projects.permissions import IsProjectAdmin, IsProjectStaffAndReadOnly
from ..models import Tag
from ..serializers import TagSerializer
from projects.models import Tag
from projects.serializers import TagSerializer
class TagList(generics.ListCreateAPIView):
Loading…
Cancel
Save