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.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/code_server.py +305 -33
- synapse_sdk/clients/agent/__init__.py +2 -1
- synapse_sdk/clients/agent/container.py +143 -0
- synapse_sdk/clients/agent/ray.py +296 -38
- synapse_sdk/clients/backend/annotation.py +1 -1
- synapse_sdk/clients/backend/core.py +31 -4
- synapse_sdk/clients/backend/data_collection.py +82 -7
- synapse_sdk/clients/backend/hitl.py +1 -1
- synapse_sdk/clients/backend/ml.py +1 -1
- synapse_sdk/clients/base.py +211 -61
- synapse_sdk/loggers.py +46 -0
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/categories/base.py +59 -9
- synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
- synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
- synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
- synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
- synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
- synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
- synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
- synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -177
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1130 -32
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +157 -4
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +7 -4
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
- synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
- synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
- synapse_sdk/plugins/categories/upload/templates/config.yaml +28 -2
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +82 -20
- synapse_sdk/plugins/models.py +111 -9
- synapse_sdk/plugins/templates/plugin-config-schema.json +7 -0
- synapse_sdk/plugins/templates/schema.json +7 -0
- synapse_sdk/plugins/utils/__init__.py +3 -0
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/converters/dm/__init__.py +42 -41
- synapse_sdk/utils/converters/dm/base.py +137 -0
- synapse_sdk/utils/converters/dm/from_v1.py +208 -562
- synapse_sdk/utils/converters/dm/to_v1.py +258 -304
- synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
- synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
- synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
- synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
- synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
- synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
- synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
- synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
- synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
- synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
- synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
- synapse_sdk/utils/converters/dm/types.py +168 -0
- synapse_sdk/utils/converters/dm/utils.py +162 -0
- synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
- synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +32 -0
- synapse_sdk/utils/file/checksum.py +56 -0
- synapse_sdk/utils/file/chunking.py +31 -0
- synapse_sdk/utils/file/download.py +385 -0
- synapse_sdk/utils/file/encoding.py +40 -0
- synapse_sdk/utils/file/io.py +22 -0
- synapse_sdk/utils/file/upload.py +165 -0
- synapse_sdk/utils/file/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/{file.py → file.py.backup} +77 -0
- synapse_sdk/utils/network.py +272 -0
- synapse_sdk/utils/storage/__init__.py +6 -2
- synapse_sdk/utils/storage/providers/file_system.py +6 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/METADATA +19 -2
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/RECORD +134 -74
- synapse_sdk/devtools/docs/.gitignore +0 -20
- synapse_sdk/devtools/docs/README.md +0 -41
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +0 -12
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +0 -44
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +0 -29
- synapse_sdk/devtools/docs/blog/authors.yml +0 -25
- synapse_sdk/devtools/docs/blog/tags.yml +0 -19
- synapse_sdk/devtools/docs/docusaurus.config.ts +0 -138
- synapse_sdk/devtools/docs/package-lock.json +0 -17455
- synapse_sdk/devtools/docs/package.json +0 -47
- synapse_sdk/devtools/docs/sidebars.ts +0 -44
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +0 -71
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- synapse_sdk/devtools/docs/src/css/custom.css +0 -30
- synapse_sdk/devtools/docs/src/pages/index.module.css +0 -23
- synapse_sdk/devtools/docs/src/pages/index.tsx +0 -21
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +0 -7
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +0 -170
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- synapse_sdk/devtools/docs/tsconfig.json +0 -8
- synapse_sdk/plugins/categories/export/actions/export.py +0 -346
- synapse_sdk/plugins/categories/export/enums.py +0 -7
- synapse_sdk/plugins/categories/neural_net/actions/gradio.py +0 -151
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +0 -943
- synapse_sdk/plugins/categories/upload/actions/upload.py +0 -954
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
|
@@ -11,7 +11,8 @@ from synapse_sdk.clients.ray import RayClient
|
|
|
11
11
|
from synapse_sdk.plugins.enums import RunMethod
|
|
12
12
|
from synapse_sdk.plugins.exceptions import ActionError
|
|
13
13
|
from synapse_sdk.plugins.models import PluginRelease, Run
|
|
14
|
-
from synapse_sdk.plugins.upload import archive_and_upload, build_and_upload
|
|
14
|
+
from synapse_sdk.plugins.upload import archive_and_upload, build_and_upload
|
|
15
|
+
from synapse_sdk.shared import init_sentry, needs_sentry_init
|
|
15
16
|
from synapse_sdk.utils.module_loading import import_string
|
|
16
17
|
from synapse_sdk.utils.pydantic.errors import pydantic_to_drf_error
|
|
17
18
|
|
|
@@ -108,15 +109,37 @@ class Action:
|
|
|
108
109
|
def plugin_url(self):
|
|
109
110
|
if self.debug:
|
|
110
111
|
plugin_path = self.envs.get('SYNAPSE_DEBUG_PLUGIN_PATH') or '.'
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
|
|
113
|
+
# For HTTP/HTTPS URLs in debug mode, convert to Ray GCS (Global Control Store) URL
|
|
114
|
+
if plugin_path.startswith(('http://', 'https://')):
|
|
115
|
+
try:
|
|
116
|
+
from synapse_sdk.plugins.utils import convert_http_to_ray_gcs
|
|
117
|
+
|
|
118
|
+
plugin_url = convert_http_to_ray_gcs(plugin_path)
|
|
119
|
+
except (ImportError, RuntimeError):
|
|
120
|
+
plugin_url = plugin_path
|
|
121
|
+
|
|
122
|
+
elif self.envs.get('SYNAPSE_PLUGIN_STORAGE'):
|
|
116
123
|
plugin_url = archive_and_upload(plugin_path, self.plugin_storage_url)
|
|
124
|
+
else:
|
|
125
|
+
plugin_url = plugin_path
|
|
126
|
+
|
|
117
127
|
self.envs['SYNAPSE_DEBUG_PLUGIN_PATH'] = plugin_url
|
|
118
128
|
return plugin_url
|
|
119
|
-
|
|
129
|
+
|
|
130
|
+
# Production path: get URL from storage provider
|
|
131
|
+
url = self.plugin_release.get_url(self.plugin_storage_url)
|
|
132
|
+
|
|
133
|
+
# Convert HTTP URLs to Ray GCS URLs if needed
|
|
134
|
+
if url.startswith(('http://', 'https://')):
|
|
135
|
+
try:
|
|
136
|
+
from synapse_sdk.plugins.utils import convert_http_to_ray_gcs
|
|
137
|
+
|
|
138
|
+
url = convert_http_to_ray_gcs(url)
|
|
139
|
+
except (ImportError, RuntimeError):
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
return url
|
|
120
143
|
|
|
121
144
|
@property
|
|
122
145
|
def debug_modules(self):
|
|
@@ -138,8 +161,26 @@ class Action:
|
|
|
138
161
|
|
|
139
162
|
@property
|
|
140
163
|
def package_manager_options(self):
|
|
141
|
-
|
|
142
|
-
|
|
164
|
+
# Get user-defined options from plugin config
|
|
165
|
+
user_options = self.plugin_config.get('package_manager_options', [])
|
|
166
|
+
|
|
167
|
+
if self.plugin_package_manager == 'uv':
|
|
168
|
+
defaults = ['--no-cache']
|
|
169
|
+
# Add defaults if not already present
|
|
170
|
+
options_list = defaults.copy()
|
|
171
|
+
for option in user_options:
|
|
172
|
+
if option not in options_list:
|
|
173
|
+
options_list.append(option)
|
|
174
|
+
return {'uv_pip_install_options': options_list}
|
|
175
|
+
else:
|
|
176
|
+
# For pip, use pip_install_options with --upgrade flag to ensure
|
|
177
|
+
# packages from requirements.txt (like synapse-sdk) override pre-installed versions
|
|
178
|
+
defaults = ['--upgrade']
|
|
179
|
+
options_list = defaults.copy()
|
|
180
|
+
for option in user_options:
|
|
181
|
+
if option not in options_list:
|
|
182
|
+
options_list.append(option)
|
|
183
|
+
return {'pip_install_options': options_list}
|
|
143
184
|
|
|
144
185
|
def get_run(self):
|
|
145
186
|
context = {
|
|
@@ -149,6 +190,7 @@ class Action:
|
|
|
149
190
|
'params': self.params,
|
|
150
191
|
'envs': self.envs,
|
|
151
192
|
'debug': self.debug,
|
|
193
|
+
'action_name': self.name,
|
|
152
194
|
}
|
|
153
195
|
return self.run_class(self.job_id, context)
|
|
154
196
|
|
|
@@ -167,6 +209,10 @@ class Action:
|
|
|
167
209
|
for key, value in self.package_manager_options.items():
|
|
168
210
|
runtime_env[self.plugin_package_manager][key] = value
|
|
169
211
|
|
|
212
|
+
# Sentry init if SENTRY_DSN is set
|
|
213
|
+
if needs_sentry_init():
|
|
214
|
+
runtime_env['worker_process_setup_hook'] = 'synapse_sdk.shared.worker_process_setup_hook'
|
|
215
|
+
|
|
170
216
|
# 맨 마지막에 진행되어야 함
|
|
171
217
|
runtime_env['env_vars'] = self.envs
|
|
172
218
|
|
|
@@ -243,6 +289,8 @@ class Action:
|
|
|
243
289
|
|
|
244
290
|
* Executes the entrypoint with Ray job. Ray job manages the entrypoint execution and stores the results.
|
|
245
291
|
"""
|
|
292
|
+
self.ray_init()
|
|
293
|
+
|
|
246
294
|
main_options = []
|
|
247
295
|
options = ['run', '--direct']
|
|
248
296
|
arguments = [self.name, f'{json.dumps(json.dumps(self.params))}']
|
|
@@ -297,5 +345,7 @@ class Action:
|
|
|
297
345
|
def ray_init(self):
|
|
298
346
|
import ray
|
|
299
347
|
|
|
348
|
+
init_sentry()
|
|
349
|
+
|
|
300
350
|
if not ray.is_initialized():
|
|
301
351
|
ray.init(address=self.envs['RAY_ADDRESS'], ignore_reinit_error=True)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from .action import ExportAction
|
|
2
|
+
from .enums import ExportStatus, LogCode
|
|
3
|
+
from .exceptions import ExportError, ExportTargetError, ExportValidationError
|
|
4
|
+
from .models import ExportParams
|
|
5
|
+
from .run import ExportRun
|
|
6
|
+
from .utils import (
|
|
7
|
+
AssignmentExportTargetHandler,
|
|
8
|
+
ExportTargetHandler,
|
|
9
|
+
GroundTruthExportTargetHandler,
|
|
10
|
+
TargetHandlerFactory,
|
|
11
|
+
TaskExportTargetHandler,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'ExportAction',
|
|
16
|
+
'ExportStatus',
|
|
17
|
+
'LogCode',
|
|
18
|
+
'ExportError',
|
|
19
|
+
'ExportTargetError',
|
|
20
|
+
'ExportValidationError',
|
|
21
|
+
'ExportParams',
|
|
22
|
+
'ExportRun',
|
|
23
|
+
'ExportTargetHandler',
|
|
24
|
+
'AssignmentExportTargetHandler',
|
|
25
|
+
'GroundTruthExportTargetHandler',
|
|
26
|
+
'TaskExportTargetHandler',
|
|
27
|
+
'TargetHandlerFactory',
|
|
28
|
+
]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from itertools import tee
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
4
|
+
from pydantic_core import PydanticCustomError
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.clients.exceptions import ClientError
|
|
7
|
+
from synapse_sdk.i18n import gettext as _
|
|
8
|
+
from synapse_sdk.plugins.categories.base import Action
|
|
9
|
+
from synapse_sdk.plugins.categories.decorators import register_action
|
|
10
|
+
from synapse_sdk.plugins.enums import PluginCategory, RunMethod
|
|
11
|
+
from synapse_sdk.utils.storage import get_pathlib
|
|
12
|
+
|
|
13
|
+
from .enums import LogCode
|
|
14
|
+
from .models import ExportParams
|
|
15
|
+
from .run import ExportRun
|
|
16
|
+
from .utils import TargetHandlerFactory
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@register_action
|
|
20
|
+
class ExportAction(Action):
|
|
21
|
+
"""Main export action for processing and exporting data from various targets.
|
|
22
|
+
|
|
23
|
+
Handles export operations including target validation, data retrieval,
|
|
24
|
+
and file generation. Supports export from assignment, ground_truth, and task
|
|
25
|
+
targets with comprehensive progress tracking and error handling.
|
|
26
|
+
|
|
27
|
+
Features:
|
|
28
|
+
- Multiple target source support (assignment, ground_truth, task)
|
|
29
|
+
- Filter validation and data retrieval
|
|
30
|
+
- Original file and data file export options
|
|
31
|
+
- Progress tracking with detailed metrics
|
|
32
|
+
- Comprehensive error logging
|
|
33
|
+
- Project configuration handling
|
|
34
|
+
|
|
35
|
+
Class Attributes:
|
|
36
|
+
name (str): Action identifier ('export')
|
|
37
|
+
category (PluginCategory): EXPORT category
|
|
38
|
+
method (RunMethod): JOB execution method
|
|
39
|
+
run_class (type): ExportRun for specialized logging
|
|
40
|
+
params_model (type): ExportParams for parameter validation
|
|
41
|
+
progress_categories (dict): Progress tracking configuration
|
|
42
|
+
metrics_categories (dict): Metrics collection configuration
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> action = ExportAction(
|
|
46
|
+
... params={
|
|
47
|
+
... 'name': 'Assignment Export',
|
|
48
|
+
... 'storage': 1,
|
|
49
|
+
... 'path': '/exports/assignments',
|
|
50
|
+
... 'target': 'assignment',
|
|
51
|
+
... 'filter': {'project': 123}
|
|
52
|
+
... },
|
|
53
|
+
... plugin_config=config
|
|
54
|
+
... )
|
|
55
|
+
>>> result = action.start()
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
name = 'export'
|
|
59
|
+
category = PluginCategory.EXPORT
|
|
60
|
+
method = RunMethod.JOB
|
|
61
|
+
params_model = ExportParams
|
|
62
|
+
run_class = ExportRun
|
|
63
|
+
progress_categories = {
|
|
64
|
+
'dataset_conversion': {
|
|
65
|
+
'proportion': 100,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
metrics_categories = {
|
|
69
|
+
'data_file': {
|
|
70
|
+
'stand_by': 0,
|
|
71
|
+
'failed': 0,
|
|
72
|
+
'success': 0,
|
|
73
|
+
},
|
|
74
|
+
'original_file': {
|
|
75
|
+
'stand_by': 0,
|
|
76
|
+
'failed': 0,
|
|
77
|
+
'success': 0,
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def get_filtered_results(self, filters, handler):
|
|
82
|
+
"""Get filtered target results.
|
|
83
|
+
|
|
84
|
+
Retrieves data from the specified target using the provided filters
|
|
85
|
+
through the appropriate target handler.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
filters (dict): Filter criteria to apply
|
|
89
|
+
handler (ExportTargetHandler): Target-specific handler
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
tuple: (results, count) where results is the data and count is total
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
PydanticCustomError: If data retrieval fails
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
result_list = handler.get_results(self.client, filters)
|
|
99
|
+
results = result_list[0]
|
|
100
|
+
count = result_list[1]
|
|
101
|
+
except ClientError:
|
|
102
|
+
raise PydanticCustomError('client_error', _('Unable to get dataset.'))
|
|
103
|
+
return results, count
|
|
104
|
+
|
|
105
|
+
def start(self) -> Dict[str, Any]:
|
|
106
|
+
"""Start the export process.
|
|
107
|
+
|
|
108
|
+
Main entry point for export operations. Handles parameter preparation,
|
|
109
|
+
target handler selection, data retrieval, and export execution.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Dict[str, Any]: Export results from the entrypoint
|
|
113
|
+
|
|
114
|
+
Raises:
|
|
115
|
+
Various exceptions based on validation and processing failures
|
|
116
|
+
"""
|
|
117
|
+
self.run.log_message_with_code(LogCode.EXPORT_STARTED)
|
|
118
|
+
|
|
119
|
+
# Get expand setting from config, default to True (expand data)
|
|
120
|
+
filters = {**self.params['filter']}
|
|
121
|
+
data_expand = self.config.get('data_expand', True)
|
|
122
|
+
if data_expand:
|
|
123
|
+
filters['expand'] = 'data'
|
|
124
|
+
|
|
125
|
+
target = self.params['target']
|
|
126
|
+
handler = TargetHandlerFactory.get_handler(target)
|
|
127
|
+
|
|
128
|
+
self.params['results'], self.params['count'] = self.get_filtered_results(filters, handler)
|
|
129
|
+
|
|
130
|
+
if self.params['count'] == 0:
|
|
131
|
+
self.run.log_message_with_code(LogCode.NO_RESULTS_FOUND)
|
|
132
|
+
else:
|
|
133
|
+
self.run.log_message_with_code(LogCode.RESULTS_RETRIEVED, self.params['count'])
|
|
134
|
+
|
|
135
|
+
# For the 'ground_truth' target, retrieve project information from the first result and add configuration
|
|
136
|
+
if target == 'ground_truth':
|
|
137
|
+
try:
|
|
138
|
+
# Split generator into two using tee()
|
|
139
|
+
peek_iter, main_iter = tee(self.params['results'])
|
|
140
|
+
first_result = next(peek_iter) # Peek first value only
|
|
141
|
+
project_pk = first_result['project']
|
|
142
|
+
project_info = self.client.get_project(project_pk)
|
|
143
|
+
self.params['project_id'] = project_pk
|
|
144
|
+
self.params['configuration'] = project_info.get('configuration', {})
|
|
145
|
+
self.params['results'] = main_iter # Keep original generator intact
|
|
146
|
+
except (StopIteration, KeyError):
|
|
147
|
+
self.params['configuration'] = {}
|
|
148
|
+
# For the 'assignment' and 'task' targets, retrieve the project from the filter as before
|
|
149
|
+
elif target in ['assignment', 'task'] and 'project' in self.params['filter']:
|
|
150
|
+
project_pk = self.params['filter']['project']
|
|
151
|
+
project_info = self.client.get_project(project_pk)
|
|
152
|
+
self.params['configuration'] = project_info.get('configuration', {})
|
|
153
|
+
|
|
154
|
+
export_items = handler.get_export_item(self.params['results'])
|
|
155
|
+
storage = self.client.get_storage(self.params['storage'])
|
|
156
|
+
pathlib_cwd = get_pathlib(storage, self.params['path'])
|
|
157
|
+
exporter = self.entrypoint(self.run, export_items, pathlib_cwd, **self.params)
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
result = exporter.export()
|
|
161
|
+
self.run.log_message_with_code(LogCode.EXPORT_COMPLETED)
|
|
162
|
+
return result
|
|
163
|
+
except Exception as e:
|
|
164
|
+
self.run.log_message_with_code(LogCode.EXPORT_FAILED, str(e))
|
|
165
|
+
raise
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from synapse_sdk.shared.enums import Context
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExportStatus(str, Enum):
|
|
7
|
+
"""Export processing status enumeration.
|
|
8
|
+
|
|
9
|
+
Defines the possible states for export operations, data files, and export items
|
|
10
|
+
throughout the export process.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
SUCCESS: Export completed successfully
|
|
14
|
+
FAILED: Export failed with errors
|
|
15
|
+
STAND_BY: Export waiting to be processed
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
SUCCESS = 'success'
|
|
19
|
+
FAILED = 'failed'
|
|
20
|
+
STAND_BY = 'stand_by'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LogCode(str, Enum):
|
|
24
|
+
"""Type-safe logging codes for export operations.
|
|
25
|
+
|
|
26
|
+
Enumeration of all possible log events during export processing. Each code
|
|
27
|
+
corresponds to a specific event or error state with predefined message
|
|
28
|
+
templates and log levels.
|
|
29
|
+
|
|
30
|
+
The codes are organized by category:
|
|
31
|
+
- Validation codes (VALIDATION_FAILED, STORAGE_VALIDATION_FAILED, etc.)
|
|
32
|
+
- Export processing codes (EXPORT_STARTED, EXPORT_COMPLETED, etc.)
|
|
33
|
+
- File processing codes (ORIGINAL_FILE_EXPORTED, DATA_FILE_EXPORTED, etc.)
|
|
34
|
+
- Error handling codes (TARGET_HANDLER_ERROR, EXPORT_FAILED, etc.)
|
|
35
|
+
|
|
36
|
+
Each code maps to a configuration in LOG_MESSAGES with message template
|
|
37
|
+
and appropriate log level.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
STORAGE_VALIDATION_FAILED = 'STORAGE_VALIDATION_FAILED'
|
|
41
|
+
FILTER_VALIDATION_FAILED = 'FILTER_VALIDATION_FAILED'
|
|
42
|
+
TARGET_VALIDATION_FAILED = 'TARGET_VALIDATION_FAILED'
|
|
43
|
+
VALIDATION_FAILED = 'VALIDATION_FAILED'
|
|
44
|
+
EXPORT_STARTED = 'EXPORT_STARTED'
|
|
45
|
+
EXPORT_COMPLETED = 'EXPORT_COMPLETED'
|
|
46
|
+
EXPORT_FAILED = 'EXPORT_FAILED'
|
|
47
|
+
NO_RESULTS_FOUND = 'NO_RESULTS_FOUND'
|
|
48
|
+
RESULTS_RETRIEVED = 'RESULTS_RETRIEVED'
|
|
49
|
+
ORIGINAL_FILE_EXPORTED = 'ORIGINAL_FILE_EXPORTED'
|
|
50
|
+
DATA_FILE_EXPORTED = 'DATA_FILE_EXPORTED'
|
|
51
|
+
FILE_EXPORT_FAILED = 'FILE_EXPORT_FAILED'
|
|
52
|
+
TARGET_HANDLER_ERROR = 'TARGET_HANDLER_ERROR'
|
|
53
|
+
NULL_DATA_DETECTED = 'NULL_DATA_DETECTED'
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
LOG_MESSAGES = {
|
|
57
|
+
LogCode.STORAGE_VALIDATION_FAILED: {
|
|
58
|
+
'message': 'Storage validation failed.',
|
|
59
|
+
'level': Context.DANGER,
|
|
60
|
+
},
|
|
61
|
+
LogCode.FILTER_VALIDATION_FAILED: {
|
|
62
|
+
'message': 'Filter validation failed.',
|
|
63
|
+
'level': Context.DANGER,
|
|
64
|
+
},
|
|
65
|
+
LogCode.TARGET_VALIDATION_FAILED: {
|
|
66
|
+
'message': 'Target validation failed.',
|
|
67
|
+
'level': Context.DANGER,
|
|
68
|
+
},
|
|
69
|
+
LogCode.VALIDATION_FAILED: {
|
|
70
|
+
'message': 'Validation failed.',
|
|
71
|
+
'level': Context.DANGER,
|
|
72
|
+
},
|
|
73
|
+
LogCode.EXPORT_STARTED: {
|
|
74
|
+
'message': 'Export process started.',
|
|
75
|
+
'level': None,
|
|
76
|
+
},
|
|
77
|
+
LogCode.EXPORT_COMPLETED: {
|
|
78
|
+
'message': 'Export process completed.',
|
|
79
|
+
'level': None,
|
|
80
|
+
},
|
|
81
|
+
LogCode.EXPORT_FAILED: {
|
|
82
|
+
'message': 'Export process failed: {}',
|
|
83
|
+
'level': Context.DANGER,
|
|
84
|
+
},
|
|
85
|
+
LogCode.NO_RESULTS_FOUND: {
|
|
86
|
+
'message': 'No results found for export.',
|
|
87
|
+
'level': Context.WARNING,
|
|
88
|
+
},
|
|
89
|
+
LogCode.RESULTS_RETRIEVED: {
|
|
90
|
+
'message': 'Retrieved {} results for export',
|
|
91
|
+
'level': None,
|
|
92
|
+
},
|
|
93
|
+
LogCode.ORIGINAL_FILE_EXPORTED: {
|
|
94
|
+
'message': 'Original file exported successfully.',
|
|
95
|
+
'level': None,
|
|
96
|
+
},
|
|
97
|
+
LogCode.DATA_FILE_EXPORTED: {
|
|
98
|
+
'message': 'Data file exported successfully.',
|
|
99
|
+
'level': None,
|
|
100
|
+
},
|
|
101
|
+
LogCode.FILE_EXPORT_FAILED: {
|
|
102
|
+
'message': 'Failed to export file: {}',
|
|
103
|
+
'level': Context.DANGER,
|
|
104
|
+
},
|
|
105
|
+
LogCode.TARGET_HANDLER_ERROR: {
|
|
106
|
+
'message': 'Target handler error: {}',
|
|
107
|
+
'level': Context.DANGER,
|
|
108
|
+
},
|
|
109
|
+
LogCode.NULL_DATA_DETECTED: {
|
|
110
|
+
'message': 'Data is null for export item',
|
|
111
|
+
'level': Context.WARNING,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
class ExportError(Exception):
|
|
2
|
+
"""Base exception for export-related errors.
|
|
3
|
+
|
|
4
|
+
This exception is raised when an export operation encounters errors
|
|
5
|
+
that prevent successful completion. It serves as the base class for
|
|
6
|
+
more specific export-related exceptions.
|
|
7
|
+
|
|
8
|
+
Used during export processing to handle various error conditions
|
|
9
|
+
such as validation failures, data access errors, or processing issues.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> if not validate_export_data(data):
|
|
13
|
+
... raise ExportError("Export data validation failed")
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExportValidationError(ExportError):
|
|
20
|
+
"""Exception raised when export parameter validation fails.
|
|
21
|
+
|
|
22
|
+
This exception is raised when export parameters or configuration
|
|
23
|
+
fail validation checks, preventing the export operation from starting.
|
|
24
|
+
|
|
25
|
+
Used during parameter validation to distinguish validation errors
|
|
26
|
+
from other types of export failures.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> if not storage_exists(storage_id):
|
|
30
|
+
... raise ExportValidationError(f"Storage {storage_id} does not exist")
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ExportTargetError(ExportError):
|
|
37
|
+
"""Exception raised when export target handling encounters errors.
|
|
38
|
+
|
|
39
|
+
This exception is raised when target-specific operations (assignment,
|
|
40
|
+
ground_truth, task) fail due to data access issues, filter problems,
|
|
41
|
+
or target-specific validation failures.
|
|
42
|
+
|
|
43
|
+
Used during target data retrieval and processing to handle target-specific
|
|
44
|
+
errors separately from general export errors.
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
>>> try:
|
|
48
|
+
... results = client.list_assignments(params=filters)
|
|
49
|
+
... except ClientError as e:
|
|
50
|
+
... raise ExportTargetError(f"Failed to retrieve assignments: {e}")
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
pass
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Annotated, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import AfterValidator, BaseModel, field_validator
|
|
4
|
+
from pydantic_core import PydanticCustomError
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.clients.exceptions import ClientError
|
|
7
|
+
from synapse_sdk.i18n import gettext as _
|
|
8
|
+
from synapse_sdk.utils.pydantic.validators import non_blank
|
|
9
|
+
|
|
10
|
+
from .utils import TargetHandlerFactory
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExportParams(BaseModel):
|
|
14
|
+
"""Export action parameter validation model.
|
|
15
|
+
|
|
16
|
+
Defines and validates all parameters required for export operations.
|
|
17
|
+
Uses Pydantic for type validation and custom validators to ensure
|
|
18
|
+
storage and filter resources exist before processing.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
name (str): Human-readable name for the export operation
|
|
22
|
+
description (str | None): Optional description of the export
|
|
23
|
+
storage (int): Storage ID where exported data will be saved
|
|
24
|
+
save_original_file (bool): Whether to save the original file
|
|
25
|
+
path (str): File system path where exported data will be saved
|
|
26
|
+
target (str): The target source to export data from (assignment, ground_truth, task)
|
|
27
|
+
filter (dict): Filter criteria to apply when retrieving data
|
|
28
|
+
extra_params (dict | None): Additional parameters for export customization.
|
|
29
|
+
Example: {"include_metadata": True, "compression": "gzip"}
|
|
30
|
+
|
|
31
|
+
Validation:
|
|
32
|
+
- name: Must be non-blank after validation
|
|
33
|
+
- storage: Must exist and be accessible via client API
|
|
34
|
+
- target: Must be one of the supported target types
|
|
35
|
+
- filter: Must be valid for the specified target type
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> params = ExportParams(
|
|
39
|
+
... name="Assignment Export",
|
|
40
|
+
... storage=1,
|
|
41
|
+
... path="/exports/assignments",
|
|
42
|
+
... target="assignment",
|
|
43
|
+
... filter={"project": 123}
|
|
44
|
+
... )
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
name: Annotated[str, AfterValidator(non_blank)]
|
|
48
|
+
description: str | None = None
|
|
49
|
+
storage: int
|
|
50
|
+
save_original_file: bool = True
|
|
51
|
+
path: str
|
|
52
|
+
target: Literal['assignment', 'ground_truth', 'task']
|
|
53
|
+
filter: dict
|
|
54
|
+
extra_params: dict | None = None
|
|
55
|
+
|
|
56
|
+
@field_validator('storage')
|
|
57
|
+
@staticmethod
|
|
58
|
+
def check_storage_exists(value, info):
|
|
59
|
+
action = info.context['action']
|
|
60
|
+
client = action.client
|
|
61
|
+
try:
|
|
62
|
+
client.get_storage(value)
|
|
63
|
+
except ClientError:
|
|
64
|
+
raise PydanticCustomError('client_error', _('Unable to get storage from Synapse backend.'))
|
|
65
|
+
return value
|
|
66
|
+
|
|
67
|
+
@field_validator('filter')
|
|
68
|
+
@staticmethod
|
|
69
|
+
def check_filter_by_target(value, info):
|
|
70
|
+
action = info.context['action']
|
|
71
|
+
client = action.client
|
|
72
|
+
target = action.params['target']
|
|
73
|
+
handler = TargetHandlerFactory.get_handler(target)
|
|
74
|
+
return handler.validate_filter(value, client)
|