Skip to content

Commit a9d5711

Browse files
authored
Merge pull request #341 from Labelbox/DIAG-983-fix
fix applied
2 parents b6a6bc3 + 4cac910 commit a9d5711

File tree

4 files changed

+125
-5
lines changed

4 files changed

+125
-5
lines changed

labelbox/data/serialization/labelbox_v1/objects.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,18 @@ def dict(self, *args, **kwargs):
2727

2828
@validator('classifications', pre=True)
2929
def validate_subclasses(cls, value, field):
30-
# Dropdown subclasses create extra unessesary nesting. So we just remove it.
30+
# checklist subclasses create extra unessesary nesting. So we just remove it.
3131
if isinstance(value, list) and len(value):
32-
if isinstance(value[0], list):
33-
return value[0]
32+
subclasses = []
33+
for v in value:
34+
# this is due to Checklists providing extra brackets []. We grab every item
35+
# in the brackets if this is the case
36+
if isinstance(v, list):
37+
for inner_v in v:
38+
subclasses.append(inner_v)
39+
else:
40+
subclasses.append(v)
41+
return subclasses
3442
return value
3543

3644

tests/data/serialization/labelbox_v1/test_image.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
'tests/data/assets/labelbox_v1/highly_nested_image.json',
1010
'tests/data/assets/labelbox_v1/image_export.json'
1111
])
12+
#TODO: some checklists from the export come in as [checklist ans: []]
13+
# while others are checklist ans: []... when we can figure out why we sometimes
14+
# have extra brackets, we can look into testing nested checklist answers
15+
# and ensuring the export's output matches deserialized/serialized output
1216
def test_image(file_path):
1317
with open(file_path, 'r') as file:
1418
payload = json.load(file)
@@ -32,7 +36,6 @@ def test_image(file_path):
3236
if isinstance(annotation_b['classifications'][0], list):
3337
annotation_b['classifications'] = annotation_b[
3438
'classifications'][0]
35-
3639
assert annotation_a == annotation_b
3740

3841

tests/integration/conftest.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from labelbox.pagination import PaginatedCollection
2121
from labelbox.schema.invite import Invite
2222
from labelbox.schema.user import User
23-
from labelbox import OntologyBuilder, Tool
23+
from labelbox import OntologyBuilder, Tool, Option, Classification
2424
from labelbox.schema.annotation_import import LabelImport
2525

2626
IMG_URL = "https://picsum.photos/200/300"
@@ -333,3 +333,55 @@ def configured_project_with_label(client, rand_gen, image_url):
333333
yield [project, label]
334334
dataset.delete()
335335
project.delete()
336+
337+
338+
@pytest.fixture
339+
def configured_project_with_complex_ontology(client, rand_gen, image_url):
340+
project = client.create_project(name=rand_gen(str))
341+
dataset = client.create_dataset(name=rand_gen(str), projects=project)
342+
data_row = dataset.create_data_row(row_data=image_url)
343+
editor = list(
344+
project.client.get_labeling_frontends(
345+
where=LabelingFrontend.name == "editor"))[0]
346+
347+
ontology = OntologyBuilder()
348+
tools = [
349+
Tool(tool=Tool.Type.BBOX, name="test-bbox-class"),
350+
Tool(tool=Tool.Type.LINE, name="test-line-class"),
351+
Tool(tool=Tool.Type.POINT, name="test-point-class"),
352+
Tool(tool=Tool.Type.POLYGON, name="test-polygon-class"),
353+
Tool(tool=Tool.Type.NER, name="test-ner-class")
354+
]
355+
356+
options = [
357+
Option(value="first option answer"),
358+
Option(value="second option answer"),
359+
Option(value="third option answer")
360+
]
361+
362+
classifications = [
363+
Classification(class_type=Classification.Type.TEXT,
364+
instructions="test-text-class"),
365+
Classification(class_type=Classification.Type.DROPDOWN,
366+
instructions="test-dropdown-class",
367+
options=options),
368+
Classification(class_type=Classification.Type.RADIO,
369+
instructions="test-radio-class",
370+
options=options),
371+
Classification(class_type=Classification.Type.CHECKLIST,
372+
instructions="test-checklist-class",
373+
options=options)
374+
]
375+
376+
for t in tools:
377+
for c in classifications:
378+
t.add_classification(c)
379+
ontology.add_tool(t)
380+
for c in classifications:
381+
ontology.add_classification(c)
382+
383+
project.setup(editor, ontology.asdict())
384+
385+
yield [project, data_row]
386+
dataset.delete()
387+
project.delete()

tests/integration/test_export.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import uuid
2+
from labelbox.data.annotation_types.annotation import ObjectAnnotation
3+
4+
from labelbox.schema.annotation_import import LabelImport
5+
6+
7+
def test_export_annotations_nested_checklist(
8+
client, configured_project_with_complex_ontology):
9+
project, data_row = configured_project_with_complex_ontology
10+
ontology = project.ontology().normalized
11+
12+
tool = ontology["tools"][0]
13+
14+
nested_check = [
15+
subc for subc in tool["classifications"]
16+
if subc["name"] == "test-checklist-class"
17+
][0]
18+
19+
data = [{
20+
"uuid":
21+
str(uuid.uuid4()),
22+
"schemaId":
23+
tool['featureSchemaId'],
24+
"dataRow": {
25+
"id": data_row.uid
26+
},
27+
"bbox": {
28+
"top": 20,
29+
"left": 20,
30+
"height": 50,
31+
"width": 50
32+
},
33+
"classifications": [{
34+
"schemaId":
35+
nested_check["featureSchemaId"],
36+
"answers": [
37+
{
38+
"schemaId": nested_check["options"][0]["featureSchemaId"]
39+
},
40+
{
41+
"schemaId": nested_check["options"][1]["featureSchemaId"]
42+
},
43+
]
44+
}]
45+
}]
46+
47+
task = LabelImport.create_from_objects(client, project.uid,
48+
f'label-import-{uuid.uuid4()}', data)
49+
task.wait_until_done()
50+
labels = project.label_generator().as_list()
51+
object_annotation = [
52+
annot for annot in next(labels).annotations
53+
if isinstance(annot, ObjectAnnotation)
54+
][0]
55+
56+
nested_class_answers = object_annotation.classifications[0].value.answer
57+
assert len(nested_class_answers) == 2

0 commit comments

Comments
 (0)