synapse-sdk 1.0.0b5__py3-none-any.whl → 2025.12.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/code_server.py +305 -33
  3. synapse_sdk/clients/agent/__init__.py +2 -1
  4. synapse_sdk/clients/agent/container.py +143 -0
  5. synapse_sdk/clients/agent/ray.py +296 -38
  6. synapse_sdk/clients/backend/annotation.py +1 -1
  7. synapse_sdk/clients/backend/core.py +31 -4
  8. synapse_sdk/clients/backend/data_collection.py +82 -7
  9. synapse_sdk/clients/backend/hitl.py +1 -1
  10. synapse_sdk/clients/backend/ml.py +1 -1
  11. synapse_sdk/clients/base.py +211 -61
  12. synapse_sdk/loggers.py +46 -0
  13. synapse_sdk/plugins/README.md +1340 -0
  14. synapse_sdk/plugins/categories/base.py +59 -9
  15. synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
  16. synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
  17. synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
  18. synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
  19. synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
  20. synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
  21. synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
  22. synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
  23. synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
  24. synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
  25. synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -177
  26. synapse_sdk/plugins/categories/neural_net/actions/train.py +1130 -32
  27. synapse_sdk/plugins/categories/neural_net/actions/tune.py +157 -4
  28. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +7 -4
  29. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
  30. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
  31. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
  32. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
  33. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
  34. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
  35. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
  36. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
  37. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
  38. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
  39. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
  40. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
  41. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
  42. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
  43. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
  44. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
  45. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
  46. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
  47. synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
  48. synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
  49. synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
  50. synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
  51. synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
  52. synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
  53. synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
  54. synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
  55. synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
  56. synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
  57. synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
  58. synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
  59. synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
  60. synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
  61. synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
  62. synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
  63. synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
  64. synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
  65. synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
  66. synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
  67. synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
  68. synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
  69. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
  70. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
  71. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
  72. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
  73. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
  74. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
  75. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
  76. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
  77. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
  78. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
  79. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
  80. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
  81. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
  82. synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
  83. synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
  84. synapse_sdk/plugins/categories/upload/templates/config.yaml +28 -2
  85. synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
  86. synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +82 -20
  87. synapse_sdk/plugins/models.py +111 -9
  88. synapse_sdk/plugins/templates/plugin-config-schema.json +7 -0
  89. synapse_sdk/plugins/templates/schema.json +7 -0
  90. synapse_sdk/plugins/utils/__init__.py +3 -0
  91. synapse_sdk/plugins/utils/ray_gcs.py +66 -0
  92. synapse_sdk/shared/__init__.py +25 -0
  93. synapse_sdk/utils/converters/dm/__init__.py +42 -41
  94. synapse_sdk/utils/converters/dm/base.py +137 -0
  95. synapse_sdk/utils/converters/dm/from_v1.py +208 -562
  96. synapse_sdk/utils/converters/dm/to_v1.py +258 -304
  97. synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
  98. synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
  99. synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
  100. synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
  101. synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
  102. synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
  103. synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
  104. synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
  105. synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
  106. synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
  107. synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
  108. synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
  109. synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
  110. synapse_sdk/utils/converters/dm/types.py +168 -0
  111. synapse_sdk/utils/converters/dm/utils.py +162 -0
  112. synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
  113. synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
  114. synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
  115. synapse_sdk/utils/file/__init__.py +58 -0
  116. synapse_sdk/utils/file/archive.py +32 -0
  117. synapse_sdk/utils/file/checksum.py +56 -0
  118. synapse_sdk/utils/file/chunking.py +31 -0
  119. synapse_sdk/utils/file/download.py +385 -0
  120. synapse_sdk/utils/file/encoding.py +40 -0
  121. synapse_sdk/utils/file/io.py +22 -0
  122. synapse_sdk/utils/file/upload.py +165 -0
  123. synapse_sdk/utils/file/video/__init__.py +29 -0
  124. synapse_sdk/utils/file/video/transcode.py +307 -0
  125. synapse_sdk/utils/{file.py → file.py.backup} +77 -0
  126. synapse_sdk/utils/network.py +272 -0
  127. synapse_sdk/utils/storage/__init__.py +6 -2
  128. synapse_sdk/utils/storage/providers/file_system.py +6 -0
  129. {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/METADATA +19 -2
  130. {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/RECORD +134 -74
  131. synapse_sdk/devtools/docs/.gitignore +0 -20
  132. synapse_sdk/devtools/docs/README.md +0 -41
  133. synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +0 -12
  134. synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +0 -44
  135. synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
  136. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  137. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +0 -29
  138. synapse_sdk/devtools/docs/blog/authors.yml +0 -25
  139. synapse_sdk/devtools/docs/blog/tags.yml +0 -19
  140. synapse_sdk/devtools/docs/docusaurus.config.ts +0 -138
  141. synapse_sdk/devtools/docs/package-lock.json +0 -17455
  142. synapse_sdk/devtools/docs/package.json +0 -47
  143. synapse_sdk/devtools/docs/sidebars.ts +0 -44
  144. synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +0 -71
  145. synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +0 -11
  146. synapse_sdk/devtools/docs/src/css/custom.css +0 -30
  147. synapse_sdk/devtools/docs/src/pages/index.module.css +0 -23
  148. synapse_sdk/devtools/docs/src/pages/index.tsx +0 -21
  149. synapse_sdk/devtools/docs/src/pages/markdown-page.md +0 -7
  150. synapse_sdk/devtools/docs/static/.nojekyll +0 -0
  151. synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
  152. synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
  153. synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
  154. synapse_sdk/devtools/docs/static/img/logo.png +0 -0
  155. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
  156. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +0 -170
  157. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +0 -40
  158. synapse_sdk/devtools/docs/tsconfig.json +0 -8
  159. synapse_sdk/plugins/categories/export/actions/export.py +0 -346
  160. synapse_sdk/plugins/categories/export/enums.py +0 -7
  161. synapse_sdk/plugins/categories/neural_net/actions/gradio.py +0 -151
  162. synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +0 -943
  163. synapse_sdk/plugins/categories/upload/actions/upload.py +0 -954
  164. {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +0 -0
  165. {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
  166. {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/licenses/LICENSE +0 -0
  167. {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,214 @@
1
+ """
2
+ DM Schema V1/V2 Tool-Specific Processors
3
+
4
+ Created: 2025-12-11
5
+
6
+ Implement and register processors in this module when adding new tool support.
7
+ """
8
+
9
+ from typing import Any, Protocol, runtime_checkable
10
+
11
+
12
+ @runtime_checkable
13
+ class ToolProcessor(Protocol):
14
+ """Tool-Specific Conversion Processor Interface
15
+
16
+ Implement this protocol when adding new tool support.
17
+
18
+ Attributes:
19
+ tool_name: Tool name (e.g., 'bounding_box', 'polygon')
20
+
21
+ Example:
22
+ >>> class KeypointProcessor:
23
+ ... tool_name = "keypoint"
24
+ ...
25
+ ... def to_v2(self, v1_annotation, v1_data):
26
+ ... # V1 → V2 conversion logic
27
+ ... return {...}
28
+ ...
29
+ ... def to_v1(self, v2_annotation):
30
+ ... # V2 → V1 conversion logic
31
+ ... return ({...}, {...})
32
+ """
33
+
34
+ @property
35
+ def tool_name(self) -> str:
36
+ """Tool name (e.g., 'bounding_box', 'polygon')"""
37
+ ...
38
+
39
+ def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
40
+ """Convert V1 annotation to V2 format
41
+
42
+ Args:
43
+ v1_annotation: V1 annotations[] item
44
+ v1_data: V1 annotationsData[] item (same ID)
45
+
46
+ Returns:
47
+ V2 format annotation object (id, classification, attrs, data)
48
+
49
+ Raises:
50
+ ValueError: Data cannot be converted
51
+ """
52
+ ...
53
+
54
+ def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
55
+ """Convert V2 annotation to V1 format
56
+
57
+ Args:
58
+ v2_annotation: V2 annotation object
59
+
60
+ Returns:
61
+ (V1 annotation, V1 annotationData) tuple
62
+
63
+ Raises:
64
+ ValueError: Data cannot be converted
65
+ """
66
+ ...
67
+
68
+
69
+ # =============================================================================
70
+ # Processor Registry
71
+ # =============================================================================
72
+
73
+ # Global registry storing registered processors
74
+ _PROCESSOR_REGISTRY: dict[str, ToolProcessor] = {}
75
+
76
+
77
+ def register_tool_processor(processor: ToolProcessor) -> None:
78
+ """Register a tool processor to the global registry
79
+
80
+ Args:
81
+ processor: ToolProcessor implementation
82
+ """
83
+ _PROCESSOR_REGISTRY[processor.tool_name] = processor
84
+
85
+
86
+ def get_tool_processor(tool_name: str) -> ToolProcessor | None:
87
+ """Get a tool processor from the global registry
88
+
89
+ Args:
90
+ tool_name: Tool name
91
+
92
+ Returns:
93
+ Registered processor or None
94
+ """
95
+ return _PROCESSOR_REGISTRY.get(tool_name)
96
+
97
+
98
+ def get_all_processors() -> dict[str, ToolProcessor]:
99
+ """Return all registered processors
100
+
101
+ Returns:
102
+ Tool name → processor dictionary
103
+ """
104
+ return _PROCESSOR_REGISTRY.copy()
105
+
106
+
107
+ # =============================================================================
108
+ # Processor Import and Auto-Registration
109
+ # =============================================================================
110
+
111
+ # Auto-registered when processor modules are imported
112
+ # Note: Actual processors are imported later to prevent circular imports
113
+
114
+
115
+ def _register_default_processors() -> None:
116
+ """Register default processors
117
+
118
+ Called on module load to register default processors.
119
+ """
120
+ try:
121
+ from .bounding_box import BoundingBoxProcessor
122
+
123
+ register_tool_processor(BoundingBoxProcessor())
124
+ except ImportError:
125
+ pass # Not yet implemented
126
+
127
+ try:
128
+ from .polygon import PolygonProcessor
129
+
130
+ register_tool_processor(PolygonProcessor())
131
+ except ImportError:
132
+ pass # Not yet implemented
133
+
134
+ try:
135
+ from .polyline import PolylineProcessor
136
+
137
+ register_tool_processor(PolylineProcessor())
138
+ except ImportError:
139
+ pass # Not yet implemented
140
+
141
+ try:
142
+ from .keypoint import KeypointProcessor
143
+
144
+ register_tool_processor(KeypointProcessor())
145
+ except ImportError:
146
+ pass # Not yet implemented
147
+
148
+ try:
149
+ from .bounding_box_3d import BoundingBox3DProcessor
150
+
151
+ register_tool_processor(BoundingBox3DProcessor())
152
+ except ImportError:
153
+ pass # Not yet implemented
154
+
155
+ try:
156
+ from .segmentation import SegmentationProcessor
157
+
158
+ register_tool_processor(SegmentationProcessor())
159
+ except ImportError:
160
+ pass # Not yet implemented
161
+
162
+ try:
163
+ from .named_entity import NamedEntityProcessor
164
+
165
+ register_tool_processor(NamedEntityProcessor())
166
+ except ImportError:
167
+ pass # Not yet implemented
168
+
169
+ try:
170
+ from .segmentation_3d import Segmentation3DProcessor
171
+
172
+ register_tool_processor(Segmentation3DProcessor())
173
+ except ImportError:
174
+ pass # Not yet implemented
175
+
176
+ try:
177
+ from .classification import ClassificationProcessor
178
+
179
+ register_tool_processor(ClassificationProcessor())
180
+ except ImportError:
181
+ pass # Not yet implemented
182
+
183
+ try:
184
+ from .relation import RelationProcessor
185
+
186
+ register_tool_processor(RelationProcessor())
187
+ except ImportError:
188
+ pass # Not yet implemented
189
+
190
+ try:
191
+ from .prompt import PromptProcessor
192
+
193
+ register_tool_processor(PromptProcessor())
194
+ except ImportError:
195
+ pass # Not yet implemented
196
+
197
+ try:
198
+ from .answer import AnswerProcessor
199
+
200
+ register_tool_processor(AnswerProcessor())
201
+ except ImportError:
202
+ pass # Not yet implemented
203
+
204
+
205
+ # Attempt to register default processors on module load
206
+ _register_default_processors()
207
+
208
+
209
+ __all__ = [
210
+ 'ToolProcessor',
211
+ 'register_tool_processor',
212
+ 'get_tool_processor',
213
+ 'get_all_processors',
214
+ ]
@@ -0,0 +1,95 @@
1
+ """
2
+ Answer Tool Processor
3
+
4
+ Created: 2025-12-12
5
+
6
+ Conversion Rules (see data-model.md 9.11):
7
+ V1 → V2:
8
+ - output → data.output
9
+ - model, displayName, generatedBy, promptAnnotationId → preserved in data
10
+ - classification.class → classification
11
+ - classification.{other} → attrs[{name, value}]
12
+
13
+ V2 → V1:
14
+ - data.output → output
15
+ - Other fields from data → preserved in annotationsData
16
+ - classification → classification.class
17
+ - attrs[{name, value}] → classification.{name: value}
18
+ """
19
+
20
+ from typing import Any
21
+
22
+
23
+ class AnswerProcessor:
24
+ """Answer Tool Processor
25
+
26
+ V1 annotationsData: {id, tool, model, output: [{type, value, primaryKey, changeHistory}],
27
+ displayName, generatedBy, promptAnnotationId}
28
+ V2 data: {output: [...], model, displayName, generatedBy, promptAnnotationId, timestamp?}
29
+
30
+ Answer annotation data conversion.
31
+ """
32
+
33
+ tool_name = 'answer'
34
+
35
+ _META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
36
+ _INTERNAL_ATTR_PREFIX = '_'
37
+ # Fields to copy from annotationsData to V2 data
38
+ _DATA_FIELDS = {'output', 'model', 'displayName', 'generatedBy', 'promptAnnotationId', 'timestamp'}
39
+
40
+ def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
41
+ """Convert V1 answer to V2"""
42
+ classification_obj = v1_annotation.get('classification') or {}
43
+
44
+ # Build V2 data (output and other fields)
45
+ data: dict[str, Any] = {}
46
+ for key in self._DATA_FIELDS:
47
+ if key in v1_data:
48
+ data[key] = v1_data[key]
49
+
50
+ # Build V2 attrs (all classification properties excluding class)
51
+ attrs: list[dict[str, Any]] = []
52
+ for key, value in classification_obj.items():
53
+ if key != 'class':
54
+ attrs.append({'name': key, 'value': value})
55
+
56
+ return {
57
+ 'id': v1_annotation.get('id', ''),
58
+ 'classification': classification_obj.get('class', ''),
59
+ 'attrs': attrs,
60
+ 'data': data,
61
+ }
62
+
63
+ def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
64
+ """Convert V2 answer to V1"""
65
+ annotation_id = v2_annotation.get('id', '')
66
+ classification_str = v2_annotation.get('classification', '')
67
+ attrs = v2_annotation.get('attrs', [])
68
+ data = v2_annotation.get('data', {})
69
+
70
+ # Build V1 classification
71
+ classification: dict[str, Any] = {'class': classification_str}
72
+ for attr in attrs:
73
+ name = attr.get('name', '')
74
+ value = attr.get('value')
75
+ if not name.startswith(self._INTERNAL_ATTR_PREFIX):
76
+ classification[name] = value
77
+
78
+ v1_annotation: dict[str, Any] = {
79
+ 'id': annotation_id,
80
+ 'tool': self.tool_name,
81
+ 'classification': classification,
82
+ }
83
+
84
+ # Build V1 annotationsData
85
+ v1_data: dict[str, Any] = {
86
+ 'id': annotation_id,
87
+ 'tool': self.tool_name,
88
+ }
89
+
90
+ # Copy fields from data
91
+ for key in self._DATA_FIELDS:
92
+ if key in data:
93
+ v1_data[key] = data[key]
94
+
95
+ return v1_annotation, v1_data
@@ -0,0 +1,132 @@
1
+ """
2
+ Bounding Box Tool Processor
3
+
4
+ Created: 2025-12-11
5
+
6
+ Conversion Rules (see data-model.md 4.1):
7
+ V1 → V2:
8
+ - coordinate.{x, y, width, height} → data[x, y, width, height]
9
+ - coordinate.rotation → attrs[{name:"rotation", value}]
10
+ - classification.class → classification
11
+ - classification.{other} → attrs[{name, value}]
12
+
13
+ V2 → V1:
14
+ - data[0,1,2,3] → coordinate.{x, y, width, height}
15
+ - attrs.rotation → coordinate.rotation
16
+ - classification → classification.class
17
+ - attrs[{name, value}] → classification.{name: value} (excluding special attrs)
18
+ """
19
+
20
+ from typing import Any
21
+
22
+
23
+ class BoundingBoxProcessor:
24
+ """Bounding Box Tool Processor
25
+
26
+ V1 coordinate: {x, y, width, height, rotation?}
27
+ V2 data: [x, y, width, height]
28
+ """
29
+
30
+ tool_name = 'bounding_box'
31
+
32
+ # V1 meta fields (not stored in attrs)
33
+ _META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
34
+
35
+ # Fields to restore from V2 attrs to coordinate
36
+ _COORDINATE_ATTRS = {'rotation'}
37
+
38
+ # Special attrs not restored to V1 classification (_ prefix)
39
+ _INTERNAL_ATTR_PREFIX = '_'
40
+
41
+ def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
42
+ """Convert V1 bounding box to V2
43
+
44
+ Args:
45
+ v1_annotation: V1 annotations[] item
46
+ v1_data: V1 annotationsData[] item (same ID)
47
+
48
+ Returns:
49
+ V2 format bounding box annotation
50
+ """
51
+ coordinate = v1_data.get('coordinate', {})
52
+ classification_obj = v1_annotation.get('classification') or {}
53
+
54
+ # V2 data: [x, y, width, height]
55
+ data = [
56
+ coordinate.get('x', 0),
57
+ coordinate.get('y', 0),
58
+ coordinate.get('width', 0),
59
+ coordinate.get('height', 0),
60
+ ]
61
+
62
+ # Build V2 attrs
63
+ attrs: list[dict[str, Any]] = []
64
+
65
+ # Add rotation to attrs
66
+ if 'rotation' in coordinate:
67
+ attrs.append({'name': 'rotation', 'value': coordinate['rotation']})
68
+
69
+ # Add other classification properties to attrs (excluding class)
70
+ for key, value in classification_obj.items():
71
+ if key != 'class':
72
+ attrs.append({'name': key, 'value': value})
73
+
74
+ # Build V2 annotation
75
+ return {
76
+ 'id': v1_annotation.get('id', ''),
77
+ 'classification': classification_obj.get('class', ''),
78
+ 'attrs': attrs,
79
+ 'data': data,
80
+ }
81
+
82
+ def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
83
+ """Convert V2 bounding box to V1
84
+
85
+ Args:
86
+ v2_annotation: V2 annotation object
87
+
88
+ Returns:
89
+ (V1 annotation, V1 annotationData) tuple
90
+ """
91
+ annotation_id = v2_annotation.get('id', '')
92
+ classification_str = v2_annotation.get('classification', '')
93
+ attrs = v2_annotation.get('attrs', [])
94
+ data = v2_annotation.get('data', [0, 0, 0, 0])
95
+
96
+ # Build V1 coordinate
97
+ coordinate: dict[str, Any] = {
98
+ 'x': data[0] if len(data) > 0 else 0,
99
+ 'y': data[1] if len(data) > 1 else 0,
100
+ 'width': data[2] if len(data) > 2 else 0,
101
+ 'height': data[3] if len(data) > 3 else 0,
102
+ }
103
+
104
+ # Build V1 classification
105
+ classification: dict[str, Any] = {'class': classification_str}
106
+
107
+ # Restore properties from attrs
108
+ for attr in attrs:
109
+ name = attr.get('name', '')
110
+ value = attr.get('value')
111
+
112
+ if name in self._COORDINATE_ATTRS:
113
+ # Add rotation etc. to coordinate
114
+ coordinate[name] = value
115
+ elif not name.startswith(self._INTERNAL_ATTR_PREFIX):
116
+ # Add non-internal attrs to classification
117
+ classification[name] = value
118
+
119
+ # V1 annotation (meta info)
120
+ v1_annotation: dict[str, Any] = {
121
+ 'id': annotation_id,
122
+ 'tool': self.tool_name,
123
+ 'classification': classification,
124
+ }
125
+
126
+ # V1 annotationData (coordinate info)
127
+ v1_data: dict[str, Any] = {
128
+ 'id': annotation_id,
129
+ 'coordinate': coordinate,
130
+ }
131
+
132
+ return v1_annotation, v1_data
@@ -0,0 +1,121 @@
1
+ """
2
+ 3D Bounding Box Tool Processor
3
+
4
+ Created: 2025-12-12
5
+
6
+ Conversion Rules (see data-model.md 9.6):
7
+ V1 → V2:
8
+ - psr {position, scale, rotation} → data {position, scale, rotation}
9
+ - classification.class → classification
10
+ - classification.{other} → attrs[{name, value}]
11
+
12
+ V2 → V1:
13
+ - data {position, scale, rotation} → psr {position, scale, rotation}
14
+ - classification → classification.class
15
+ - attrs[{name, value}] → classification.{name: value} (excluding special attrs)
16
+ """
17
+
18
+ from typing import Any
19
+
20
+
21
+ class BoundingBox3DProcessor:
22
+ """3D Bounding Box Tool Processor
23
+
24
+ V1 psr: {position, scale, rotation} each {x, y, z}
25
+ V2 data: {position, scale, rotation} each {x, y, z}
26
+
27
+ PSR structure is identical, only key name changes (psr → data).
28
+ Used with pcd (point cloud) media type.
29
+ """
30
+
31
+ tool_name = '3d_bounding_box'
32
+
33
+ # V1 meta fields (not stored in attrs)
34
+ _META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
35
+
36
+ # Special attrs not restored to V1 classification (_ prefix)
37
+ _INTERNAL_ATTR_PREFIX = '_'
38
+
39
+ def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
40
+ """Convert V1 3D bounding box to V2
41
+
42
+ Args:
43
+ v1_annotation: V1 annotations[] item
44
+ v1_data: V1 annotationsData[] item (same ID)
45
+
46
+ Returns:
47
+ V2 format 3D bounding box annotation
48
+ """
49
+ psr = v1_data.get('psr', {})
50
+ classification_obj = v1_annotation.get('classification') or {}
51
+
52
+ # V2 data: copy psr structure as-is
53
+ data = {
54
+ 'position': psr.get('position', {'x': 0, 'y': 0, 'z': 0}),
55
+ 'scale': psr.get('scale', {'x': 0, 'y': 0, 'z': 0}),
56
+ 'rotation': psr.get('rotation', {'x': 0, 'y': 0, 'z': 0}),
57
+ }
58
+
59
+ # Build V2 attrs
60
+ attrs: list[dict[str, Any]] = []
61
+
62
+ # Add other classification properties to attrs (excluding class)
63
+ for key, value in classification_obj.items():
64
+ if key != 'class':
65
+ attrs.append({'name': key, 'value': value})
66
+
67
+ # Build V2 annotation
68
+ return {
69
+ 'id': v1_annotation.get('id', ''),
70
+ 'classification': classification_obj.get('class', ''),
71
+ 'attrs': attrs,
72
+ 'data': data,
73
+ }
74
+
75
+ def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
76
+ """Convert V2 3D bounding box to V1
77
+
78
+ Args:
79
+ v2_annotation: V2 annotation object
80
+
81
+ Returns:
82
+ (V1 annotation, V1 annotationData) tuple
83
+ """
84
+ annotation_id = v2_annotation.get('id', '')
85
+ classification_str = v2_annotation.get('classification', '')
86
+ attrs = v2_annotation.get('attrs', [])
87
+ data = v2_annotation.get('data', {})
88
+
89
+ # Build V1 psr: copy data structure as-is
90
+ psr: dict[str, Any] = {
91
+ 'position': data.get('position', {'x': 0, 'y': 0, 'z': 0}),
92
+ 'scale': data.get('scale', {'x': 0, 'y': 0, 'z': 0}),
93
+ 'rotation': data.get('rotation', {'x': 0, 'y': 0, 'z': 0}),
94
+ }
95
+
96
+ # Build V1 classification
97
+ classification: dict[str, Any] = {'class': classification_str}
98
+
99
+ # Restore properties from attrs
100
+ for attr in attrs:
101
+ name = attr.get('name', '')
102
+ value = attr.get('value')
103
+
104
+ if not name.startswith(self._INTERNAL_ATTR_PREFIX):
105
+ # Add non-internal attrs to classification
106
+ classification[name] = value
107
+
108
+ # V1 annotation (meta info)
109
+ v1_annotation: dict[str, Any] = {
110
+ 'id': annotation_id,
111
+ 'tool': self.tool_name,
112
+ 'classification': classification,
113
+ }
114
+
115
+ # V1 annotationData (coordinate info)
116
+ v1_data: dict[str, Any] = {
117
+ 'id': annotation_id,
118
+ 'psr': psr,
119
+ }
120
+
121
+ return v1_annotation, v1_data
@@ -0,0 +1,75 @@
1
+ """
2
+ Classification Tool Processor
3
+
4
+ Created: 2025-12-12
5
+
6
+ Conversion Rules (see data-model.md 9.8):
7
+ V1 → V2:
8
+ - classification.class → classification
9
+ - classification.{other} → attrs[{name, value}]
10
+ - annotationsData contains only id
11
+
12
+ V2 → V1:
13
+ - classification → classification.class
14
+ - attrs[{name, value}] → classification.{name: value}
15
+ - data is empty object {}
16
+ """
17
+
18
+ from typing import Any
19
+
20
+
21
+ class ClassificationProcessor:
22
+ """Classification Tool Processor
23
+
24
+ V1: All properties in annotations, only id in annotationsData
25
+ V2: data is empty object, all properties stored in attrs
26
+ """
27
+
28
+ tool_name = 'classification'
29
+
30
+ _META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
31
+ _INTERNAL_ATTR_PREFIX = '_'
32
+
33
+ def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
34
+ """Convert V1 classification to V2"""
35
+ classification_obj = v1_annotation.get('classification') or {}
36
+
37
+ # Build V2 attrs (all properties excluding class)
38
+ attrs: list[dict[str, Any]] = []
39
+ for key, value in classification_obj.items():
40
+ if key != 'class':
41
+ attrs.append({'name': key, 'value': value})
42
+
43
+ return {
44
+ 'id': v1_annotation.get('id', ''),
45
+ 'classification': classification_obj.get('class', ''),
46
+ 'attrs': attrs,
47
+ 'data': {}, # Empty object
48
+ }
49
+
50
+ def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
51
+ """Convert V2 classification to V1"""
52
+ annotation_id = v2_annotation.get('id', '')
53
+ classification_str = v2_annotation.get('classification', '')
54
+ attrs = v2_annotation.get('attrs', [])
55
+
56
+ # Build V1 classification
57
+ classification: dict[str, Any] = {'class': classification_str}
58
+ for attr in attrs:
59
+ name = attr.get('name', '')
60
+ value = attr.get('value')
61
+ if not name.startswith(self._INTERNAL_ATTR_PREFIX):
62
+ classification[name] = value
63
+
64
+ v1_annotation: dict[str, Any] = {
65
+ 'id': annotation_id,
66
+ 'tool': self.tool_name,
67
+ 'classification': classification,
68
+ }
69
+
70
+ # annotationsData contains only id
71
+ v1_data: dict[str, Any] = {
72
+ 'id': annotation_id,
73
+ }
74
+
75
+ return v1_annotation, v1_data