synapse-sdk 2025.9.1__py3-none-any.whl → 2025.9.4__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.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
- synapse_sdk/devtools/docs/docs/api/clients/backend.md +368 -1
- synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
- synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
- synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
- synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
- synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
- synapse_sdk/devtools/docs/docs/plugins/developing-upload-template.md +1463 -0
- synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +161 -34
- synapse_sdk/devtools/docs/docs/plugins/upload-plugins.md +1497 -213
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +378 -11
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/developing-upload-template.md +1463 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +161 -34
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/upload-plugins.md +1752 -572
- synapse_sdk/devtools/docs/sidebars.ts +7 -0
- synapse_sdk/plugins/README.md +1 -2
- synapse_sdk/plugins/categories/base.py +7 -0
- 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 +160 -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/plugin/__init__.py +1 -1
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +1 -2
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +154 -531
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +143 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +66 -29
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +182 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +106 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +80 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +66 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +101 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +89 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +96 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +61 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +86 -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 +34 -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 +233 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +253 -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/async_upload.py +109 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +43 -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 +45 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +194 -83
- synapse_sdk/plugins/categories/upload/templates/config.yaml +4 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +269 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +71 -27
- synapse_sdk/plugins/models.py +7 -0
- synapse_sdk/shared/__init__.py +21 -0
- {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/METADATA +2 -1
- {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/RECORD +79 -28
- synapse_sdk/plugins/categories/export/actions/export.py +0 -385
- synapse_sdk/plugins/categories/export/enums.py +0 -7
- {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/WHEEL +0 -0
- {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/top_level.txt +0 -0
|
@@ -53,7 +53,14 @@ const sidebars: SidebarsConfig = {
|
|
|
53
53
|
type: 'category',
|
|
54
54
|
label: 'Clients',
|
|
55
55
|
items: [
|
|
56
|
+
'api/clients/index',
|
|
56
57
|
'api/clients/backend',
|
|
58
|
+
'api/clients/annotation-mixin',
|
|
59
|
+
'api/clients/core-mixin',
|
|
60
|
+
'api/clients/data-collection-mixin',
|
|
61
|
+
'api/clients/hitl-mixin',
|
|
62
|
+
'api/clients/integration-mixin',
|
|
63
|
+
'api/clients/ml-mixin',
|
|
57
64
|
'api/clients/agent',
|
|
58
65
|
'api/clients/ray',
|
|
59
66
|
'api/clients/base',
|
synapse_sdk/plugins/README.md
CHANGED
|
@@ -329,7 +329,7 @@ from .enums import LogCode, LOG_MESSAGES, UploadStatus
|
|
|
329
329
|
from .exceptions import ExcelParsingError, ExcelSecurityError
|
|
330
330
|
from .models import UploadParams
|
|
331
331
|
from .run import UploadRun
|
|
332
|
-
from .utils import
|
|
332
|
+
from .utils import ExcelSecurityConfig, PathAwareJSONEncoder
|
|
333
333
|
|
|
334
334
|
__all__ = [
|
|
335
335
|
'UploadAction',
|
|
@@ -342,7 +342,6 @@ __all__ = [
|
|
|
342
342
|
'ExcelParsingError',
|
|
343
343
|
'PathAwareJSONEncoder',
|
|
344
344
|
'ExcelSecurityConfig',
|
|
345
|
-
'ExcelMetadataUtils',
|
|
346
345
|
]
|
|
347
346
|
```
|
|
348
347
|
|
|
@@ -12,6 +12,7 @@ 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
14
|
from synapse_sdk.plugins.upload import archive_and_upload, build_and_upload, download_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
|
|
|
@@ -181,6 +182,10 @@ class Action:
|
|
|
181
182
|
for key, value in self.package_manager_options.items():
|
|
182
183
|
runtime_env[self.plugin_package_manager][key] = value
|
|
183
184
|
|
|
185
|
+
# Sentry init if SENTRY_DSN is set
|
|
186
|
+
if needs_sentry_init():
|
|
187
|
+
runtime_env['worker_process_setup_hook'] = init_sentry
|
|
188
|
+
|
|
184
189
|
# 맨 마지막에 진행되어야 함
|
|
185
190
|
runtime_env['env_vars'] = self.envs
|
|
186
191
|
|
|
@@ -311,5 +316,7 @@ class Action:
|
|
|
311
316
|
def ray_init(self):
|
|
312
317
|
import ray
|
|
313
318
|
|
|
319
|
+
init_sentry()
|
|
320
|
+
|
|
314
321
|
if not ray.is_initialized():
|
|
315
322
|
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,160 @@
|
|
|
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 Ground Truth 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
|
+
filters = {'expand': 'data', **self.params['filter']}
|
|
120
|
+
target = self.params['target']
|
|
121
|
+
handler = TargetHandlerFactory.get_handler(target)
|
|
122
|
+
|
|
123
|
+
self.params['results'], self.params['count'] = self.get_filtered_results(filters, handler)
|
|
124
|
+
|
|
125
|
+
if self.params['count'] == 0:
|
|
126
|
+
self.run.log_message_with_code(LogCode.NO_RESULTS_FOUND)
|
|
127
|
+
else:
|
|
128
|
+
self.run.log_message_with_code(LogCode.RESULTS_RETRIEVED, self.params['count'])
|
|
129
|
+
|
|
130
|
+
# For the 'ground_truth' target, retrieve project information from the first result and add configuration
|
|
131
|
+
if target == 'ground_truth':
|
|
132
|
+
try:
|
|
133
|
+
# Split generator into two using tee()
|
|
134
|
+
peek_iter, main_iter = tee(self.params['results'])
|
|
135
|
+
first_result = next(peek_iter) # Peek first value only
|
|
136
|
+
project_pk = first_result['project']
|
|
137
|
+
project_info = self.client.get_project(project_pk)
|
|
138
|
+
self.params['project_id'] = project_pk
|
|
139
|
+
self.params['configuration'] = project_info.get('configuration', {})
|
|
140
|
+
self.params['results'] = main_iter # Keep original generator intact
|
|
141
|
+
except (StopIteration, KeyError):
|
|
142
|
+
self.params['configuration'] = {}
|
|
143
|
+
# For the 'assignment' and 'task' targets, retrieve the project from the filter as before
|
|
144
|
+
elif target in ['assignment', 'task'] and 'project' in self.params['filter']:
|
|
145
|
+
project_pk = self.params['filter']['project']
|
|
146
|
+
project_info = self.client.get_project(project_pk)
|
|
147
|
+
self.params['configuration'] = project_info.get('configuration', {})
|
|
148
|
+
|
|
149
|
+
export_items = handler.get_export_item(self.params['results'])
|
|
150
|
+
storage = self.client.get_storage(self.params['storage'])
|
|
151
|
+
pathlib_cwd = get_pathlib(storage, self.params['path'])
|
|
152
|
+
exporter = self.entrypoint(self.run, export_items, pathlib_cwd, **self.params)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
result = exporter.export()
|
|
156
|
+
self.run.log_message_with_code(LogCode.EXPORT_COMPLETED)
|
|
157
|
+
return result
|
|
158
|
+
except Exception as e:
|
|
159
|
+
self.run.log_message_with_code(LogCode.EXPORT_FAILED, str(e))
|
|
160
|
+
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)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from synapse_sdk.plugins.models import Run
|
|
8
|
+
from synapse_sdk.shared.enums import Context
|
|
9
|
+
|
|
10
|
+
from .enums import LOG_MESSAGES, ExportStatus, LogCode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExportRun(Run):
|
|
14
|
+
"""Export-specific run management class.
|
|
15
|
+
|
|
16
|
+
Extends the base Run class with export-specific logging capabilities
|
|
17
|
+
and event tracking. Provides type-safe logging using LogCode enums
|
|
18
|
+
and specialized methods for tracking export progress.
|
|
19
|
+
|
|
20
|
+
Manages logging for export events, data files, and export targets
|
|
21
|
+
throughout the export lifecycle. Each log entry includes status,
|
|
22
|
+
timestamps, and relevant metadata.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
Inherits all attributes from base Run class plus export-specific
|
|
26
|
+
logging methods and nested model classes for structured logging.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> run = ExportRun(job_id, context)
|
|
30
|
+
>>> run.log_message_with_code(LogCode.EXPORT_STARTED)
|
|
31
|
+
>>> run.log_export_event(LogCode.RESULTS_RETRIEVED, target_id, count)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
class ExportEventLog(BaseModel):
|
|
35
|
+
"""Model for export event log entries.
|
|
36
|
+
|
|
37
|
+
Records significant events during export processing with
|
|
38
|
+
target identification and status information.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
target_id (int): The ID of the export target
|
|
42
|
+
info (str | None): Optional additional information
|
|
43
|
+
status (Context): Event status/severity level
|
|
44
|
+
created (str): Timestamp when event occurred
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
target_id: int
|
|
48
|
+
info: str | None = None
|
|
49
|
+
status: Context
|
|
50
|
+
created: str
|
|
51
|
+
|
|
52
|
+
class DataFileLog(BaseModel):
|
|
53
|
+
"""Model for data file export log entries.
|
|
54
|
+
|
|
55
|
+
Tracks the export status of individual data files during
|
|
56
|
+
export operations.
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
target_id (int): The ID of the target being exported
|
|
60
|
+
data_file_info (str | None): JSON information about the data file
|
|
61
|
+
status (ExportStatus): Export status (SUCCESS/FAILED/STAND_BY)
|
|
62
|
+
error (str | None): Error message if export failed
|
|
63
|
+
created (str): Timestamp when log entry was created
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
target_id: int
|
|
67
|
+
data_file_info: str | None
|
|
68
|
+
status: ExportStatus
|
|
69
|
+
error: str | None = None
|
|
70
|
+
created: str
|
|
71
|
+
|
|
72
|
+
class MetricsRecord(BaseModel):
|
|
73
|
+
"""Model for export metrics tracking.
|
|
74
|
+
|
|
75
|
+
Records count-based metrics for monitoring export
|
|
76
|
+
progress and success rates.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
stand_by (int): Number of items waiting to be processed
|
|
80
|
+
failed (int): Number of items that failed processing
|
|
81
|
+
success (int): Number of items successfully processed
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
stand_by: int
|
|
85
|
+
failed: int
|
|
86
|
+
success: int
|
|
87
|
+
|
|
88
|
+
def log_message_with_code(self, code: LogCode, *args, level: Optional[Context] = None):
|
|
89
|
+
"""Log message using predefined code with type safety.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
code (LogCode): The log message code
|
|
93
|
+
*args: Arguments to format the message
|
|
94
|
+
level (Context | None): Optional context level override
|
|
95
|
+
"""
|
|
96
|
+
if code not in LOG_MESSAGES:
|
|
97
|
+
self.log_message(f'Unknown log code: {code}')
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
log_config = LOG_MESSAGES[code]
|
|
101
|
+
message = log_config['message'].format(*args) if args else log_config['message']
|
|
102
|
+
log_level = level or log_config['level'] or Context.INFO
|
|
103
|
+
|
|
104
|
+
# Always call log_message for basic logging
|
|
105
|
+
if log_level:
|
|
106
|
+
self.log_message(message, context=log_level.value)
|
|
107
|
+
else:
|
|
108
|
+
self.log_message(message)
|
|
109
|
+
|
|
110
|
+
def log_file(
|
|
111
|
+
self, log_type: str, target_id: int, data_file_info: dict, status: ExportStatus, error: str | None = None
|
|
112
|
+
):
|
|
113
|
+
"""Log export file information.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
log_type (str): The type of log ('export_data_file' or 'export_original_file').
|
|
117
|
+
target_id (int): The ID of the data file.
|
|
118
|
+
data_file_info (dict): The JSON info of the data file.
|
|
119
|
+
status (ExportStatus): The status of the data file.
|
|
120
|
+
error (str | None): The error message, if any.
|
|
121
|
+
"""
|
|
122
|
+
now = datetime.now().isoformat()
|
|
123
|
+
self.log(
|
|
124
|
+
log_type,
|
|
125
|
+
self.DataFileLog(
|
|
126
|
+
target_id=target_id,
|
|
127
|
+
data_file_info=json.dumps(data_file_info),
|
|
128
|
+
status=status.value,
|
|
129
|
+
error=error,
|
|
130
|
+
created=now,
|
|
131
|
+
).model_dump(),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def log_export_event(self, code: LogCode, target_id: int, *args, level: Context | None = None):
|
|
135
|
+
"""Log export event using predefined code.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
code (str): The log message code.
|
|
139
|
+
target_id (int): The ID of the export target.
|
|
140
|
+
*args: Arguments to format the message.
|
|
141
|
+
level (Context | None): Optional context level override.
|
|
142
|
+
"""
|
|
143
|
+
# Call log_message_with_code to handle the basic logging
|
|
144
|
+
self.log_message_with_code(code, *args, level=level)
|
|
145
|
+
|
|
146
|
+
# Also log the event for export-specific tracking
|
|
147
|
+
if code not in LOG_MESSAGES:
|
|
148
|
+
now = datetime.now().isoformat()
|
|
149
|
+
self.log(
|
|
150
|
+
'export_event',
|
|
151
|
+
self.ExportEventLog(
|
|
152
|
+
target_id=target_id, info=f'Unknown log code: {code}', status=Context.DANGER, created=now
|
|
153
|
+
).model_dump(),
|
|
154
|
+
)
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
log_config = LOG_MESSAGES[code]
|
|
158
|
+
message = log_config['message'].format(*args) if args else log_config['message']
|
|
159
|
+
log_level = level or log_config['level'] or Context.INFO
|
|
160
|
+
|
|
161
|
+
now = datetime.now().isoformat()
|
|
162
|
+
self.log(
|
|
163
|
+
'export_event',
|
|
164
|
+
self.ExportEventLog(info=message, status=log_level, target_id=target_id, created=now).model_dump(),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def log_metrics(self, record: MetricsRecord, category: str):
|
|
168
|
+
"""Log export metrics.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
record (MetricsRecord): The metrics record to log.
|
|
172
|
+
category (str): The category of the metrics.
|
|
173
|
+
"""
|
|
174
|
+
record = self.MetricsRecord.model_validate(record)
|
|
175
|
+
self.set_metrics(value=record.model_dump(), category=category)
|
|
176
|
+
|
|
177
|
+
def export_log_json_file(
|
|
178
|
+
self,
|
|
179
|
+
target_id: int,
|
|
180
|
+
data_file_info: dict,
|
|
181
|
+
status: ExportStatus = ExportStatus.STAND_BY,
|
|
182
|
+
error: str | None = None,
|
|
183
|
+
):
|
|
184
|
+
"""Log export json data file."""
|
|
185
|
+
self.log_file('export_data_file', target_id, data_file_info, status, error)
|
|
186
|
+
|
|
187
|
+
def export_log_original_file(
|
|
188
|
+
self,
|
|
189
|
+
target_id: int,
|
|
190
|
+
data_file_info: dict,
|
|
191
|
+
status: ExportStatus = ExportStatus.STAND_BY,
|
|
192
|
+
error: str | None = None,
|
|
193
|
+
):
|
|
194
|
+
"""Log export origin data file."""
|
|
195
|
+
self.log_file('export_original_file', target_id, data_file_info, status, error)
|