11import logging
2+ import uuid
23from typing import Any , Dict , Generator , Iterable
34
45from ...annotation_types .collection import LabelCollection , LabelGenerator
6+ from ...annotation_types .relationship import RelationshipAnnotation
57from .label import NDLabel
68
79logger = logging .getLogger (__name__ )
@@ -21,7 +23,7 @@ def deserialize(json_data: Iterable[Dict[str, Any]]) -> LabelGenerator:
2123 Returns:
2224 LabelGenerator containing the ndjson data.
2325 """
24- data = NDLabel (** {' annotations' : json_data })
26+ data = NDLabel (** {" annotations" : json_data })
2527 res = data .to_common ()
2628 return res
2729
@@ -40,8 +42,50 @@ def serialize(
4042 Returns:
4143 A generator for accessing the ndjson representation of the data
4244 """
45+ used_annotation_uuids = set ()
46+ for label in labels :
47+ annotation_uuid_to_generated_uuid_lookup = {}
48+ # UUIDs are private properties used to enhance UX when defining relationships.
49+ # They are created for all annotations, but only utilized for relationships.
50+ # To avoid overwriting, UUIDs must be unique across labels.
51+ # Non-relationship annotation UUIDs are dropped (server-side generation will occur).
52+ # For relationship annotations, new UUIDs are generated and stored in a lookup table.
53+ for annotation in label .annotations :
54+ if isinstance (annotation , RelationshipAnnotation ):
55+ source_uuid = annotation .value .source ._uuid
56+ target_uuid = annotation .value .target ._uuid
57+
58+ if (len (
59+ used_annotation_uuids .intersection (
60+ {source_uuid , target_uuid })) > 0 ):
61+ new_source_uuid = uuid .uuid4 ()
62+ new_target_uuid = uuid .uuid4 ()
63+
64+ annotation_uuid_to_generated_uuid_lookup [
65+ source_uuid ] = new_source_uuid
66+ annotation_uuid_to_generated_uuid_lookup [
67+ target_uuid ] = new_target_uuid
68+ annotation .value .source ._uuid = new_source_uuid
69+ annotation .value .target ._uuid = new_target_uuid
70+ else :
71+ annotation_uuid_to_generated_uuid_lookup [
72+ source_uuid ] = source_uuid
73+ annotation_uuid_to_generated_uuid_lookup [
74+ target_uuid ] = target_uuid
75+ used_annotation_uuids .add (annotation ._uuid )
76+
77+ for annotation in label .annotations :
78+ if (not isinstance (annotation , RelationshipAnnotation ) and
79+ hasattr (annotation , "_uuid" )):
80+ annotation ._uuid = annotation_uuid_to_generated_uuid_lookup .get (
81+ annotation ._uuid , annotation ._uuid )
82+
4383 for example in NDLabel .from_common (labels ):
44- res = example .dict (by_alias = True )
84+ annotation_uuid = getattr (example , "uuid" , None )
85+
86+ res = example .dict (
87+ by_alias = True ,
88+ exclude = {"uuid" } if annotation_uuid == "None" else None )
4589 for k , v in list (res .items ()):
4690 if k in IGNORE_IF_NONE and v is None :
4791 del res [k ]
0 commit comments