Browse Source

Merge pull request #1632 from doccano/enhancement/refactorProjectModel

[Enhancement] refactor project model and serializer
pull/1633/head
Hiroki Nakayama 2 years ago
committed by GitHub
parent
commit
69511c3ad0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 119 additions and 68 deletions
  1. 94
      backend/api/models.py
  2. 10
      backend/api/serializers.py
  3. 4
      backend/api/views/auto_labeling.py
  4. 79
      frontend/domain/models/project/project.ts

94
backend/api/models.py

@ -1,7 +1,7 @@
import abc
import random
import string
import uuid
from typing import Literal
from auto_labeling_pipeline.models import RequestModelFactory
from django.contrib.auth.models import User
@ -39,8 +39,30 @@ class Project(PolymorphicModel):
collaborative_annotation = models.BooleanField(default=False)
single_class_classification = models.BooleanField(default=False)
def is_task_of(self, task: Literal['text', 'image', 'speech']):
raise NotImplementedError()
@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
@ -48,40 +70,82 @@ class Project(PolymorphicModel):
class TextClassificationProject(Project):
def is_task_of(self, task: Literal['text', 'image', 'speech']):
return task == 'text'
@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)
def is_task_of(self, task: Literal['text', 'image', 'speech']):
return task == 'text'
@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):
def is_task_of(self, task: Literal['text', 'image', 'speech']):
return task == 'text'
@property
def is_text_project(self) -> bool:
return True
class IntentDetectionAndSlotFillingProject(Project):
def is_task_of(self, task: Literal['text', 'image', 'speech']):
return task == 'text'
@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):
def is_task_of(self, task: Literal['text', 'image', 'speech']):
return task == 'speech'
@property
def is_text_project(self) -> bool:
return False
class ImageClassificationProject(Project):
def is_task_of(self, task: Literal['text', 'image', 'speech']):
return task == 'image'
@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
def generate_random_hex_color():

10
backend/api/serializers.py

@ -173,11 +173,21 @@ class ProjectSerializer(serializers.ModelSerializer):
'random_order',
'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',
'users',
'is_text_project',
'can_define_label',
'can_define_relation',
'can_define_category',
'can_define_span',
'tags'
)

4
backend/api/views/auto_labeling.py

@ -142,7 +142,7 @@ class AutoLabelingConfigParameterTest(APIView):
def prepare_example(self):
text = self.request.data['text']
if self.project.is_task_of('text'):
if self.project.is_text_project:
return text
else:
tu = TemporaryUpload.objects.get(upload_id=text)
@ -221,7 +221,7 @@ class AutoLabelingAnnotation(generics.CreateAPIView):
def get_example(self, project):
example = get_object_or_404(Example, pk=self.kwargs['example_id'])
if project.is_task_of('text'):
if project.is_text_project:
return example.text
else:
return str(example.filename)

79
frontend/domain/models/project/project.ts

@ -17,6 +17,11 @@ export class ProjectReadItem {
public allow_overlapping: boolean,
public grapheme_mode: boolean,
public tags: Object[],
public is_text_project: boolean,
public can_define_label: boolean,
public can_define_relation: boolean,
public can_define_span: boolean,
public can_define_category: boolean,
) {}
static valueOf(
@ -34,7 +39,12 @@ export class ProjectReadItem {
resourcetype,
allow_overlapping,
grapheme_mode,
tags
tags,
is_text_project,
can_define_label,
can_define_relation,
can_define_span,
can_define_category,
}:
{
id: number,
@ -50,7 +60,12 @@ export class ProjectReadItem {
resourcetype: string,
allow_overlapping: boolean,
grapheme_mode: boolean,
tags: Object[]
tags: Object[],
is_text_project: boolean,
can_define_label: boolean,
can_define_relation: boolean,
can_define_span: boolean,
can_define_category: boolean
}
): ProjectReadItem {
return new ProjectReadItem(
@ -67,7 +82,12 @@ export class ProjectReadItem {
resourcetype,
allow_overlapping,
grapheme_mode,
tags
tags,
is_text_project,
can_define_label,
can_define_relation,
can_define_span,
can_define_category
)
}
@ -85,66 +105,23 @@ export class ProjectReadItem {
}
get canDefineLabel() {
const allowedProjectTypes = [
'DocumentClassification',
'SequenceLabeling',
'IntentDetectionAndSlotFilling',
'ImageClassification'
]
return allowedProjectTypes.includes(this.project_type)
return this.can_define_label
}
get canDefineRelation() {
const allowedProjectTypes = [
'SequenceLabeling'
]
return allowedProjectTypes.includes(this.project_type)
return this.can_define_relation
}
get isTextProject() {
const allowedProjectTypes = [
'DocumentClassification',
'SequenceLabeling',
'Seq2seq',
'IntentDetectionAndSlotFilling'
]
return allowedProjectTypes.includes(this.project_type)
return this.is_text_project
}
get hasCategory(): boolean {
const allowedProjectTypes = [
'DocumentClassification',
'IntentDetectionAndSlotFilling',
'ImageClassification'
]
return allowedProjectTypes.includes(this.project_type)
return this.can_define_category
}
get hasSpan(): boolean {
const allowedProjectTypes = [
'IntentDetectionAndSlotFilling',
'SequenceLabeling'
]
return allowedProjectTypes.includes(this.project_type)
}
toObject(): Object {
return {
id: this.id,
name: this.name,
description: this.description,
guideline: this.guideline,
users: this.users,
project_type: this.project_type,
updated_at: this.updated_at,
random_order: this.random_order,
collaborative_annotation: this.collaborative_annotation,
single_class_classification: this.single_class_classification,
resourcetype: this.resourcetype,
allow_overlapping: this.allow_overlapping,
grapheme_mode: this.grapheme_mode,
tags: this.tags
}
return this.can_define_span
}
}

Loading…
Cancel
Save