synapse-sdk 1.0.0a35__py3-none-any.whl → 2025.11.7__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/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +308 -5
- synapse_sdk/cli/alias/utils.py +1 -1
- synapse_sdk/cli/code_server.py +687 -0
- synapse_sdk/cli/config.py +440 -0
- synapse_sdk/cli/devtools.py +90 -0
- synapse_sdk/cli/plugin/publish.py +23 -15
- synapse_sdk/clients/agent/__init__.py +9 -3
- synapse_sdk/clients/agent/container.py +133 -0
- synapse_sdk/clients/agent/core.py +19 -0
- synapse_sdk/clients/agent/ray.py +298 -9
- synapse_sdk/clients/backend/__init__.py +28 -12
- synapse_sdk/clients/backend/annotation.py +9 -1
- synapse_sdk/clients/backend/core.py +31 -4
- synapse_sdk/clients/backend/data_collection.py +186 -0
- synapse_sdk/clients/backend/hitl.py +1 -1
- synapse_sdk/clients/backend/integration.py +4 -3
- synapse_sdk/clients/backend/ml.py +1 -1
- synapse_sdk/clients/backend/models.py +35 -1
- synapse_sdk/clients/base.py +309 -36
- synapse_sdk/clients/ray/serve.py +2 -0
- synapse_sdk/devtools/__init__.py +0 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/docs/.gitignore +20 -0
- synapse_sdk/devtools/docs/README.md +41 -0
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- 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 +29 -0
- synapse_sdk/devtools/docs/blog/authors.yml +25 -0
- synapse_sdk/devtools/docs/blog/tags.yml +19 -0
- synapse_sdk/devtools/docs/docs/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
- synapse_sdk/devtools/docs/docs/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/docs/api/clients/base.md +257 -0
- 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/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/docs/api/index.md +52 -0
- synapse_sdk/devtools/docs/docs/api/plugins/categories.md +43 -0
- synapse_sdk/devtools/docs/docs/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/docs/api/plugins/utils.md +328 -0
- synapse_sdk/devtools/docs/docs/categories.md +0 -0
- synapse_sdk/devtools/docs/docs/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/docs/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/docs/configuration.md +83 -0
- synapse_sdk/devtools/docs/docs/contributing.md +306 -0
- synapse_sdk/devtools/docs/docs/examples/index.md +29 -0
- synapse_sdk/devtools/docs/docs/faq.md +179 -0
- synapse_sdk/devtools/docs/docs/features/converters/index.md +455 -0
- synapse_sdk/devtools/docs/docs/features/index.md +24 -0
- synapse_sdk/devtools/docs/docs/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/docs/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/docs/features/utils/storage.md +57 -0
- synapse_sdk/devtools/docs/docs/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/docs/installation.md +94 -0
- synapse_sdk/devtools/docs/docs/introduction.md +47 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +814 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/docs/plugins/plugins.md +852 -0
- synapse_sdk/devtools/docs/docs/quickstart.md +78 -0
- synapse_sdk/devtools/docs/docs/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/_category_.json +8 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/congratulations.md +23 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-document.md +57 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-page.md +43 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/_category_.json +7 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/translate-your-site.md +88 -0
- synapse_sdk/devtools/docs/docusaurus.config.ts +148 -0
- synapse_sdk/devtools/docs/i18n/ko/code.json +325 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/agent.md +43 -0
- 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 +420 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +257 -0
- 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/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/index.md +52 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/categories.md +0 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/configuration.md +83 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/contributing.md +306 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/index.md +29 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +179 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/converters/index.md +30 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/index.md +24 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/storage.md +60 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/installation.md +94 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/introduction.md +47 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +815 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/plugins.md +117 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/quickstart.md +78 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +34 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/footer.json +42 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/navbar.json +18 -0
- synapse_sdk/devtools/docs/package-lock.json +18784 -0
- synapse_sdk/devtools/docs/package.json +48 -0
- synapse_sdk/devtools/docs/sidebars.ts +122 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- synapse_sdk/devtools/docs/src/css/custom.css +30 -0
- synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
- synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
- 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 +171 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- synapse_sdk/devtools/docs/tsconfig.json +8 -0
- synapse_sdk/devtools/server.py +41 -0
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/loggers.py +65 -7
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/categories/base.py +73 -11
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
- 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.py → export/utils.py} +47 -82
- 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 -129
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +9 -62
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1062 -32
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +27 -5
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- 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 +145 -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 +97 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -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 +287 -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 +87 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +966 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +232 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +471 -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 +203 -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 +84 -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 +203 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +97 -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 +258 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +281 -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 +29 -2
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +294 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +88 -30
- synapse_sdk/plugins/models.py +122 -16
- synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
- synapse_sdk/plugins/templates/schema.json +491 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
- synapse_sdk/plugins/utils/__init__.py +46 -0
- synapse_sdk/plugins/utils/actions.py +119 -0
- synapse_sdk/plugins/utils/config.py +203 -0
- synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/plugins/utils/registry.py +58 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/shared/enums.py +93 -0
- synapse_sdk/utils/converters/__init__.py +240 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +322 -0
- synapse_sdk/utils/converters/coco/to_dm.py +215 -0
- synapse_sdk/utils/converters/dm/__init__.py +56 -0
- synapse_sdk/utils/converters/dm/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm/to_v1.py +367 -0
- synapse_sdk/utils/converters/pascal/__init__.py +0 -0
- synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
- synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
- synapse_sdk/utils/converters/yolo/__init__.py +0 -0
- synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
- synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
- synapse_sdk/utils/dataset.py +46 -0
- synapse_sdk/utils/encryption.py +158 -0
- synapse_sdk/utils/file/__init__.py +39 -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/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/{file.py → file.py.backup} +84 -2
- synapse_sdk/utils/http.py +138 -0
- synapse_sdk/utils/network.py +293 -0
- synapse_sdk/utils/storage/__init__.py +36 -2
- synapse_sdk/utils/storage/providers/__init__.py +141 -0
- synapse_sdk/utils/storage/providers/file_system.py +134 -0
- synapse_sdk/utils/storage/providers/http.py +190 -0
- synapse_sdk/utils/storage/providers/s3.py +54 -6
- synapse_sdk/utils/storage/providers/sftp.py +31 -0
- synapse_sdk/utils/storage/registry.py +6 -0
- synapse_sdk-2025.11.7.dist-info/METADATA +122 -0
- synapse_sdk-2025.11.7.dist-info/RECORD +386 -0
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/WHEEL +1 -1
- synapse_sdk/clients/backend/dataset.py +0 -102
- synapse_sdk/plugins/categories/upload/actions/upload.py +0 -293
- synapse_sdk-1.0.0a35.dist-info/METADATA +0 -47
- synapse_sdk-1.0.0a35.dist-info/RECORD +0 -137
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1092 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: export-plugins
|
|
3
|
+
title: Export Plugins
|
|
4
|
+
sidebar_position: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Export Plugins
|
|
8
|
+
|
|
9
|
+
Export plugins provide data export and transformation operations for exporting annotated data, ground truth datasets, assignments, and tasks from the Synapse platform.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
**Available Actions:**
|
|
14
|
+
|
|
15
|
+
- `export` - Export data from various sources (assignments, ground truth, tasks) with customizable processing
|
|
16
|
+
|
|
17
|
+
**Use Cases:**
|
|
18
|
+
|
|
19
|
+
- Exporting annotated datasets for training
|
|
20
|
+
- Converting ground truth data to custom formats
|
|
21
|
+
- Creating data packages for distribution
|
|
22
|
+
- Batch processing of assignment results
|
|
23
|
+
- Transforming annotation data for external tools
|
|
24
|
+
|
|
25
|
+
**Supported Export Targets:**
|
|
26
|
+
|
|
27
|
+
- `assignment` - Export assignment data with annotations
|
|
28
|
+
- `ground_truth` - Export ground truth dataset versions
|
|
29
|
+
- `task` - Export task data with associated annotations
|
|
30
|
+
|
|
31
|
+
## BaseExporter and Exporter Class Architecture
|
|
32
|
+
|
|
33
|
+
The following diagrams illustrate the relationship between BaseExporter and Exporter classes and their method implementations:
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
classDiagram
|
|
37
|
+
%% Light/Dark mode compatible colors using CSS variables
|
|
38
|
+
classDef baseClass fill:#f0f8ff,stroke:#4169e1,stroke-width:2px,color:#000
|
|
39
|
+
classDef childClass fill:#f0fff0,stroke:#228b22,stroke-width:2px,color:#000
|
|
40
|
+
classDef method fill:#fff8dc,stroke:#daa520,stroke-width:1px,color:#000
|
|
41
|
+
classDef abstractMethod fill:#ffe4e1,stroke:#dc143c,stroke-width:1px,color:#000
|
|
42
|
+
classDef helperMethod fill:#f5f5f5,stroke:#696969,stroke-width:1px,color:#000
|
|
43
|
+
|
|
44
|
+
class BaseExporter {
|
|
45
|
+
%% Core attributes
|
|
46
|
+
+run: object
|
|
47
|
+
+export_items: Generator
|
|
48
|
+
+path_root: Path
|
|
49
|
+
+params: dict
|
|
50
|
+
|
|
51
|
+
%% Main workflow methods
|
|
52
|
+
+export(export_items, results, **kwargs) dict
|
|
53
|
+
+process_data_conversion(export_item) object
|
|
54
|
+
+process_file_saving(...) bool
|
|
55
|
+
+setup_output_directories(path, flag) dict
|
|
56
|
+
|
|
57
|
+
%% Abstract methods (to be implemented)
|
|
58
|
+
+convert_data(data)* object
|
|
59
|
+
+before_convert(data)* object
|
|
60
|
+
+after_convert(data)* object
|
|
61
|
+
|
|
62
|
+
%% File operations
|
|
63
|
+
+save_original_file(result, path, errors) ExportStatus
|
|
64
|
+
+save_as_json(result, path, errors) ExportStatus
|
|
65
|
+
+get_original_file_name(files) str
|
|
66
|
+
|
|
67
|
+
%% Helper methods
|
|
68
|
+
-_create_unique_export_path(name) Path
|
|
69
|
+
-_save_error_list(path, json_errors, file_errors)
|
|
70
|
+
-_process_original_file_saving(...) bool
|
|
71
|
+
-_process_json_file_saving(...) bool
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class Exporter {
|
|
75
|
+
%% Inherited from BaseExporter
|
|
76
|
+
+export(export_items, results, **kwargs) dict
|
|
77
|
+
|
|
78
|
+
%% Implemented abstract methods
|
|
79
|
+
+convert_data(data) object
|
|
80
|
+
+before_convert(data) object
|
|
81
|
+
+after_convert(data) object
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
%% Inheritance relationship
|
|
85
|
+
BaseExporter <|-- Exporter
|
|
86
|
+
|
|
87
|
+
%% Apply styles
|
|
88
|
+
class BaseExporter baseClass
|
|
89
|
+
class Exporter childClass
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Method Execution Flow
|
|
93
|
+
|
|
94
|
+
This flowchart shows the complete execution flow of export operations:
|
|
95
|
+
|
|
96
|
+
```mermaid
|
|
97
|
+
flowchart TD
|
|
98
|
+
%% Start
|
|
99
|
+
A[export method called] --> B[Initialize paths and metrics]
|
|
100
|
+
B --> C[Setup output directories]
|
|
101
|
+
C --> D[Loop through export_items]
|
|
102
|
+
|
|
103
|
+
%% Data processing pipeline
|
|
104
|
+
D --> E[process_data_conversion]
|
|
105
|
+
E --> F[before_convert]
|
|
106
|
+
F --> G[convert_data]
|
|
107
|
+
G --> H[after_convert]
|
|
108
|
+
|
|
109
|
+
%% File saving pipeline
|
|
110
|
+
H --> I[process_file_saving]
|
|
111
|
+
I --> J{save_original_file?}
|
|
112
|
+
J -->|Yes| K[_process_original_file_saving]
|
|
113
|
+
J -->|No| L[_process_json_file_saving]
|
|
114
|
+
K --> L
|
|
115
|
+
|
|
116
|
+
%% Continue or finish
|
|
117
|
+
L --> M{More items?}
|
|
118
|
+
M -->|Yes| D
|
|
119
|
+
M -->|No| N[Save error lists]
|
|
120
|
+
N --> O[Return export path]
|
|
121
|
+
|
|
122
|
+
%% Error handling
|
|
123
|
+
K --> P{Original file failed?}
|
|
124
|
+
P -->|Yes| Q[Skip to next item]
|
|
125
|
+
P -->|No| L
|
|
126
|
+
|
|
127
|
+
L --> R{JSON file failed?}
|
|
128
|
+
R -->|Yes| Q
|
|
129
|
+
R -->|No| S[Update metrics]
|
|
130
|
+
S --> M
|
|
131
|
+
Q --> M
|
|
132
|
+
|
|
133
|
+
%% Styling for light/dark compatibility
|
|
134
|
+
classDef startEnd fill:#e1f5fe,stroke:#01579b,color:#000
|
|
135
|
+
classDef process fill:#f3e5f5,stroke:#4a148c,color:#000
|
|
136
|
+
classDef decision fill:#fff3e0,stroke:#e65100,color:#000
|
|
137
|
+
classDef data fill:#e8f5e8,stroke:#2e7d32,color:#000
|
|
138
|
+
classDef error fill:#ffebee,stroke:#c62828,color:#000
|
|
139
|
+
|
|
140
|
+
class A,O startEnd
|
|
141
|
+
class B,C,E,F,G,H,I,K,L,N,S process
|
|
142
|
+
class J,M,P,R decision
|
|
143
|
+
class D data
|
|
144
|
+
class Q error
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Key Relationships and Responsibilities
|
|
148
|
+
|
|
149
|
+
**BaseExporter (Abstract Base Class)**
|
|
150
|
+
|
|
151
|
+
- **Core Functionality**: Provides complete export workflow infrastructure
|
|
152
|
+
- **Template Methods**: `export()` method orchestrates the entire process
|
|
153
|
+
- **Hook Methods**: `convert_data()`, `before_convert()`, `after_convert()` for customization
|
|
154
|
+
- **Utilities**: File operations, directory setup, error handling, progress tracking
|
|
155
|
+
|
|
156
|
+
**Exporter (Concrete Implementation)**
|
|
157
|
+
|
|
158
|
+
- **Inheritance**: Extends `BaseExporter`
|
|
159
|
+
- **Minimal Implementation**: Provides basic implementations of abstract methods
|
|
160
|
+
- **Pass-through Behavior**: Most methods delegate to parent class
|
|
161
|
+
- **Customization Points**: Override conversion methods for specific logic
|
|
162
|
+
|
|
163
|
+
### Method Categories
|
|
164
|
+
|
|
165
|
+
- **🔵 Core Workflow**: Main export orchestration methods
|
|
166
|
+
- **🟢 Template/Hook**: Methods designed to be overridden by subclasses
|
|
167
|
+
- **🟡 File Operations**: Concrete file saving and handling methods
|
|
168
|
+
- **🔸 Helper/Utility**: Private methods for internal operations
|
|
169
|
+
|
|
170
|
+
The design follows the **Template Method Pattern** where `BaseExporter.export()` defines the algorithm skeleton, and subclasses customize specific steps through the hook methods.
|
|
171
|
+
|
|
172
|
+
## Plugin Configuration
|
|
173
|
+
|
|
174
|
+
Export plugin templates include configuration fields for filtering and plugin discovery:
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
actions:
|
|
178
|
+
export:
|
|
179
|
+
entrypoint: plugin.export.Exporter
|
|
180
|
+
annotation_types:
|
|
181
|
+
- image
|
|
182
|
+
- video
|
|
183
|
+
- audio
|
|
184
|
+
- text
|
|
185
|
+
- pcd
|
|
186
|
+
- prompt
|
|
187
|
+
|
|
188
|
+
data_types:
|
|
189
|
+
- image
|
|
190
|
+
- video
|
|
191
|
+
- audio
|
|
192
|
+
- text
|
|
193
|
+
- pcd
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Configuration Fields
|
|
197
|
+
|
|
198
|
+
- **data_types**: List of supported data types for filtering export plugins (plugin-level filter)
|
|
199
|
+
|
|
200
|
+
- Supported values: `image`, `video`, `audio`, `text`, `pcd`
|
|
201
|
+
- Used by the platform to filter and display relevant export plugins to users based on their data type
|
|
202
|
+
|
|
203
|
+
- **annotation_types**: List of supported annotation types for filtering export plugins (action-level filter)
|
|
204
|
+
- Supported values: `image`, `video`, `audio`, `text`, `pcd`, `prompt`
|
|
205
|
+
- Defined within each action's configuration (e.g., `actions.export.annotation_types`)
|
|
206
|
+
- Used by the platform to filter and display relevant export plugins to users based on their annotation type
|
|
207
|
+
- Each action can have different annotation type requirements
|
|
208
|
+
|
|
209
|
+
**Best Practice**: Customize these fields to accurately reflect your plugin's capabilities. The template includes all common types as examples, but you should modify the list to match what your plugin actually supports.
|
|
210
|
+
|
|
211
|
+
## BaseExporter Class Structure
|
|
212
|
+
|
|
213
|
+
The new BaseExporter class provides an object-oriented approach for export plugins:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
217
|
+
|
|
218
|
+
class Exporter(BaseExporter):
|
|
219
|
+
"""Plugin export action interface for organizing files."""
|
|
220
|
+
|
|
221
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
222
|
+
"""Initialize the plugin export action class."""
|
|
223
|
+
super().__init__(run, export_items, path_root, **params)
|
|
224
|
+
|
|
225
|
+
def convert_data(self, data):
|
|
226
|
+
"""Converts the data."""
|
|
227
|
+
return data
|
|
228
|
+
|
|
229
|
+
def before_convert(self, data):
|
|
230
|
+
"""Preprocesses the data before conversion."""
|
|
231
|
+
return data
|
|
232
|
+
|
|
233
|
+
def after_convert(self, data):
|
|
234
|
+
"""Post-processes the data after conversion."""
|
|
235
|
+
return data
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## BaseExporter Core Functionality
|
|
239
|
+
|
|
240
|
+
### Auto-provided Utilities
|
|
241
|
+
|
|
242
|
+
- **Complete Export Workflow**: `export()` method handles the complete export process
|
|
243
|
+
- **Data Conversion Pipeline**: `process_data_conversion()` handles before_convert → convert_data → after_convert processing
|
|
244
|
+
- **File Saving Management**: `process_file_saving()` handles original file and JSON file saving operations (can be overridden)
|
|
245
|
+
- **Output Directory Setup**: `setup_output_directories()` creates output directory structure (can be overridden)
|
|
246
|
+
|
|
247
|
+
### Required Methods (should be implemented by subclasses)
|
|
248
|
+
|
|
249
|
+
- **convert_data()**: Transform data during export
|
|
250
|
+
|
|
251
|
+
### Optional Methods (can be overridden by subclasses)
|
|
252
|
+
|
|
253
|
+
- **save_original_file()**: Save original files from export items
|
|
254
|
+
- **save_as_json()**: Save data as JSON files
|
|
255
|
+
- **before_convert()**: Pre-process data before conversion
|
|
256
|
+
- **after_convert()**: Post-process data after conversion
|
|
257
|
+
- **process_file_saving()**: Custom file saving logic
|
|
258
|
+
- **additional_file_saving()**: Save additional files after processing all export items
|
|
259
|
+
|
|
260
|
+
### Helper Methods
|
|
261
|
+
|
|
262
|
+
- **\_process_original_file_saving()**: Handle original file saving with metrics
|
|
263
|
+
- **\_process_json_file_saving()**: Handle JSON file saving with metrics
|
|
264
|
+
|
|
265
|
+
### Auto-provided Utilities
|
|
266
|
+
|
|
267
|
+
- Progress tracking via `self.run.set_progress()`
|
|
268
|
+
- Logging via `self.run.log_message()` and other run methods
|
|
269
|
+
- Error handling and metrics collection via self.run methods
|
|
270
|
+
|
|
271
|
+
## Additional File Saving
|
|
272
|
+
|
|
273
|
+
The `additional_file_saving()` method is called after all export items have been processed and is designed for saving files that depend on the collective data from all processed items. This is useful for creating:
|
|
274
|
+
|
|
275
|
+
- Metadata files (e.g., dataset statistics, class mappings)
|
|
276
|
+
- Configuration files (e.g., dataset.yaml for YOLO, classes.txt)
|
|
277
|
+
- Summary files (e.g., export reports, processing logs)
|
|
278
|
+
- Index files (e.g., file lists, directory structures)
|
|
279
|
+
|
|
280
|
+
### Method Signature
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
def additional_file_saving(self, unique_export_path):
|
|
284
|
+
"""Save additional files after processing all export items.
|
|
285
|
+
|
|
286
|
+
This method is called after the main export loop completes and is intended
|
|
287
|
+
for saving files that need to be created based on the collective data from
|
|
288
|
+
all processed export items (e.g., metadata files, configuration files,
|
|
289
|
+
summary files, etc.).
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
unique_export_path (str): The unique export directory path where
|
|
293
|
+
additional files should be saved.
|
|
294
|
+
"""
|
|
295
|
+
pass
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Example Usage
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
class YOLOExporter(BaseExporter):
|
|
302
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
303
|
+
super().__init__(run, export_items, path_root, **params)
|
|
304
|
+
self.class_names = set()
|
|
305
|
+
self.dataset_stats = {
|
|
306
|
+
'total_images': 0,
|
|
307
|
+
'total_annotations': 0,
|
|
308
|
+
'class_distribution': {}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
def convert_data(self, data):
|
|
312
|
+
# Track classes and stats during conversion
|
|
313
|
+
for annotation in data.get('annotations', []):
|
|
314
|
+
class_name = annotation['class_name']
|
|
315
|
+
self.class_names.add(class_name)
|
|
316
|
+
self.dataset_stats['class_distribution'][class_name] = \
|
|
317
|
+
self.dataset_stats['class_distribution'].get(class_name, 0) + 1
|
|
318
|
+
|
|
319
|
+
self.dataset_stats['total_images'] += 1
|
|
320
|
+
self.dataset_stats['total_annotations'] += len(data.get('annotations', []))
|
|
321
|
+
|
|
322
|
+
return data # ... rest of conversion logic
|
|
323
|
+
|
|
324
|
+
def additional_file_saving(self, unique_export_path):
|
|
325
|
+
"""Save YOLO configuration and metadata files."""
|
|
326
|
+
data_dir = Path(unique_export_path) / 'data'
|
|
327
|
+
data_dir.mkdir(exist_ok=True)
|
|
328
|
+
|
|
329
|
+
# 1. Save classes.txt file
|
|
330
|
+
classes_file = data_dir / 'classes.txt'
|
|
331
|
+
with classes_file.open('w') as f:
|
|
332
|
+
for class_name in sorted(self.class_names):
|
|
333
|
+
f.write(f"{class_name}\n")
|
|
334
|
+
self.run.log_message(f"Saved classes file: {classes_file}")
|
|
335
|
+
|
|
336
|
+
# 2. Save dataset.yaml file
|
|
337
|
+
dataset_config = {
|
|
338
|
+
'path': str(unique_export_path),
|
|
339
|
+
'train': 'images',
|
|
340
|
+
'val': 'images',
|
|
341
|
+
'names': {i: name for i, name in enumerate(sorted(self.class_names))}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
dataset_file = data_dir / 'dataset.yaml'
|
|
345
|
+
with dataset_file.open('w') as f:
|
|
346
|
+
yaml.dump(dataset_config, f, default_flow_style=False)
|
|
347
|
+
self.run.log_message(f"Saved dataset config: {dataset_file}")
|
|
348
|
+
|
|
349
|
+
# 3. Save export statistics
|
|
350
|
+
stats_file = data_dir / 'export_stats.json'
|
|
351
|
+
with stats_file.open('w') as f:
|
|
352
|
+
json.dump(self.dataset_stats, f, indent=2)
|
|
353
|
+
self.run.log_message(f"Saved export statistics: {stats_file}")
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Common Use Cases
|
|
357
|
+
|
|
358
|
+
#### 1. Dataset Configuration Files
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
def additional_file_saving(self, unique_export_path):
|
|
362
|
+
# Create dataset configuration for training frameworks
|
|
363
|
+
config = {
|
|
364
|
+
'dataset_name': self.params.get('name'),
|
|
365
|
+
'created_at': datetime.now().isoformat(),
|
|
366
|
+
'total_samples': len(self.processed_items),
|
|
367
|
+
'classes': list(self.class_mapping.keys())
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
config_file = Path(unique_export_path) / 'dataset_config.json'
|
|
371
|
+
with config_file.open('w') as f:
|
|
372
|
+
json.dump(config, f, indent=2)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### 2. Export Summary Reports
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
def additional_file_saving(self, unique_export_path):
|
|
379
|
+
# Generate export summary
|
|
380
|
+
summary = {
|
|
381
|
+
'export_info': {
|
|
382
|
+
'plugin_name': self.__class__.__name__,
|
|
383
|
+
'export_time': datetime.now().isoformat(),
|
|
384
|
+
'export_path': str(unique_export_path)
|
|
385
|
+
},
|
|
386
|
+
'statistics': self.get_export_statistics(),
|
|
387
|
+
'errors': self.get_error_summary()
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
summary_file = Path(unique_export_path) / 'export_summary.json'
|
|
391
|
+
with summary_file.open('w') as f:
|
|
392
|
+
json.dump(summary, f, indent=2)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### 3. Index and Manifest Files
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
def additional_file_saving(self, unique_export_path):
|
|
399
|
+
# Create file index for processed items
|
|
400
|
+
file_index = []
|
|
401
|
+
for item in self.processed_items:
|
|
402
|
+
file_index.append({
|
|
403
|
+
'original_file': item['original_filename'],
|
|
404
|
+
'json_file': f"{item['stem']}.json",
|
|
405
|
+
'processed_at': item['timestamp']
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
index_file = Path(unique_export_path) / 'file_index.json'
|
|
409
|
+
with index_file.open('w') as f:
|
|
410
|
+
json.dump(file_index, f, indent=2)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Key Features
|
|
414
|
+
|
|
415
|
+
- **Progress Tracking**: Built-in progress monitoring with `run.set_progress()`
|
|
416
|
+
- **Error Handling**: Automatic error collection and reporting
|
|
417
|
+
- **Metrics Logging**: Track success/failure rates with `run.log_metrics()`
|
|
418
|
+
- **File Management**: Handles both original files and processed JSON data
|
|
419
|
+
- **Logging**: Comprehensive logging with `run.log_message()` and custom events
|
|
420
|
+
|
|
421
|
+
## Practical Examples
|
|
422
|
+
|
|
423
|
+
### YOLO Format Exporter with Custom Directory Structure
|
|
424
|
+
|
|
425
|
+
Here's a complete example that exports data in YOLO format while leveraging `setup_output_directories` and `process_file_saving`:
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
429
|
+
import os
|
|
430
|
+
import json
|
|
431
|
+
|
|
432
|
+
class YOLOExporter(BaseExporter):
|
|
433
|
+
"""Plugin to export data in YOLO format."""
|
|
434
|
+
|
|
435
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
436
|
+
super().__init__(run, export_items, path_root, **params)
|
|
437
|
+
self.class_mapping = {}
|
|
438
|
+
|
|
439
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
440
|
+
"""Create directories matching YOLO project structure."""
|
|
441
|
+
directories = ['images', 'labels', 'data']
|
|
442
|
+
|
|
443
|
+
for directory in directories:
|
|
444
|
+
dir_path = os.path.join(unique_export_path, directory)
|
|
445
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
446
|
+
self.run.log_message(f"Created YOLO directory: {dir_path}")
|
|
447
|
+
|
|
448
|
+
return unique_export_path
|
|
449
|
+
|
|
450
|
+
def convert_data(self, data):
|
|
451
|
+
"""Convert annotation data to YOLO format."""
|
|
452
|
+
converted_annotations = []
|
|
453
|
+
|
|
454
|
+
for annotation in data.get('annotations', []):
|
|
455
|
+
# Convert bounding box to YOLO format
|
|
456
|
+
bbox = annotation['geometry']['bbox']
|
|
457
|
+
image_width = data['image']['width']
|
|
458
|
+
image_height = data['image']['height']
|
|
459
|
+
|
|
460
|
+
# YOLO format: center_x, center_y, width, height (normalized)
|
|
461
|
+
center_x = (bbox['x'] + bbox['width'] / 2) / image_width
|
|
462
|
+
center_y = (bbox['y'] + bbox['height'] / 2) / image_height
|
|
463
|
+
width = bbox['width'] / image_width
|
|
464
|
+
height = bbox['height'] / image_height
|
|
465
|
+
|
|
466
|
+
# Class ID mapping
|
|
467
|
+
class_name = annotation['class_name']
|
|
468
|
+
if class_name not in self.class_mapping:
|
|
469
|
+
self.class_mapping[class_name] = len(self.class_mapping)
|
|
470
|
+
|
|
471
|
+
class_id = self.class_mapping[class_name]
|
|
472
|
+
|
|
473
|
+
converted_annotations.append({
|
|
474
|
+
'class_id': class_id,
|
|
475
|
+
'center_x': center_x,
|
|
476
|
+
'center_y': center_y,
|
|
477
|
+
'width': width,
|
|
478
|
+
'height': height
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
'yolo_annotations': converted_annotations,
|
|
483
|
+
'class_mapping': self.class_mapping,
|
|
484
|
+
'image_info': data['image']
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
def process_file_saving(
|
|
488
|
+
self,
|
|
489
|
+
final_data,
|
|
490
|
+
unique_export_path,
|
|
491
|
+
save_original_file_flag,
|
|
492
|
+
errors_json_file_list,
|
|
493
|
+
errors_original_file_list,
|
|
494
|
+
original_file_metrics_record,
|
|
495
|
+
data_file_metrics_record,
|
|
496
|
+
current_index,
|
|
497
|
+
):
|
|
498
|
+
"""Handle file saving in YOLO format."""
|
|
499
|
+
try:
|
|
500
|
+
export_item = self.export_items[current_index - 1]
|
|
501
|
+
base_name = os.path.splitext(export_item.original_file.name)[0]
|
|
502
|
+
|
|
503
|
+
# 1. Save image file to images folder
|
|
504
|
+
if save_original_file_flag:
|
|
505
|
+
images_dir = os.path.join(unique_export_path, 'images')
|
|
506
|
+
image_path = os.path.join(images_dir, export_item.original_file.name)
|
|
507
|
+
import shutil
|
|
508
|
+
shutil.copy2(export_item.original_file.path, image_path)
|
|
509
|
+
self.run.log_message(f"Saved image: {image_path}")
|
|
510
|
+
|
|
511
|
+
# 2. Save YOLO label file to labels folder
|
|
512
|
+
labels_dir = os.path.join(unique_export_path, 'labels')
|
|
513
|
+
label_path = os.path.join(labels_dir, f"{base_name}.txt")
|
|
514
|
+
|
|
515
|
+
with open(label_path, 'w') as f:
|
|
516
|
+
for ann in final_data.get('yolo_annotations', []):
|
|
517
|
+
line = f"{ann['class_id']} {ann['center_x']} {ann['center_y']} {ann['width']} {ann['height']}\n"
|
|
518
|
+
f.write(line)
|
|
519
|
+
|
|
520
|
+
self.run.log_message(f"Saved YOLO label: {label_path}")
|
|
521
|
+
|
|
522
|
+
# 3. Save class mapping file (only once)
|
|
523
|
+
if current_index == 1: # Only when processing first file
|
|
524
|
+
classes_path = os.path.join(unique_export_path, 'data', 'classes.txt')
|
|
525
|
+
with open(classes_path, 'w') as f:
|
|
526
|
+
for class_name, class_id in sorted(final_data['class_mapping'].items(), key=lambda x: x[1]):
|
|
527
|
+
f.write(f"{class_name}\n")
|
|
528
|
+
self.run.log_message(f"Saved classes file: {classes_path}")
|
|
529
|
+
|
|
530
|
+
return True
|
|
531
|
+
|
|
532
|
+
except Exception as e:
|
|
533
|
+
self.run.log_message(f"Error during file saving: {str(e)}", level="error")
|
|
534
|
+
errors_json_file_list.append(f"Export item {current_index}: {str(e)}")
|
|
535
|
+
return True # Return True to continue processing other files
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
This example demonstrates how to leverage BaseExporter's key extension points `setup_output_directories` and `process_file_saving` to:
|
|
539
|
+
|
|
540
|
+
- Create YOLO project structure (`images/`, `labels/`, `data/`)
|
|
541
|
+
- Save image files and YOLO label files in appropriate locations
|
|
542
|
+
- Manage class mapping files
|
|
543
|
+
- Track progress and handle errors
|
|
544
|
+
|
|
545
|
+
## Quick Start Guide
|
|
546
|
+
|
|
547
|
+
Step-by-step guide to create a simple plugin using BaseExporter:
|
|
548
|
+
|
|
549
|
+
### Step 1: Inherit Base Class
|
|
550
|
+
|
|
551
|
+
```python
|
|
552
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
553
|
+
|
|
554
|
+
class MyExporter(BaseExporter):
|
|
555
|
+
def convert_data(self, data):
|
|
556
|
+
# Required: Implement data transformation logic
|
|
557
|
+
return data # or return transformed data
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Step 2: Override Additional Methods as Needed
|
|
561
|
+
|
|
562
|
+
```python
|
|
563
|
+
def before_convert(self, data):
|
|
564
|
+
# Optional: Pre-processing before conversion
|
|
565
|
+
return data
|
|
566
|
+
|
|
567
|
+
def after_convert(self, converted_data):
|
|
568
|
+
# Optional: Post-processing after conversion
|
|
569
|
+
return converted_data
|
|
570
|
+
|
|
571
|
+
def save_as_json(self, converted_data, output_path):
|
|
572
|
+
# Optional: Custom save format
|
|
573
|
+
# By default saves as JSON format
|
|
574
|
+
pass
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Step 3: Register Plugin
|
|
578
|
+
|
|
579
|
+
Plugin directory structure:
|
|
580
|
+
|
|
581
|
+
```
|
|
582
|
+
my_plugin/
|
|
583
|
+
├── __init__.py
|
|
584
|
+
├── plugin.py # MyExporter class definition
|
|
585
|
+
└── manifest.yaml # Plugin metadata
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Export Action Architecture
|
|
589
|
+
|
|
590
|
+
The export system has been refactored into a modular architecture with specialized components for different aspects of data export processing:
|
|
591
|
+
|
|
592
|
+
```mermaid
|
|
593
|
+
classDiagram
|
|
594
|
+
%% Light/Dark mode compatible colors with semi-transparency
|
|
595
|
+
classDef baseClass fill:#e1f5fe80,stroke:#0288d1,stroke-width:2px
|
|
596
|
+
classDef childClass fill:#c8e6c980,stroke:#388e3c,stroke-width:2px
|
|
597
|
+
classDef modelClass fill:#fff9c480,stroke:#f57c00,stroke-width:2px
|
|
598
|
+
classDef utilClass fill:#f5f5f580,stroke:#616161,stroke-width:2px
|
|
599
|
+
classDef enumClass fill:#ffccbc80,stroke:#d32f2f,stroke-width:2px
|
|
600
|
+
|
|
601
|
+
class ExportAction {
|
|
602
|
+
+name: str = "export"
|
|
603
|
+
+category: PluginCategory.EXPORT
|
|
604
|
+
+method: RunMethod.JOB
|
|
605
|
+
+run_class: ExportRun
|
|
606
|
+
+params_model: ExportParams
|
|
607
|
+
+progress_categories: dict
|
|
608
|
+
+metrics_categories: dict
|
|
609
|
+
|
|
610
|
+
+start() dict
|
|
611
|
+
+get_exporter(...) object
|
|
612
|
+
+_get_export_items(target, filter) Generator
|
|
613
|
+
+_create_target_handler(target) object
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
class ExportRun {
|
|
617
|
+
+log_message_with_code(code, args, level) None
|
|
618
|
+
+log_export_event(code, args, level) None
|
|
619
|
+
+export_log_json_file(id, file_info, status) None
|
|
620
|
+
+export_log_original_file(id, file_info, status) None
|
|
621
|
+
+ExportEventLog: BaseModel
|
|
622
|
+
+DataFileLog: BaseModel
|
|
623
|
+
+MetricsRecord: BaseModel
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
class ExportParams {
|
|
627
|
+
+name: str
|
|
628
|
+
+storage: int
|
|
629
|
+
+target: Literal["assignment", "ground_truth", "task"]
|
|
630
|
+
+filter: dict
|
|
631
|
+
+path: str
|
|
632
|
+
+save_original_file: bool = True
|
|
633
|
+
+extra_params: dict = {}
|
|
634
|
+
|
|
635
|
+
+check_storage_exists(value) str
|
|
636
|
+
+validate_target_filter(cls, values) dict
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
class LogCode {
|
|
640
|
+
+EXPORT_STARTED: str
|
|
641
|
+
+ITEMS_DISCOVERED: str
|
|
642
|
+
+CONVERSION_STARTED: str
|
|
643
|
+
+CONVERSION_COMPLETED: str
|
|
644
|
+
+FILE_SAVED: str
|
|
645
|
+
+EXPORT_COMPLETED: str
|
|
646
|
+
+EXPORT_FAILED: str
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
class ExportStatus {
|
|
650
|
+
+SUCCESS: str = "success"
|
|
651
|
+
+FAILED: str = "failed"
|
|
652
|
+
+SKIPPED: str = "skipped"
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
class ExportError {
|
|
656
|
+
+message: str
|
|
657
|
+
+code: str
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
class ExportValidationError {
|
|
661
|
+
+message: str
|
|
662
|
+
+field: str
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
class ExportTargetError {
|
|
666
|
+
+message: str
|
|
667
|
+
+target: str
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
%% Relationships
|
|
671
|
+
ExportAction --> ExportRun : uses
|
|
672
|
+
ExportAction --> ExportParams : validates with
|
|
673
|
+
ExportRun --> LogCode : logs with
|
|
674
|
+
ExportRun --> ExportStatus : tracks status
|
|
675
|
+
ExportAction --> ExportError : may raise
|
|
676
|
+
ExportAction --> ExportValidationError : may raise
|
|
677
|
+
ExportAction --> ExportTargetError : may raise
|
|
678
|
+
|
|
679
|
+
%% Apply styles
|
|
680
|
+
class ExportAction baseClass
|
|
681
|
+
class ExportRun childClass
|
|
682
|
+
class ExportParams modelClass
|
|
683
|
+
class LogCode,ExportStatus enumClass
|
|
684
|
+
class ExportError,ExportValidationError,ExportTargetError utilClass
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Modular Structure
|
|
688
|
+
|
|
689
|
+
The export action follows a clean modular organization:
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
synapse_sdk/plugins/categories/export/actions/export/
|
|
693
|
+
├── __init__.py # Clean module interfaces
|
|
694
|
+
├── action.py # ExportAction class
|
|
695
|
+
├── enums.py # ExportStatus, LogCode + LOG_MESSAGES
|
|
696
|
+
├── exceptions.py # Export-specific exceptions
|
|
697
|
+
├── models.py # ExportParams model
|
|
698
|
+
├── run.py # ExportRun class
|
|
699
|
+
└── utils.py # Target handlers and utilities
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**Key Benefits:**
|
|
703
|
+
|
|
704
|
+
- **Enhanced Maintainability**: Smaller, focused files are easier to understand and modify
|
|
705
|
+
- **Code Consistency**: Export action now follows the same pattern as upload action
|
|
706
|
+
- **Better Organization**: Related functionality is grouped logically
|
|
707
|
+
- **Improved Readability**: Clear separation of concerns across modules
|
|
708
|
+
|
|
709
|
+
## Creating Export Plugins
|
|
710
|
+
|
|
711
|
+
Export plugins now use the BaseExporter class-based approach for better organization and reusability. Here's how to create a custom export plugin:
|
|
712
|
+
|
|
713
|
+
### Step 1: Generate Export Plugin Template
|
|
714
|
+
|
|
715
|
+
```bash
|
|
716
|
+
synapse plugin create
|
|
717
|
+
# Select 'export' as category
|
|
718
|
+
# Plugin will be created with export template
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Step 2: Customize Export Parameters
|
|
722
|
+
|
|
723
|
+
The `ExportParams` model defines the required parameters:
|
|
724
|
+
|
|
725
|
+
```python
|
|
726
|
+
from synapse_sdk.plugins.categories.export.actions.export.models import ExportParams
|
|
727
|
+
from pydantic import BaseModel
|
|
728
|
+
from typing import Literal
|
|
729
|
+
|
|
730
|
+
class CustomExportParams(ExportParams):
|
|
731
|
+
# Add custom parameters
|
|
732
|
+
output_format: Literal['json', 'csv', 'xml'] = 'json'
|
|
733
|
+
include_metadata: bool = True
|
|
734
|
+
compression: bool = False
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### Step 3: Implement Data Transformation
|
|
738
|
+
|
|
739
|
+
Implement the required methods in your `Exporter` class in `plugin/export.py`:
|
|
740
|
+
|
|
741
|
+
```python
|
|
742
|
+
from datetime import datetime
|
|
743
|
+
from synapse_sdk.plugins.categories.export.templates.plugin import BaseExporter
|
|
744
|
+
|
|
745
|
+
class Exporter(BaseExporter):
|
|
746
|
+
"""Custom export plugin with COCO format conversion."""
|
|
747
|
+
|
|
748
|
+
def convert_data(self, data):
|
|
749
|
+
"""Convert annotation data to your desired format."""
|
|
750
|
+
# Example: Convert to COCO format
|
|
751
|
+
if data.get('data_type') == 'image_detection':
|
|
752
|
+
return self.convert_to_coco_format(data)
|
|
753
|
+
elif data.get('data_type') == 'image_classification':
|
|
754
|
+
return self.convert_to_classification_format(data)
|
|
755
|
+
return data
|
|
756
|
+
|
|
757
|
+
def before_convert(self, export_item):
|
|
758
|
+
"""Preprocess data before conversion."""
|
|
759
|
+
# Add validation, filtering, or preprocessing
|
|
760
|
+
if not export_item.get('data'):
|
|
761
|
+
return None # Skip empty items
|
|
762
|
+
|
|
763
|
+
# Add custom metadata
|
|
764
|
+
export_item['processed_at'] = datetime.now().isoformat()
|
|
765
|
+
return export_item
|
|
766
|
+
|
|
767
|
+
def after_convert(self, converted_data):
|
|
768
|
+
"""Post-process converted data."""
|
|
769
|
+
# Add final touches, validation, or formatting
|
|
770
|
+
if 'annotations' in converted_data:
|
|
771
|
+
converted_data['annotation_count'] = len(converted_data['annotations'])
|
|
772
|
+
return converted_data
|
|
773
|
+
|
|
774
|
+
def convert_to_coco_format(self, data):
|
|
775
|
+
"""Example: Convert to COCO detection format."""
|
|
776
|
+
coco_data = {
|
|
777
|
+
"images": [],
|
|
778
|
+
"annotations": [],
|
|
779
|
+
"categories": []
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
# Transform annotation data to COCO format
|
|
783
|
+
for annotation in data.get('annotations', []):
|
|
784
|
+
coco_annotation = {
|
|
785
|
+
"id": annotation['id'],
|
|
786
|
+
"image_id": annotation['image_id'],
|
|
787
|
+
"category_id": annotation['category_id'],
|
|
788
|
+
"bbox": annotation['bbox'],
|
|
789
|
+
"area": annotation.get('area', 0),
|
|
790
|
+
"iscrowd": 0
|
|
791
|
+
}
|
|
792
|
+
coco_data["annotations"].append(coco_annotation)
|
|
793
|
+
|
|
794
|
+
return coco_data
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Step 4: Configure Export Targets
|
|
798
|
+
|
|
799
|
+
The export action supports different data sources:
|
|
800
|
+
|
|
801
|
+
```python
|
|
802
|
+
# Filter examples for different targets
|
|
803
|
+
filters = {
|
|
804
|
+
# For ground truth export
|
|
805
|
+
"ground_truth": {
|
|
806
|
+
"ground_truth_dataset_version": 123,
|
|
807
|
+
"expand": ["data"]
|
|
808
|
+
},
|
|
809
|
+
|
|
810
|
+
# For assignment export
|
|
811
|
+
"assignment": {
|
|
812
|
+
"project": 456,
|
|
813
|
+
"status": "completed",
|
|
814
|
+
"expand": ["data"]
|
|
815
|
+
},
|
|
816
|
+
|
|
817
|
+
# For task export
|
|
818
|
+
"task": {
|
|
819
|
+
"project": 456,
|
|
820
|
+
"assignment": 789,
|
|
821
|
+
"expand": ["data_unit", "assignment"]
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### Step 5: Handle File Operations
|
|
827
|
+
|
|
828
|
+
Customize file saving and organization by overriding BaseExporter methods:
|
|
829
|
+
|
|
830
|
+
```python
|
|
831
|
+
import json
|
|
832
|
+
from pathlib import Path
|
|
833
|
+
from synapse_sdk.plugins.categories.export.actions.export.enums import ExportStatus
|
|
834
|
+
|
|
835
|
+
class Exporter(BaseExporter):
|
|
836
|
+
"""Custom export plugin with multiple format support."""
|
|
837
|
+
|
|
838
|
+
def save_as_json(self, result, base_path, error_file_list):
|
|
839
|
+
"""Custom JSON saving with different formats."""
|
|
840
|
+
file_name = Path(self.get_original_file_name(result['files'])).stem
|
|
841
|
+
|
|
842
|
+
# Choose output format based on params
|
|
843
|
+
if self.params.get('output_format') == 'csv':
|
|
844
|
+
return self.save_as_csv(result, base_path, error_file_list)
|
|
845
|
+
elif self.params.get('output_format') == 'xml':
|
|
846
|
+
return self.save_as_xml(result, base_path, error_file_list)
|
|
847
|
+
|
|
848
|
+
# Default JSON handling
|
|
849
|
+
json_data = result['data']
|
|
850
|
+
file_info = {'file_name': f'{file_name}.json'}
|
|
851
|
+
|
|
852
|
+
try:
|
|
853
|
+
with (base_path / f'{file_name}.json').open('w', encoding='utf-8') as f:
|
|
854
|
+
json.dump(json_data, f, indent=4, ensure_ascii=False)
|
|
855
|
+
status = ExportStatus.SUCCESS
|
|
856
|
+
except Exception as e:
|
|
857
|
+
error_file_list.append([f'{file_name}.json', str(e)])
|
|
858
|
+
status = ExportStatus.FAILED
|
|
859
|
+
|
|
860
|
+
self.run.export_log_json_file(result['id'], file_info, status)
|
|
861
|
+
return status
|
|
862
|
+
|
|
863
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
864
|
+
"""Custom directory structure."""
|
|
865
|
+
# Create format-specific directories
|
|
866
|
+
output_paths = super().setup_output_directories(unique_export_path, save_original_file_flag)
|
|
867
|
+
|
|
868
|
+
# Add custom directories based on output format
|
|
869
|
+
format_dir = unique_export_path / self.params.get('output_format', 'json')
|
|
870
|
+
format_dir.mkdir(parents=True, exist_ok=True)
|
|
871
|
+
output_paths['format_output_path'] = format_dir
|
|
872
|
+
|
|
873
|
+
return output_paths
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### Step 6: Usage Examples
|
|
877
|
+
|
|
878
|
+
Running export plugins with different configurations:
|
|
879
|
+
|
|
880
|
+
```bash
|
|
881
|
+
# Basic export of ground truth data
|
|
882
|
+
synapse plugin run export '{
|
|
883
|
+
"name": "my_export",
|
|
884
|
+
"storage": 1,
|
|
885
|
+
"target": "ground_truth",
|
|
886
|
+
"filter": {"ground_truth_dataset_version": 123},
|
|
887
|
+
"path": "exports/ground_truth",
|
|
888
|
+
"save_original_file": true
|
|
889
|
+
}' --plugin my-export-plugin
|
|
890
|
+
|
|
891
|
+
# Export assignments with custom parameters
|
|
892
|
+
synapse plugin run export '{
|
|
893
|
+
"name": "assignment_export",
|
|
894
|
+
"storage": 1,
|
|
895
|
+
"target": "assignment",
|
|
896
|
+
"filter": {"project": 456, "status": "completed"},
|
|
897
|
+
"path": "exports/assignments",
|
|
898
|
+
"save_original_file": false,
|
|
899
|
+
"extra_params": {
|
|
900
|
+
"output_format": "coco",
|
|
901
|
+
"include_metadata": true
|
|
902
|
+
}
|
|
903
|
+
}' --plugin custom-coco-export
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
## Common Export Patterns
|
|
907
|
+
|
|
908
|
+
```python
|
|
909
|
+
# Pattern 1: Format-specific conversion
|
|
910
|
+
class Exporter(BaseExporter):
|
|
911
|
+
def convert_data(self, data):
|
|
912
|
+
"""Convert to YOLO format."""
|
|
913
|
+
if data.get('task_type') == 'object_detection':
|
|
914
|
+
return self.convert_to_yolo_format(data)
|
|
915
|
+
return data
|
|
916
|
+
|
|
917
|
+
# Pattern 2: Conditional file organization
|
|
918
|
+
class Exporter(BaseExporter):
|
|
919
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
920
|
+
# Call parent method
|
|
921
|
+
output_paths = super().setup_output_directories(unique_export_path, save_original_file_flag)
|
|
922
|
+
|
|
923
|
+
# Create separate folders by category
|
|
924
|
+
for category in ['train', 'val', 'test']:
|
|
925
|
+
category_path = unique_export_path / category
|
|
926
|
+
category_path.mkdir(parents=True, exist_ok=True)
|
|
927
|
+
output_paths[f'{category}_path'] = category_path
|
|
928
|
+
|
|
929
|
+
return output_paths
|
|
930
|
+
|
|
931
|
+
# Pattern 3: Batch processing with validation
|
|
932
|
+
class Exporter(BaseExporter):
|
|
933
|
+
def before_convert(self, export_item):
|
|
934
|
+
# Validate required fields
|
|
935
|
+
required_fields = ['data', 'files', 'id']
|
|
936
|
+
for field in required_fields:
|
|
937
|
+
if field not in export_item:
|
|
938
|
+
raise ValueError(f"Missing required field: {field}")
|
|
939
|
+
return export_item
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
## Development Tips and Best Practices
|
|
943
|
+
|
|
944
|
+
### 1. Error Handling
|
|
945
|
+
|
|
946
|
+
```python
|
|
947
|
+
def convert_data(self, data):
|
|
948
|
+
try:
|
|
949
|
+
# Conversion logic
|
|
950
|
+
result = self.process_annotations(data)
|
|
951
|
+
return result
|
|
952
|
+
except Exception as e:
|
|
953
|
+
self.run.log_message(f"Error during conversion: {str(e)}", level="error")
|
|
954
|
+
raise # BaseExporter handles errors automatically
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### 2. Progress Tracking
|
|
958
|
+
|
|
959
|
+
```python
|
|
960
|
+
def convert_data(self, data):
|
|
961
|
+
annotations = data.get('annotations', [])
|
|
962
|
+
total = len(annotations)
|
|
963
|
+
|
|
964
|
+
for i, annotation in enumerate(annotations):
|
|
965
|
+
# Update progress (value between 0-100)
|
|
966
|
+
progress = int((i / total) * 100)
|
|
967
|
+
self.run.set_progress(progress)
|
|
968
|
+
|
|
969
|
+
# Conversion logic...
|
|
970
|
+
|
|
971
|
+
return converted_data
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### 3. Metrics Collection
|
|
975
|
+
|
|
976
|
+
```python
|
|
977
|
+
def after_convert(self, converted_data):
|
|
978
|
+
# Collect useful metrics
|
|
979
|
+
metrics = {
|
|
980
|
+
'total_exported': len(converted_data.get('annotations', [])),
|
|
981
|
+
'processing_time': time.time() - self.start_time,
|
|
982
|
+
'success_rate': self.calculate_success_rate(),
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
self.run.log_metrics(metrics)
|
|
986
|
+
return converted_data
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
### 4. Logging Usage
|
|
990
|
+
|
|
991
|
+
```python
|
|
992
|
+
def convert_data(self, data):
|
|
993
|
+
self.run.log_message("Starting data conversion", level="info")
|
|
994
|
+
|
|
995
|
+
if not data.get('annotations'):
|
|
996
|
+
self.run.log_message("No annotation data found", level="warning")
|
|
997
|
+
return data
|
|
998
|
+
|
|
999
|
+
# Conversion logic...
|
|
1000
|
+
|
|
1001
|
+
self.run.log_message(f"Conversion complete: processed {len(result)} items", level="success")
|
|
1002
|
+
return result
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
### 5. Parameter Handling
|
|
1006
|
+
|
|
1007
|
+
```python
|
|
1008
|
+
def __init__(self, run, export_items, path_root, **params):
|
|
1009
|
+
super().__init__(run, export_items, path_root, **params)
|
|
1010
|
+
|
|
1011
|
+
# Handle custom parameters
|
|
1012
|
+
self.output_format = params.get('output_format', 'json')
|
|
1013
|
+
self.include_metadata = params.get('include_metadata', True)
|
|
1014
|
+
self.compression = params.get('compression', False)
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
## Best Practices
|
|
1018
|
+
|
|
1019
|
+
### Data Processing
|
|
1020
|
+
|
|
1021
|
+
- **Memory Efficiency**: Use generators for processing large datasets
|
|
1022
|
+
- **Error Recovery**: Implement graceful error handling for individual items
|
|
1023
|
+
- **Progress Reporting**: Update progress regularly for long-running exports
|
|
1024
|
+
- **Data Validation**: Validate data structure before conversion
|
|
1025
|
+
|
|
1026
|
+
```python
|
|
1027
|
+
class Exporter(BaseExporter):
|
|
1028
|
+
def export(self, export_items=None, results=None, **kwargs):
|
|
1029
|
+
"""Override the main export method for custom processing."""
|
|
1030
|
+
# Use tee to count items without consuming generator
|
|
1031
|
+
items_to_process = export_items if export_items is not None else self.export_items
|
|
1032
|
+
export_items_count, export_items_process = tee(items_to_process)
|
|
1033
|
+
total = sum(1 for _ in export_items_count)
|
|
1034
|
+
|
|
1035
|
+
# Custom processing with error handling
|
|
1036
|
+
for no, export_item in enumerate(export_items_process, start=1):
|
|
1037
|
+
try:
|
|
1038
|
+
# Use the built-in data conversion pipeline
|
|
1039
|
+
processed_item = self.process_data_conversion(export_item)
|
|
1040
|
+
self.run.set_progress(no, total, category='dataset_conversion')
|
|
1041
|
+
except Exception as e:
|
|
1042
|
+
self.run.log_message(f"Error processing item {no}: {str(e)}", "ERROR")
|
|
1043
|
+
continue
|
|
1044
|
+
|
|
1045
|
+
# Call parent's export method for standard processing
|
|
1046
|
+
# or implement your own complete workflow
|
|
1047
|
+
return super().export(export_items, results, **kwargs)
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
### File Management
|
|
1051
|
+
|
|
1052
|
+
- **Unique Paths**: Prevent file collisions with timestamp or counter suffixes
|
|
1053
|
+
- **Directory Structure**: Organize output files logically
|
|
1054
|
+
- **Error Logging**: Track failed files for debugging
|
|
1055
|
+
- **Cleanup**: Remove temporary files on completion
|
|
1056
|
+
|
|
1057
|
+
```python
|
|
1058
|
+
class Exporter(BaseExporter):
|
|
1059
|
+
def setup_output_directories(self, unique_export_path, save_original_file_flag):
|
|
1060
|
+
"""Create unique export directory structure."""
|
|
1061
|
+
# BaseExporter already handles unique path creation via _create_unique_export_path
|
|
1062
|
+
# This method sets up the internal directory structure
|
|
1063
|
+
output_paths = super().setup_output_directories(unique_export_path, save_original_file_flag)
|
|
1064
|
+
|
|
1065
|
+
# Add custom subdirectories as needed
|
|
1066
|
+
custom_dir = unique_export_path / 'custom_output'
|
|
1067
|
+
custom_dir.mkdir(parents=True, exist_ok=True)
|
|
1068
|
+
output_paths['custom_output_path'] = custom_dir
|
|
1069
|
+
|
|
1070
|
+
return output_paths
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
### Format Conversion
|
|
1074
|
+
|
|
1075
|
+
- **Flexible Templates**: Design templates that work with multiple data types
|
|
1076
|
+
- **Schema Validation**: Validate output against expected schemas
|
|
1077
|
+
- **Metadata Preservation**: Maintain important metadata during conversion
|
|
1078
|
+
- **Version Compatibility**: Handle different data schema versions
|
|
1079
|
+
|
|
1080
|
+
## Frequently Asked Questions
|
|
1081
|
+
|
|
1082
|
+
**Q: Can I implement plugins directly without using BaseExporter?**
|
|
1083
|
+
A: Yes, but it's not recommended. BaseExporter provides essential features like progress tracking, error handling, and metrics collection automatically.
|
|
1084
|
+
|
|
1085
|
+
**Q: Can I export to multiple file formats simultaneously?**
|
|
1086
|
+
A: Yes, you can override the `process_file_saving()` method to save in multiple formats.
|
|
1087
|
+
|
|
1088
|
+
**Q: How can I optimize memory usage when processing large datasets?**
|
|
1089
|
+
A: Consider using streaming processing in `convert_data()` rather than loading all data at once.
|
|
1090
|
+
|
|
1091
|
+
**Q: What should I do if progress is not displaying correctly?**
|
|
1092
|
+
A: Make sure you're calling `self.run.set_progress()` at appropriate intervals and using integer values between 0-100.
|