diff --git a/darwin/importer/importer.py b/darwin/importer/importer.py index 871160cfe..88ad5727e 100644 --- a/darwin/importer/importer.py +++ b/darwin/importer/importer.py @@ -671,12 +671,23 @@ def _import_properties( if not annotation_id_map.get(annotation_id): continue annotation = annotation_id_map[annotation_id] - + annotation_class = annotation.annotation_class + annotation_class_name = annotation_class.name + annotation_type = ( + annotation_class.annotation_internal_type + or annotation_class.annotation_type + ) + annotation_class_id = annotation_class_ids_map[ + (annotation_class_name, annotation_type) + ] for a_prop in annotation.properties or []: frame_index = str(a_prop.frame_index) for prop in created_properties + updated_properties: - if prop.name == a_prop.name: + if ( + prop.name == a_prop.name + and annotation_class_id == prop.annotation_class_id + ): if a_prop.value is None: if not annotation_property_map[annotation_id][frame_index][ prop.id diff --git a/tests/darwin/data/metadata_identical_properties_different_classes.json b/tests/darwin/data/metadata_identical_properties_different_classes.json new file mode 100644 index 000000000..d6f020ad4 --- /dev/null +++ b/tests/darwin/data/metadata_identical_properties_different_classes.json @@ -0,0 +1,53 @@ +{ + "version": "1.0", + "schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/metadata/1.0/schema.json", + "classes": [ + { + "name": "test_class_1", + "type": "bounding_box", + "description": null, + "color": "rgba(255,46,0,1.0)", + "sub_types": [ + "inference" + ], + "properties": [ + { + "name": "existing_property_single_select", + "type": "single_select", + "description": "", + "required": false, + "property_values": [ + { + "value": "1", + "color": "rgba(255,46,0,1.0)" + } + ] + } + ] + }, + { + "name": "test_class_2", + "type": "bounding_box", + "description": null, + "color": "rgba(255,46,0,1.0)", + "sub_types": [ + "inference" + ], + "properties": [ + { + "name": "existing_property_single_select", + "type": "single_select", + "description": "", + "required": false, + "property_values": [ + { + "value": "1", + "color": "rgba(255,46,0,1.0)" + } + ] + } + ] + } + ], + "properties": [] + } \ No newline at end of file diff --git a/tests/darwin/importer/importer_test.py b/tests/darwin/importer/importer_test.py index be717d589..9d03218c5 100644 --- a/tests/darwin/importer/importer_test.py +++ b/tests/darwin/importer/importer_test.py @@ -13,7 +13,6 @@ PropertyValue, ) import pytest - from darwin import datatypes as dt from darwin.importer import get_importer from darwin.importer.importer import ( @@ -35,7 +34,7 @@ @pytest.fixture -def setup_data(request): +def setup_data(request, multiple_annotations=False): granularity = request.param client = Mock() client.default_team = "test_team" @@ -70,6 +69,36 @@ def setup_data(request): ], ) ] + if multiple_annotations: + annotations.append( + dt.Annotation( + dt.AnnotationClass("test_class_2", "polygon"), + {"paths": [[6, 7, 8, 9, 10]]}, + [], + [], + id="annotation_id_2", + properties=[ + SelectedProperty( + frame_index=None if granularity == "annotation" else "0", + name="existing_property_single_select", + type="single_select", + value="1", + ), + SelectedProperty( + frame_index=None if granularity == "annotation" else "0", + name="existing_property_multi_select", + type="multi_select", + value="1", + ), + SelectedProperty( + frame_index=None if granularity == "annotation" else "1", + name="existing_property_multi_select", + type="multi_select", + value="2", + ), + ], + ) + ) return client, team_slug, annotation_class_ids_map, annotations @@ -1619,6 +1648,99 @@ def test_import_new_section_level_property_values_with_manifest( ) +@patch("darwin.importer.importer._get_team_properties_annotation_lookup") +@pytest.mark.parametrize("setup_data", ["section"], indirect=True) +def test_import_identical_properties_to_different_classes( + mock_get_team_properties, setup_data +): + client, team_slug, _, _ = setup_data + # This test requires 2 annotations annotation + annotation_class_ids_map = { + ("test_class_1", "polygon"): 1, + ("test_class_2", "polygon"): 2, + } + annotations = [ + ( + dt.Annotation( + dt.AnnotationClass("test_class_1", "polygon"), + {"paths": [[1, 2, 3, 4, 5]]}, + [], + [], + id="1", + properties=[ + SelectedProperty( + frame_index="0", + name="existing_property_single_select", + type="single_select", + value="1", + ), + ], + ) + ), + ( + dt.Annotation( + dt.AnnotationClass("test_class_2", "polygon"), + {"paths": [[1, 2, 3, 4, 5]]}, + [], + [], + id="2", + properties=[ + SelectedProperty( + frame_index="0", + name="existing_property_single_select", + type="single_select", + value="1", + ), + ], + ) + ), + ] + mock_get_team_properties.return_value = {} + metadata_path = ( + Path(__file__).parents[1] + / "data" + / "metadata_identical_properties_different_classes.json" + ) + with patch.object(client, "create_property") as mock_create_property: + mock_create_property.side_effect = [ + FullProperty( + name="existing_property_single_select", + id="prop_id_1", + type="single_select", + required=False, + description="property-created-during-annotation-import", + annotation_class_id=1, + slug="test_team", + property_values=[ + PropertyValue( + value="1", color="rgba(255,46,0,1.0)", id="prop_val_id_1" + ), + ], + granularity=PropertyGranularity.section, + ), + FullProperty( + name="existing_property_single_select", + id="prop_id_2", + type="single_select", + required=False, + description="property-created-during-annotation-import", + annotation_class_id=2, + slug="test_team", + property_values=[ + PropertyValue( + value="1", color="rgba(255,46,0,1.0)", id="prop_val_id_2" + ), + ], + granularity=PropertyGranularity.section, + ), + ] + annotation_property_map = _import_properties( + metadata_path, client, annotations, annotation_class_ids_map, team_slug + ) + assert annotation_property_map["1"]["0"]["prop_id_1"] == {"prop_val_id_1"} + assert annotation_property_map["2"]["0"]["prop_id_2"] == {"prop_val_id_2"} + + @patch("darwin.importer.importer._get_team_properties_annotation_lookup") @pytest.mark.parametrize("setup_data", ["section"], indirect=True) def test_import_new_section_level_properties_with_manifest(