mirror of https://github.com/doccano/doccano.git
pythondatasetsactive-learningtext-annotationdatasetnatural-language-processingdata-labelingmachine-learningannotation-tool
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
11 KiB
255 lines
11 KiB
from django.core.exceptions import ValidationError
|
|
from django.db.utils import IntegrityError
|
|
from django.test import TestCase
|
|
from model_mommy import mommy
|
|
|
|
from api.models import (IMAGE_CLASSIFICATION, SEQUENCE_LABELING, Category,
|
|
CategoryType, ExampleState, Span, SpanType, TextLabel,
|
|
generate_random_hex_color)
|
|
|
|
from .api.utils import prepare_project
|
|
|
|
|
|
class TestLabel(TestCase):
|
|
|
|
def test_deny_creating_same_text(self):
|
|
label = mommy.make('CategoryType')
|
|
with self.assertRaises(IntegrityError):
|
|
mommy.make('CategoryType', project=label.project, text=label.text)
|
|
|
|
def test_keys_uniqueness(self):
|
|
label = mommy.make('CategoryType', prefix_key='ctrl', suffix_key='a')
|
|
with self.assertRaises(ValidationError):
|
|
CategoryType(project=label.project,
|
|
text='example',
|
|
prefix_key=label.prefix_key,
|
|
suffix_key=label.suffix_key).full_clean()
|
|
|
|
def test_suffix_key_uniqueness(self):
|
|
label = mommy.make('CategoryType', prefix_key=None, suffix_key='a')
|
|
with self.assertRaises(ValidationError):
|
|
CategoryType(project=label.project,
|
|
text='example',
|
|
prefix_key=label.prefix_key,
|
|
suffix_key=label.suffix_key).full_clean()
|
|
|
|
def test_cannot_add_label_only_prefix_key(self):
|
|
project = mommy.make('Project')
|
|
label = CategoryType(project=project,
|
|
text='example',
|
|
prefix_key='ctrl')
|
|
with self.assertRaises(ValidationError):
|
|
label.clean()
|
|
|
|
def test_can_add_label_only_suffix_key(self):
|
|
project = mommy.make('Project')
|
|
label = CategoryType(project=project, text='example', suffix_key='a')
|
|
try:
|
|
label.full_clean()
|
|
except ValidationError:
|
|
self.fail(msg=ValidationError)
|
|
|
|
def test_can_add_label_suffix_key_with_prefix_key(self):
|
|
project = mommy.make('Project')
|
|
label = CategoryType(project=project,
|
|
text='example',
|
|
prefix_key='ctrl',
|
|
suffix_key='a')
|
|
try:
|
|
label.full_clean()
|
|
except ValidationError:
|
|
self.fail(msg=ValidationError)
|
|
|
|
|
|
class TestCategory(TestCase):
|
|
|
|
def test_uniqueness(self):
|
|
a = mommy.make('Category')
|
|
with self.assertRaises(IntegrityError):
|
|
Category(example=a.example, user=a.user, label=a.label).save()
|
|
|
|
|
|
class TestSpan(TestCase):
|
|
|
|
def setUp(self):
|
|
self.project = prepare_project(SEQUENCE_LABELING, allow_overlapping=False)
|
|
self.example = mommy.make('Example', project=self.project.item)
|
|
self.user = self.project.users[0]
|
|
|
|
def test_start_offset_is_not_negative(self):
|
|
with self.assertRaises(IntegrityError):
|
|
mommy.make('Span', start_offset=-1, end_offset=0)
|
|
|
|
def test_end_offset_is_not_negative(self):
|
|
with self.assertRaises(IntegrityError):
|
|
mommy.make('Span', start_offset=-2, end_offset=-1)
|
|
|
|
def test_start_offset_is_less_than_end_offset(self):
|
|
with self.assertRaises(IntegrityError):
|
|
mommy.make('Span', start_offset=0, end_offset=0)
|
|
|
|
def test_unique_constraint(self):
|
|
mommy.make('Span', example=self.example, start_offset=5, end_offset=10, user=self.user)
|
|
mommy.make('Span', example=self.example, start_offset=0, end_offset=5, user=self.user)
|
|
mommy.make('Span', example=self.example, start_offset=10, end_offset=15, user=self.user)
|
|
|
|
def test_unique_constraint_violated(self):
|
|
mommy.make('Span', example=self.example, start_offset=5, end_offset=10, user=self.user)
|
|
spans = [(5, 10), (5, 11), (4, 10), (6, 9), (9, 15), (0, 6)]
|
|
for start_offset, end_offset in spans:
|
|
with self.assertRaises(ValidationError):
|
|
mommy.make(
|
|
'Span',
|
|
example=self.example,
|
|
start_offset=start_offset,
|
|
end_offset=end_offset,
|
|
user=self.user
|
|
)
|
|
|
|
def test_unique_constraint_if_overlapping_is_allowed(self):
|
|
project = prepare_project(SEQUENCE_LABELING, allow_overlapping=True)
|
|
example = mommy.make('Example', project=project.item)
|
|
user = project.users[0]
|
|
mommy.make('Span', example=example, start_offset=5, end_offset=10, user=user)
|
|
spans = [(5, 10), (5, 11), (4, 10), (6, 9), (9, 15), (0, 6)]
|
|
for start_offset, end_offset in spans:
|
|
mommy.make('Span', example=example, start_offset=start_offset, end_offset=end_offset, user=user)
|
|
|
|
def test_update(self):
|
|
span = mommy.make('Span', example=self.example, start_offset=0, end_offset=5)
|
|
span.end_offset = 6
|
|
span.save()
|
|
|
|
|
|
class TestSpanWithoutCollaborativeMode(TestCase):
|
|
|
|
def setUp(self):
|
|
self.project = prepare_project(SEQUENCE_LABELING, False, allow_overlapping=False)
|
|
self.example = mommy.make('Example', project=self.project.item)
|
|
|
|
def test_allow_users_to_create_same_spans(self):
|
|
mommy.make('Span', example=self.example, start_offset=5, end_offset=10, user=self.project.users[0])
|
|
mommy.make('Span', example=self.example, start_offset=5, end_offset=10, user=self.project.users[1])
|
|
|
|
|
|
class TestSpanWithCollaborativeMode(TestCase):
|
|
|
|
def test_deny_users_to_create_same_spans(self):
|
|
project = prepare_project(SEQUENCE_LABELING, True, allow_overlapping=False)
|
|
example = mommy.make('Example', project=project.item)
|
|
mommy.make('Span', example=example, start_offset=5, end_offset=10, user=project.users[0])
|
|
with self.assertRaises(ValidationError):
|
|
mommy.make('Span', example=example, start_offset=5, end_offset=10, user=project.users[1])
|
|
|
|
def test_allow_users_to_create_same_spans_if_overlapping_is_allowed(self):
|
|
project = prepare_project(SEQUENCE_LABELING, True, allow_overlapping=True)
|
|
example = mommy.make('Example', project=project.item)
|
|
mommy.make('Span', example=example, start_offset=5, end_offset=10, user=project.users[0])
|
|
mommy.make('Span', example=example, start_offset=5, end_offset=10, user=project.users[1])
|
|
|
|
|
|
class TestSeq2seqAnnotation(TestCase):
|
|
|
|
def test_uniqueness(self):
|
|
a = mommy.make('TextLabel')
|
|
with self.assertRaises(IntegrityError):
|
|
TextLabel(example=a.example,
|
|
user=a.user,
|
|
text=a.text).save()
|
|
|
|
|
|
class TestGeneratedColor(TestCase):
|
|
|
|
def test_length(self):
|
|
for i in range(100):
|
|
color = generate_random_hex_color()
|
|
self.assertEqual(len(color), 7)
|
|
|
|
|
|
class TestExampleState(TestCase):
|
|
|
|
def setUp(self):
|
|
self.project = prepare_project(SEQUENCE_LABELING)
|
|
self.example = mommy.make('Example', project=self.project.item)
|
|
self.other = mommy.make('Example', project=self.project.item)
|
|
self.examples = self.project.item.examples.all()
|
|
|
|
def test_initial_done(self):
|
|
done = ExampleState.objects.count_done(self.examples)
|
|
self.assertEqual(done, 0)
|
|
|
|
def test_done_confirmed_by_user(self):
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=self.project.users[0])
|
|
done = ExampleState.objects.count_done(self.examples)
|
|
self.assertEqual(done, 1)
|
|
|
|
def test_done_confirmed_by_multiple_user(self):
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=self.project.users[0])
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=self.project.users[1])
|
|
done = ExampleState.objects.count_done(self.examples)
|
|
self.assertEqual(done, 1)
|
|
|
|
def test_done_confirmed_by_different_example(self):
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=self.project.users[0])
|
|
mommy.make('ExampleState', example=self.other, confirmed_by=self.project.users[1])
|
|
done = ExampleState.objects.count_done(self.examples, self.project.users[0])
|
|
self.assertEqual(done, 1)
|
|
|
|
def test_initial_user(self):
|
|
progress = ExampleState.objects.measure_member_progress(self.examples, self.project.users)
|
|
expected_progress = [{'user': user.username, 'done': 0} for user in self.project.users]
|
|
self.assertEqual(progress, {'total': 2, 'progress': expected_progress})
|
|
|
|
def test_user_count_after_confirmation(self):
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=self.project.users[0])
|
|
progress = ExampleState.objects.measure_member_progress(self.examples, self.project.users)
|
|
expected_progress = [{'user': user.username, 'done': 0} for user in self.project.users]
|
|
expected_progress[0]['done'] = 1
|
|
self.assertEqual(progress, {'total': 2, 'progress': expected_progress})
|
|
|
|
def test_user_count_after_multiple_user_confirmation(self):
|
|
user1 = self.project.users[0]
|
|
user2 = self.project.users[1]
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=user1)
|
|
mommy.make('ExampleState', example=self.example, confirmed_by=user2)
|
|
progress = ExampleState.objects.measure_member_progress(self.examples, self.project.users)
|
|
expected_progress = [{'user': user.username, 'done': 0} for user in self.project.users]
|
|
expected_progress[0]['done'] = 1
|
|
expected_progress[1]['done'] = 1
|
|
self.assertEqual(progress, {'total': 2, 'progress': expected_progress})
|
|
|
|
|
|
class TestLabelDistribution(TestCase):
|
|
|
|
def setUp(self):
|
|
self.project = prepare_project(SEQUENCE_LABELING, allow_overlapping=False)
|
|
self.example = mommy.make('Example', project=self.project.item)
|
|
self.user = self.project.users[0]
|
|
|
|
def test_calc_label_distribution(self):
|
|
label_a = mommy.make('SpanType', text='labelA', project=self.project.item)
|
|
label_b = mommy.make('SpanType', text='labelB', project=self.project.item)
|
|
mommy.make('Span', example=self.example, start_offset=5, end_offset=10, user=self.user, label=label_a)
|
|
mommy.make('Span', example=self.example, start_offset=10, end_offset=15, user=self.user, label=label_b)
|
|
distribution = Span.objects.calc_label_distribution(
|
|
examples=self.project.item.examples.all(),
|
|
members=self.project.users,
|
|
labels=SpanType.objects.all()
|
|
)
|
|
expected = {user.username: {label.text: 0 for label in SpanType.objects.all()} for user in self.project.users}
|
|
expected[self.user.username][label_a.text] = 1
|
|
expected[self.user.username][label_b.text] = 1
|
|
self.assertEqual(distribution, expected)
|
|
|
|
|
|
class TestExample(TestCase):
|
|
|
|
def test_text_project_returns_text_as_data_property(self):
|
|
project = prepare_project(SEQUENCE_LABELING)
|
|
example = mommy.make('Example', project=project.item)
|
|
self.assertEqual(example.text, example.data)
|
|
|
|
def test_image_project_returns_filename_as_data_property(self):
|
|
project = prepare_project(IMAGE_CLASSIFICATION)
|
|
example = mommy.make('Example', project=project.item)
|
|
self.assertEqual(str(example.filename), example.data)
|