Skip to content

Commit f16da35

Browse files
[AL-7548] Improve test coverage for send to annotate (#1322)
1 parent afbb6d4 commit f16da35

File tree

5 files changed

+77
-29
lines changed

5 files changed

+77
-29
lines changed

labelbox/schema/send_to_annotate_params.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class SendToAnnotateFromCatalogParams(TypedDict):
2626
:param override_existing_annotations_rule: Optional[ConflictResolutionStrategy] - The strategy defining how to
2727
handle conflicts in classifications between the data rows that already exist in the project and incoming
2828
predictions from the source model run or annotations from the source project. Defaults to
29-
ConflictResolutionStrategy.SKIP.
29+
ConflictResolutionStrategy.KEEP_EXISTING.
3030
:param batch_priority: Optional[int] - The priority of the batch. Defaults to 5.
3131
"""
3232

@@ -49,7 +49,7 @@ class SendToAnnotateFromModelParams(TypedDict):
4949
to False.
5050
:param override_existing_annotations_rule: Optional[ConflictResolutionStrategy] - The strategy defining how to
5151
handle conflicts in classifications between the data rows that already exist in the project and incoming
52-
predictions from the source model run. Defaults to ConflictResolutionStrategy.SKIP.
52+
predictions from the source model run. Defaults to ConflictResolutionStrategy.KEEP_EXISTING.
5353
:param batch_priority: Optional[int] - The priority of the batch. Defaults to 5.
5454
"""
5555

tests/integration/annotation_import/conftest.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,21 @@ def configured_project(client, initial_dataset, ontology, rand_gen, image_url):
579579
project.delete()
580580

581581

582+
@pytest.fixture
583+
def project_with_ontology(client, configured_project, ontology, rand_gen):
584+
project = client.create_project(name=rand_gen(str),
585+
queue_mode=QueueMode.Batch,
586+
media_type=MediaType.Image)
587+
editor = list(
588+
client.get_labeling_frontends(
589+
where=LabelingFrontend.name == "editor"))[0]
590+
project.setup(editor, ontology)
591+
592+
yield project, ontology
593+
594+
project.delete()
595+
596+
582597
@pytest.fixture
583598
def configured_project_pdf(client, ontology, rand_gen, pdf_url):
584599
project = client.create_project(name=rand_gen(str),
Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22

3-
from labelbox import UniqueIds
3+
from labelbox import UniqueIds, OntologyBuilder
4+
from labelbox.schema.conflict_resolution_strategy import ConflictResolutionStrategy
45

56

67
def test_send_to_annotate_from_model(client, configured_project,
@@ -9,19 +10,43 @@ def test_send_to_annotate_from_model(client, configured_project,
910
project_with_ontology):
1011
model_run = model_run_with_data_rows
1112
data_row_ids = [p['dataRow']['id'] for p in model_run_predictions]
13+
assert len(data_row_ids) > 0
1214

13-
[destination_project, _] = project_with_ontology
15+
destination_project, _ = project_with_ontology
1416

1517
queues = destination_project.task_queues()
16-
initial_labeling_task = next(
17-
q for q in queues if q.name == "Initial labeling task")
18+
initial_review_task = next(
19+
q for q in queues if q.name == "Initial review task")
20+
21+
# build an ontology mapping using the top level tools and classifications
22+
source_ontology_builder = OntologyBuilder.from_project(configured_project)
23+
feature_schema_ids = list(
24+
tool.feature_schema_id for tool in source_ontology_builder.tools)
25+
# create a dictionary of feature schema id to itself
26+
ontology_mapping = dict(zip(feature_schema_ids, feature_schema_ids))
27+
28+
classification_feature_schema_ids = list(
29+
classification.feature_schema_id
30+
for classification in source_ontology_builder.classifications)
31+
# create a dictionary of feature schema id to itself
32+
classification_ontology_mapping = dict(
33+
zip(classification_feature_schema_ids,
34+
classification_feature_schema_ids))
35+
36+
# combine the two ontology mappings
37+
ontology_mapping.update(classification_ontology_mapping)
1838

1939
task = model_run.send_to_annotate_from_model(
2040
destination_project_id=destination_project.uid,
2141
batch_name="batch",
2242
data_rows=UniqueIds(data_row_ids),
23-
task_queue_id=initial_labeling_task.uid,
24-
params={})
43+
task_queue_id=initial_review_task.uid,
44+
params={
45+
"predictions_ontology_mapping":
46+
ontology_mapping,
47+
"override_existing_annotations_rule":
48+
ConflictResolutionStrategy.OverrideWithPredictions
49+
})
2550

2651
task.wait_till_done()
2752

@@ -32,3 +57,7 @@ def test_send_to_annotate_from_model(client, configured_project,
3257
destination_data_rows = list(destination_batches[0].export_data_rows())
3358
assert len(destination_data_rows) == len(data_row_ids)
3459
assert all([dr.uid in data_row_ids for dr in destination_data_rows])
60+
61+
# Since data rows were added to a review queue, predictions should be imported into the project as labels
62+
destination_project_labels = (list(destination_project.labels()))
63+
assert len(destination_project_labels) == len(data_row_ids)

tests/integration/conftest.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -372,18 +372,6 @@ def project_with_empty_ontology(project):
372372
yield project
373373

374374

375-
@pytest.fixture
376-
def project_with_ontology(client, rand_gen):
377-
project = client.create_project(name=rand_gen(str),
378-
queue_mode=QueueMode.Batch,
379-
media_type=MediaType.Image)
380-
ontology = _setup_ontology(project)
381-
382-
yield [project, ontology]
383-
384-
project.delete()
385-
386-
387375
@pytest.fixture
388376
def configured_project(project_with_empty_ontology, initial_dataset, rand_gen,
389377
image_url):
@@ -520,7 +508,7 @@ def _setup_ontology(project):
520508
project.setup(editor, ontology_builder.asdict())
521509
# TODO: ontology may not be synchronous after setup. remove sleep when api is more consistent
522510
time.sleep(2)
523-
return ontology_builder.from_project(project)
511+
return OntologyBuilder.from_project(project)
524512

525513

526514
@pytest.fixture
Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
1-
import pytest
2-
3-
from labelbox import UniqueIds
1+
from labelbox import UniqueIds, OntologyBuilder, LabelingFrontend
42
from labelbox.schema.conflict_resolution_strategy import ConflictResolutionStrategy
53

64

75
def test_send_to_annotate_include_annotations(
8-
client, configured_batch_project_with_label, project_with_ontology):
6+
client, configured_batch_project_with_label, project_pack):
97
[source_project, _, data_row, _] = configured_batch_project_with_label
10-
[destination_project, _] = project_with_ontology
8+
destination_project = project_pack[0]
9+
10+
source_ontology_builder = OntologyBuilder.from_project(source_project)
11+
editor = list(
12+
client.get_labeling_frontends(
13+
where=LabelingFrontend.name == "editor"))[0]
14+
destination_project.setup(editor, source_ontology_builder.asdict())
15+
16+
# build an ontology mapping using the top level tools
17+
feature_schema_ids = list(
18+
tool.feature_schema_id for tool in source_ontology_builder.tools)
19+
# create a dictionary of feature schema id to itself
20+
ontology_mapping = dict(zip(feature_schema_ids, feature_schema_ids))
1121

1222
try:
1323
queues = destination_project.task_queues()
14-
initial_labeling_task = next(
15-
q for q in queues if q.name == "Initial labeling task")
24+
initial_review_task = next(
25+
q for q in queues if q.name == "Initial review task")
1626

1727
# Send the data row to the new project
1828
task = client.send_to_annotate_from_catalog(
1929
destination_project_id=destination_project.uid,
20-
task_queue_id=initial_labeling_task.uid,
30+
task_queue_id=initial_review_task.uid,
2131
batch_name="test-batch",
2232
data_rows=UniqueIds([data_row.uid]),
2333
params={
2434
"source_project_id":
2535
source_project.uid,
36+
"annotations_ontology_mapping":
37+
ontology_mapping,
2638
"override_existing_annotations_rule":
2739
ConflictResolutionStrategy.OverrideWithAnnotations
2840
})
@@ -36,5 +48,9 @@ def test_send_to_annotate_include_annotations(
3648
destination_data_rows = list(destination_batches[0].export_data_rows())
3749
assert len(destination_data_rows) == 1
3850
assert destination_data_rows[0].uid == data_row.uid
51+
52+
# Verify annotations were copied into the destination project
53+
destination_project_labels = (list(destination_project.labels()))
54+
assert len(destination_project_labels) == 1
3955
finally:
4056
destination_project.delete()

0 commit comments

Comments
 (0)