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,948 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: upload-plugin-action
|
|
3
|
+
title: 업로드 액션 개발
|
|
4
|
+
sidebar_position: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 업로드 액션 개발
|
|
8
|
+
|
|
9
|
+
이 가이드는 업로드 액션 아키텍처를 이해, 확장 또는 사용자 정의하려는 SDK 개발자 및 기여자를 위한 것입니다.
|
|
10
|
+
|
|
11
|
+
## 아키텍처 개요
|
|
12
|
+
|
|
13
|
+
업로드 시스템은 입증된 디자인 패턴을 기반으로 한 현대적이고 확장 가능한 아키텍처를 사용합니다. 리팩토링된 구현은 이전의 모놀리식 접근 방식을 명확한 관심사 분리를 가진 모듈식, 전략 기반 시스템으로 변환합니다.
|
|
14
|
+
|
|
15
|
+
### 디자인 패턴
|
|
16
|
+
|
|
17
|
+
아키텍처는 몇 가지 주요 디자인 패턴을 활용합니다:
|
|
18
|
+
|
|
19
|
+
- **전략 패턴**: 검증, 파일 검색, 메타데이터 처리, 업로드 작업 및 데이터 단위 생성을 위한 플러그형 동작
|
|
20
|
+
- **퍼사드 패턴**: UploadOrchestrator는 복잡한 워크플로우를 조정하기 위한 단순화된 인터페이스를 제공합니다.
|
|
21
|
+
- **팩토리 패턴**: StrategyFactory는 런타임 매개변수를 기반으로 적절한 전략 구현을 생성합니다.
|
|
22
|
+
- **컨텍스트 패턴**: UploadContext는 워크플로우 구성 요소 간의 공유 상태 및 통신을 유지합니다.
|
|
23
|
+
|
|
24
|
+
### 구성 요소 아키텍처
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
classDiagram
|
|
28
|
+
%% 라이트/다크 모드 호환 색상
|
|
29
|
+
classDef coreClass fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000000
|
|
30
|
+
classDef strategyClass fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000000
|
|
31
|
+
classDef stepClass fill:#fff9c4,stroke:#f57c00,stroke-width:2px,color:#000000
|
|
32
|
+
classDef contextClass fill:#ffebee,stroke:#d32f2f,stroke-width:2px,color:#000000
|
|
33
|
+
|
|
34
|
+
class UploadAction {
|
|
35
|
+
+name: str = "upload"
|
|
36
|
+
+category: PluginCategory.UPLOAD
|
|
37
|
+
+method: RunMethod.JOB
|
|
38
|
+
+run_class: UploadRun
|
|
39
|
+
+params_model: UploadParams
|
|
40
|
+
+progress_categories: dict
|
|
41
|
+
+metrics_categories: dict
|
|
42
|
+
+strategy_factory: StrategyFactory
|
|
43
|
+
+step_registry: StepRegistry
|
|
44
|
+
|
|
45
|
+
+start() dict
|
|
46
|
+
+get_workflow_summary() dict
|
|
47
|
+
+_configure_workflow() None
|
|
48
|
+
+_configure_strategies() dict
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class UploadOrchestrator {
|
|
52
|
+
+context: UploadContext
|
|
53
|
+
+step_registry: StepRegistry
|
|
54
|
+
+strategies: dict
|
|
55
|
+
+executed_steps: list
|
|
56
|
+
+current_step_index: int
|
|
57
|
+
+rollback_executed: bool
|
|
58
|
+
|
|
59
|
+
+execute() dict
|
|
60
|
+
+get_workflow_summary() dict
|
|
61
|
+
+get_executed_steps() list
|
|
62
|
+
+is_rollback_executed() bool
|
|
63
|
+
+_execute_step(step) StepResult
|
|
64
|
+
+_handle_step_failure(step, error) None
|
|
65
|
+
+_rollback_executed_steps() None
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class UploadContext {
|
|
69
|
+
+params: dict
|
|
70
|
+
+run: UploadRun
|
|
71
|
+
+client: Any
|
|
72
|
+
+storage: Any
|
|
73
|
+
+pathlib_cwd: Path
|
|
74
|
+
+metadata: dict
|
|
75
|
+
+file_specifications: dict
|
|
76
|
+
+organized_files: list
|
|
77
|
+
+uploaded_files: list
|
|
78
|
+
+data_units: list
|
|
79
|
+
+metrics: dict
|
|
80
|
+
+errors: list
|
|
81
|
+
+strategies: dict
|
|
82
|
+
+rollback_data: dict
|
|
83
|
+
|
|
84
|
+
+update(result: StepResult) None
|
|
85
|
+
+get_result() dict
|
|
86
|
+
+has_errors() bool
|
|
87
|
+
+update_metrics(category, metrics) None
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class StepRegistry {
|
|
91
|
+
+_steps: list
|
|
92
|
+
+register(step: BaseStep) None
|
|
93
|
+
+get_steps() list
|
|
94
|
+
+get_total_progress_weight() float
|
|
95
|
+
+clear() None
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class StrategyFactory {
|
|
99
|
+
+create_validation_strategy(params, context) BaseValidationStrategy
|
|
100
|
+
+create_file_discovery_strategy(params, context) BaseFileDiscoveryStrategy
|
|
101
|
+
+create_metadata_strategy(params, context) BaseMetadataStrategy
|
|
102
|
+
+create_upload_strategy(params, context) BaseUploadStrategy
|
|
103
|
+
+create_data_unit_strategy(params, context) BaseDataUnitStrategy
|
|
104
|
+
+get_available_strategies() dict
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class BaseStep {
|
|
108
|
+
<<abstract>>
|
|
109
|
+
+name: str
|
|
110
|
+
+progress_weight: float
|
|
111
|
+
+execute(context: UploadContext) StepResult
|
|
112
|
+
+can_skip(context: UploadContext) bool
|
|
113
|
+
+rollback(context: UploadContext) None
|
|
114
|
+
+create_success_result(data) StepResult
|
|
115
|
+
+create_error_result(error) StepResult
|
|
116
|
+
+create_skip_result() StepResult
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class ExcelSecurityConfig {
|
|
120
|
+
+max_file_size_mb: int = 10
|
|
121
|
+
+max_rows: int = 100000
|
|
122
|
+
+max_columns: int = 50
|
|
123
|
+
+max_file_size_bytes: int
|
|
124
|
+
+from_action_config(action_config) ExcelSecurityConfig
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
class StepResult {
|
|
128
|
+
+success: bool
|
|
129
|
+
+data: dict
|
|
130
|
+
+error: str
|
|
131
|
+
+rollback_data: dict
|
|
132
|
+
+skipped: bool
|
|
133
|
+
+original_exception: Exception
|
|
134
|
+
+timestamp: datetime
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
%% 전략 기본 클래스
|
|
138
|
+
class BaseValidationStrategy {
|
|
139
|
+
<<abstract>>
|
|
140
|
+
+validate_files(files, context) bool
|
|
141
|
+
+validate_security(file_path) bool
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class BaseFileDiscoveryStrategy {
|
|
145
|
+
<<abstract>>
|
|
146
|
+
+discover_files(path, context) list
|
|
147
|
+
+organize_files(files, specs, context) list
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
class BaseMetadataStrategy {
|
|
151
|
+
<<abstract>>
|
|
152
|
+
+process_metadata(context) dict
|
|
153
|
+
+extract_metadata(file_path) dict
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
class BaseUploadStrategy {
|
|
157
|
+
<<abstract>>
|
|
158
|
+
+upload_files(files, context) list
|
|
159
|
+
+upload_batch(batch, context) list
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
class BaseDataUnitStrategy {
|
|
163
|
+
<<abstract>>
|
|
164
|
+
+generate_data_units(files, context) list
|
|
165
|
+
+create_data_unit_batch(batch, context) list
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
%% 워크플로우 단계
|
|
169
|
+
class InitializeStep {
|
|
170
|
+
+name = "initialize"
|
|
171
|
+
+progress_weight = 0.05
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class ProcessMetadataStep {
|
|
175
|
+
+name = "process_metadata"
|
|
176
|
+
+progress_weight = 0.05
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
class AnalyzeCollectionStep {
|
|
180
|
+
+name = "analyze_collection"
|
|
181
|
+
+progress_weight = 0.05
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
class OrganizeFilesStep {
|
|
185
|
+
+name = "organize_files"
|
|
186
|
+
+progress_weight = 0.10
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
class ValidateFilesStep {
|
|
190
|
+
+name = "validate_files"
|
|
191
|
+
+progress_weight = 0.05
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class UploadFilesStep {
|
|
195
|
+
+name = "upload_files"
|
|
196
|
+
+progress_weight = 0.30
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
class GenerateDataUnitsStep {
|
|
200
|
+
+name = "generate_data_units"
|
|
201
|
+
+progress_weight = 0.35
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
class CleanupStep {
|
|
205
|
+
+name = "cleanup"
|
|
206
|
+
+progress_weight = 0.05
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
%% 관계
|
|
210
|
+
UploadAction --> UploadRun : 사용
|
|
211
|
+
UploadAction --> UploadParams : 검증
|
|
212
|
+
UploadAction --> ExcelSecurityConfig : 구성
|
|
213
|
+
UploadAction --> UploadOrchestrator : 생성 및 실행
|
|
214
|
+
UploadAction --> StrategyFactory : 전략 구성
|
|
215
|
+
UploadAction --> StepRegistry : 워크플로우 단계 관리
|
|
216
|
+
UploadOrchestrator --> UploadContext : 상태 조정
|
|
217
|
+
UploadOrchestrator --> StepRegistry : 단계 실행
|
|
218
|
+
UploadOrchestrator --> BaseStep : 실행
|
|
219
|
+
BaseStep --> StepResult : 반환
|
|
220
|
+
UploadContext --> StepResult : 업데이트
|
|
221
|
+
StrategyFactory --> BaseValidationStrategy : 생성
|
|
222
|
+
StrategyFactory --> BaseFileDiscoveryStrategy : 생성
|
|
223
|
+
StrategyFactory --> BaseMetadataStrategy : 생성
|
|
224
|
+
StrategyFactory --> BaseUploadStrategy : 생성
|
|
225
|
+
StrategyFactory --> BaseDataUnitStrategy : 생성
|
|
226
|
+
StepRegistry --> BaseStep : 포함
|
|
227
|
+
|
|
228
|
+
%% 단계 상속
|
|
229
|
+
InitializeStep --|> BaseStep : 확장
|
|
230
|
+
ProcessMetadataStep --|> BaseStep : 확장
|
|
231
|
+
AnalyzeCollectionStep --|> BaseStep : 확장
|
|
232
|
+
OrganizeFilesStep --|> BaseStep : 확장
|
|
233
|
+
ValidateFilesStep --|> BaseStep : 확장
|
|
234
|
+
UploadFilesStep --|> BaseStep : 확장
|
|
235
|
+
GenerateDataUnitsStep --|> BaseStep : 확장
|
|
236
|
+
CleanupStep --|> BaseStep : 확장
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 단계 기반 워크플로우 실행
|
|
240
|
+
|
|
241
|
+
리팩토링된 아키텍처는 UploadOrchestrator에 의해 조정되는 단계 기반 워크플로우를 사용합니다. 각 단계는 정의된 책임과 진행 가중치를 가집니다.
|
|
242
|
+
|
|
243
|
+
#### 워크플로우 단계 개요
|
|
244
|
+
|
|
245
|
+
| 단계 | 이름 | 가중치 | 책임 |
|
|
246
|
+
| ---- | ------------------- | ------ | -------------------------------------------- |
|
|
247
|
+
| 1 | Initialize | 5% | 스토리지, pathlib 설정 및 기본 검증 |
|
|
248
|
+
| 2 | Process Metadata | 5% | 제공된 경우 Excel 메타데이터 처리 |
|
|
249
|
+
| 3 | Analyze Collection | 5% | 데이터 컬렉션 사양 검색 및 검증 |
|
|
250
|
+
| 4 | Organize Files | 10% | 유형별 파일 검색 및 구성 |
|
|
251
|
+
| 5 | Validate Files | 5% | 보안 및 내용 검증 |
|
|
252
|
+
| 6 | Upload Files | 30% | 스토리지에 파일 업로드 |
|
|
253
|
+
| 7 | Generate Data Units | 35% | 업로드된 파일에서 데이터 단위 생성 |
|
|
254
|
+
| 8 | Cleanup | 5% | 임시 리소스 정리 |
|
|
255
|
+
|
|
256
|
+
#### 실행 흐름
|
|
257
|
+
|
|
258
|
+
```mermaid
|
|
259
|
+
flowchart TD
|
|
260
|
+
%% 시작
|
|
261
|
+
A["🚀 업로드 액션 시작"] --> B["📋 UploadContext 생성"]
|
|
262
|
+
B --> C["⚙️ 전략 구성"]
|
|
263
|
+
C --> D["📝 워크플로우 단계 등록"]
|
|
264
|
+
D --> E["🎯 UploadOrchestrator 생성"]
|
|
265
|
+
|
|
266
|
+
%% 전략 주입
|
|
267
|
+
E --> F["💉 컨텍스트에 전략 주입"]
|
|
268
|
+
F --> G["📊 진행 상황 추적 초기화"]
|
|
269
|
+
|
|
270
|
+
%% 단계 실행 루프
|
|
271
|
+
G --> H["🔄 단계 실행 루프 시작"]
|
|
272
|
+
H --> I["📍 다음 단계 가져오기"]
|
|
273
|
+
I --> J{"🤔 단계를 건너뛸 수 있는가?"}
|
|
274
|
+
J -->|예| K["⏭️ 단계 건너뛰기"]
|
|
275
|
+
J -->|아니요| L["▶️ 단계 실행"]
|
|
276
|
+
|
|
277
|
+
%% 단계 실행
|
|
278
|
+
L --> M{"✅ 단계 성공?"}
|
|
279
|
+
M -->|예| N["📈 진행 상황 업데이트"]
|
|
280
|
+
M -->|아니요| O["❌ 단계 실패 처리"]
|
|
281
|
+
|
|
282
|
+
%% 성공 경로
|
|
283
|
+
N --> P["💾 단계 결과 저장"]
|
|
284
|
+
P --> Q["📝 실행된 단계에 추가"]
|
|
285
|
+
Q --> R{"🏁 더 많은 단계가 있는가?"}
|
|
286
|
+
R -->|예| I
|
|
287
|
+
R -->|아니요| S["🎉 워크플로우 완료"]
|
|
288
|
+
|
|
289
|
+
%% 건너뛰기 경로
|
|
290
|
+
K --> T["📊 진행 상황 업데이트 (건너뛰기)"]
|
|
291
|
+
T --> R
|
|
292
|
+
|
|
293
|
+
%% 오류 처리
|
|
294
|
+
O --> U["🔙 롤백 프로세스 시작"]
|
|
295
|
+
U --> V["⏪ 실행된 단계 롤백"]
|
|
296
|
+
V --> W["📝 롤백 결과 기록"]
|
|
297
|
+
W --> X["💥 예외 전파"]
|
|
298
|
+
|
|
299
|
+
%% 최종 결과
|
|
300
|
+
S --> Y["📊 최종 메트릭 수집"]
|
|
301
|
+
Y --> Z["📋 결과 요약 생성"]
|
|
302
|
+
Z --> AA["🔄 UploadAction으로 반환"]
|
|
303
|
+
|
|
304
|
+
%% 스타일 적용 - 라이트/다크 모드 호환
|
|
305
|
+
classDef startNode fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000000
|
|
306
|
+
classDef processNode fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000000
|
|
307
|
+
classDef decisionNode fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#000000
|
|
308
|
+
classDef successNode fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000000
|
|
309
|
+
classDef errorNode fill:#ffebee,stroke:#d32f2f,stroke-width:2px,color:#000000
|
|
310
|
+
|
|
311
|
+
class A,B,E startNode
|
|
312
|
+
class C,D,F,G,H,I,L,N,P,Q,T,Y,Z,AA processNode
|
|
313
|
+
class J,M,R decisionNode
|
|
314
|
+
class K,S successNode
|
|
315
|
+
class O,U,V,W,X errorNode
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
#### 전략 통합 지점
|
|
319
|
+
|
|
320
|
+
전략은 특정 지점에서 워크플로우에 주입됩니다:
|
|
321
|
+
|
|
322
|
+
- **검증 전략**: ValidateFilesStep에서 사용
|
|
323
|
+
- **파일 검색 전략**: OrganizeFilesStep에서 사용
|
|
324
|
+
- **메타데이터 전략**: ProcessMetadataStep에서 사용
|
|
325
|
+
- **업로드 전략**: UploadFilesStep에서 사용
|
|
326
|
+
- **데이터 단위 전략**: GenerateDataUnitsStep에서 사용
|
|
327
|
+
|
|
328
|
+
#### 오류 처리 및 롤백
|
|
329
|
+
|
|
330
|
+
오케스트레이터는 자동 롤백 기능을 제공합니다:
|
|
331
|
+
|
|
332
|
+
1. **예외 캡처**: 디버깅을 위해 원래 예외를 보존합니다.
|
|
333
|
+
2. **롤백 실행**: 성공적으로 실행된 모든 단계에 대해 역순으로 `rollback()`을 호출합니다.
|
|
334
|
+
3. **점진적 성능 저하**: 개별 단계 롤백이 실패하더라도 롤백을 계속합니다.
|
|
335
|
+
4. **상태 보존**: 실패 후 분석을 위해 실행 상태를 유지합니다.
|
|
336
|
+
|
|
337
|
+
## 개발 가이드
|
|
338
|
+
|
|
339
|
+
이 섹션은 사용자 정의 전략 및 워크플로우 단계로 업로드 액션을 확장하기 위한 포괄적인 지침을 제공합니다.
|
|
340
|
+
|
|
341
|
+
### 사용자 정의 전략 생성
|
|
342
|
+
|
|
343
|
+
전략은 업로드 프로세스의 다양한 측면에 대한 특정 동작을 구현합니다. 각 전략 유형에는 잘 정의된 인터페이스가 있습니다.
|
|
344
|
+
|
|
345
|
+
#### 사용자 정의 검증 전략
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.strategies.validation.base import BaseValidationStrategy
|
|
349
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.context import UploadContext
|
|
350
|
+
from pathlib import Path
|
|
351
|
+
from typing import List
|
|
352
|
+
|
|
353
|
+
class CustomValidationStrategy(BaseValidationStrategy):
|
|
354
|
+
"""고급 보안 검사를 포함한 사용자 정의 검증 전략."""
|
|
355
|
+
|
|
356
|
+
def validate_files(self, files: List[Path], context: UploadContext) -> bool:
|
|
357
|
+
"""사용자 정의 비즈니스 규칙을 사용하여 파일을 검증합니다."""
|
|
358
|
+
for file_path in files:
|
|
359
|
+
# 사용자 정의 검증 로직
|
|
360
|
+
if not self._validate_custom_rules(file_path):
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
# 보안 검증 호출
|
|
364
|
+
if not self.validate_security(file_path):
|
|
365
|
+
return False
|
|
366
|
+
return True
|
|
367
|
+
|
|
368
|
+
def validate_security(self, file_path: Path) -> bool:
|
|
369
|
+
"""사용자 정의 보안 검증."""
|
|
370
|
+
# 사용자 정의 보안 검사 구현
|
|
371
|
+
if file_path.suffix in ['.exe', '.bat', '.sh']:
|
|
372
|
+
return False
|
|
373
|
+
|
|
374
|
+
# 파일 크기 확인
|
|
375
|
+
if file_path.stat().st_size > 100 * 1024 * 1024: # 100MB
|
|
376
|
+
return False
|
|
377
|
+
|
|
378
|
+
return True
|
|
379
|
+
|
|
380
|
+
def _validate_custom_rules(self, file_path: Path) -> bool:
|
|
381
|
+
"""도메인별 검증 규칙을 구현합니다."""
|
|
382
|
+
# 사용자 정의 비즈니스 로직
|
|
383
|
+
return True
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### 사용자 정의 파일 검색 전략
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.strategies.file_discovery.base import BaseFileDiscoveryStrategy
|
|
390
|
+
from pathlib import Path
|
|
391
|
+
from typing import List, Dict, Any
|
|
392
|
+
|
|
393
|
+
class CustomFileDiscoveryStrategy(BaseFileDiscoveryStrategy):
|
|
394
|
+
"""고급 필터링을 사용한 사용자 정의 파일 검색 전략."""
|
|
395
|
+
|
|
396
|
+
def discover_files(self, path: Path, context: UploadContext) -> List[Path]:
|
|
397
|
+
"""사용자 정의 필터링 규칙으로 파일을 검색합니다."""
|
|
398
|
+
files = []
|
|
399
|
+
|
|
400
|
+
if context.get_param('is_recursive', False):
|
|
401
|
+
files = list(path.rglob('*'))
|
|
402
|
+
else:
|
|
403
|
+
files = list(path.iterdir())
|
|
404
|
+
|
|
405
|
+
# 사용자 정의 필터링 적용
|
|
406
|
+
return self._apply_custom_filters(files, context)
|
|
407
|
+
|
|
408
|
+
def organize_files(self, files: List[Path], specs: Dict[str, Any], context: UploadContext) -> List[Dict[str, Any]]:
|
|
409
|
+
"""사용자 정의 분류를 사용하여 파일을 구성합니다."""
|
|
410
|
+
organized = []
|
|
411
|
+
|
|
412
|
+
for file_path in files:
|
|
413
|
+
if file_path.is_file():
|
|
414
|
+
category = self._determine_category(file_path)
|
|
415
|
+
organized.append({
|
|
416
|
+
'file_path': file_path,
|
|
417
|
+
'category': category,
|
|
418
|
+
'metadata': self._extract_file_metadata(file_path)
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
return organized
|
|
422
|
+
|
|
423
|
+
def _apply_custom_filters(self, files: List[Path], context: UploadContext) -> List[Path]:
|
|
424
|
+
"""도메인별 파일 필터를 적용합니다."""
|
|
425
|
+
filtered = []
|
|
426
|
+
for file_path in files:
|
|
427
|
+
if self._should_include_file(file_path):
|
|
428
|
+
filtered.append(file_path)
|
|
429
|
+
return filtered
|
|
430
|
+
|
|
431
|
+
def _determine_category(self, file_path: Path) -> str:
|
|
432
|
+
"""사용자 정의 로직을 사용하여 파일 카테고리를 결정합니다."""
|
|
433
|
+
ext = file_path.suffix.lower()
|
|
434
|
+
if ext in ['.jpg', '.png', '.gif']:
|
|
435
|
+
return 'images'
|
|
436
|
+
elif ext in ['.pdf', '.doc', '.docx']:
|
|
437
|
+
return 'documents'
|
|
438
|
+
else:
|
|
439
|
+
return 'other'
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
#### 사용자 정의 업로드 전략
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.strategies.upload.base import BaseUploadStrategy
|
|
446
|
+
from typing import List, Dict, Any
|
|
447
|
+
import time
|
|
448
|
+
|
|
449
|
+
class CustomUploadStrategy(BaseUploadStrategy):
|
|
450
|
+
"""고급 재시도 로직을 사용한 사용자 정의 업로드 전략."""
|
|
451
|
+
|
|
452
|
+
def upload_files(self, files: List[Dict[str, Any]], context: UploadContext) -> List[Dict[str, Any]]:
|
|
453
|
+
"""사용자 정의 배치 및 재시도 로직으로 파일을 업로드합니다."""
|
|
454
|
+
uploaded_files = []
|
|
455
|
+
batch_size = context.get_param('upload_batch_size', 10)
|
|
456
|
+
|
|
457
|
+
# 사용자 정의 배치로 처리
|
|
458
|
+
for i in range(0, len(files), batch_size):
|
|
459
|
+
batch = files[i:i + batch_size]
|
|
460
|
+
batch_results = self.upload_batch(batch, context)
|
|
461
|
+
uploaded_files.extend(batch_results)
|
|
462
|
+
|
|
463
|
+
return uploaded_files
|
|
464
|
+
|
|
465
|
+
def upload_batch(self, batch: List[Dict[str, Any]], context: UploadContext) -> List[Dict[str, Any]]:
|
|
466
|
+
"""재시도 로직으로 파일 배치를 업로드합니다."""
|
|
467
|
+
results = []
|
|
468
|
+
|
|
469
|
+
for file_info in batch:
|
|
470
|
+
max_retries = 3
|
|
471
|
+
for attempt in range(max_retries):
|
|
472
|
+
try:
|
|
473
|
+
result = self._upload_single_file(file_info, context)
|
|
474
|
+
results.append(result)
|
|
475
|
+
break
|
|
476
|
+
except Exception as e:
|
|
477
|
+
if attempt == max_retries - 1:
|
|
478
|
+
# 마지막 시도 실패
|
|
479
|
+
context.add_error(f"{file_info['file_path']} 업로드 실패: {e}")
|
|
480
|
+
else:
|
|
481
|
+
# 재시도 전 대기
|
|
482
|
+
time.sleep(2 ** attempt)
|
|
483
|
+
|
|
484
|
+
return results
|
|
485
|
+
|
|
486
|
+
def _upload_single_file(self, file_info: Dict[str, Any], context: UploadContext) -> Dict[str, Any]:
|
|
487
|
+
"""사용자 정의 로직으로 단일 파일을 업로드합니다."""
|
|
488
|
+
file_path = file_info['file_path']
|
|
489
|
+
storage = context.storage
|
|
490
|
+
|
|
491
|
+
# 여기에 사용자 정의 업로드 로직
|
|
492
|
+
uploaded_file = {
|
|
493
|
+
'file_path': str(file_path),
|
|
494
|
+
'storage_path': f"uploads/{file_path.name}",
|
|
495
|
+
'size': file_path.stat().st_size,
|
|
496
|
+
'checksum': self._calculate_checksum(file_path)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return uploaded_file
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### 사용자 정의 워크플로우 단계 생성
|
|
503
|
+
|
|
504
|
+
사용자 정의 워크플로우 단계는 기본 단계 클래스를 확장하고 필요한 인터페이스를 구현합니다.
|
|
505
|
+
|
|
506
|
+
#### 사용자 정의 처리 단계
|
|
507
|
+
|
|
508
|
+
```python
|
|
509
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.steps.base import BaseStep
|
|
510
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.context import UploadContext, StepResult
|
|
511
|
+
from pathlib import Path
|
|
512
|
+
from typing import List, Dict
|
|
513
|
+
from datetime import datetime
|
|
514
|
+
|
|
515
|
+
class CustomProcessingStep(BaseStep):
|
|
516
|
+
"""특수 파일 처리를 위한 사용자 정의 처리 단계."""
|
|
517
|
+
|
|
518
|
+
@property
|
|
519
|
+
def name(self) -> str:
|
|
520
|
+
return 'custom_processing'
|
|
521
|
+
|
|
522
|
+
@property
|
|
523
|
+
def progress_weight(self) -> float:
|
|
524
|
+
return 0.15 # 전체 워크플로우의 15%
|
|
525
|
+
|
|
526
|
+
def execute(self, context: UploadContext) -> StepResult:
|
|
527
|
+
"""사용자 정의 처리 로직을 실행합니다."""
|
|
528
|
+
try:
|
|
529
|
+
# 사용자 정의 처리 로직
|
|
530
|
+
processed_files = self._process_files(context)
|
|
531
|
+
|
|
532
|
+
# 결과로 컨텍스트 업데이트
|
|
533
|
+
return self.create_success_result({
|
|
534
|
+
'processed_files': processed_files,
|
|
535
|
+
'processing_stats': self._get_processing_stats()
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
except Exception as e:
|
|
539
|
+
return self.create_error_result(f'사용자 정의 처리 실패: {str(e)}')
|
|
540
|
+
|
|
541
|
+
def can_skip(self, context: UploadContext) -> bool:
|
|
542
|
+
"""단계가 건너뛸 수 있는지 결정합니다."""
|
|
543
|
+
# 처리할 파일이 없으면 건너뛰기
|
|
544
|
+
return len(context.organized_files) == 0
|
|
545
|
+
|
|
546
|
+
def rollback(self, context: UploadContext) -> None:
|
|
547
|
+
"""사용자 정의 처리 작업을 롤백합니다."""
|
|
548
|
+
# 처리 중에 생성된 모든 리소스 정리
|
|
549
|
+
self._cleanup_processing_resources(context)
|
|
550
|
+
|
|
551
|
+
def _process_files(self, context: UploadContext) -> List[Dict]:
|
|
552
|
+
"""사용자 정의 파일 처리를 구현합니다."""
|
|
553
|
+
processed = []
|
|
554
|
+
|
|
555
|
+
for file_info in context.organized_files:
|
|
556
|
+
# 사용자 정의 처리 로직
|
|
557
|
+
result = self._process_single_file(file_info)
|
|
558
|
+
processed.append(result)
|
|
559
|
+
|
|
560
|
+
return processed
|
|
561
|
+
|
|
562
|
+
def _process_single_file(self, file_info: Dict) -> Dict:
|
|
563
|
+
"""단일 파일을 처리합니다."""
|
|
564
|
+
return {
|
|
565
|
+
'original': file_info,
|
|
566
|
+
'processed': True,
|
|
567
|
+
'timestamp': datetime.now()
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
def _get_processing_stats(self) -> Dict:
|
|
571
|
+
"""처리 통계를 가져옵니다."""
|
|
572
|
+
return {}
|
|
573
|
+
|
|
574
|
+
def _cleanup_processing_resources(self, context: UploadContext) -> None:
|
|
575
|
+
"""처리 리소스를 정리합니다."""
|
|
576
|
+
pass
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### 전략 팩토리 확장
|
|
580
|
+
|
|
581
|
+
사용자 정의 전략을 사용 가능하게 하려면 StrategyFactory를 확장합니다:
|
|
582
|
+
|
|
583
|
+
```python
|
|
584
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.factory import StrategyFactory
|
|
585
|
+
from typing import Dict
|
|
586
|
+
|
|
587
|
+
class CustomStrategyFactory(StrategyFactory):
|
|
588
|
+
"""사용자 정의 전략을 포함한 확장된 팩토리."""
|
|
589
|
+
|
|
590
|
+
def create_validation_strategy(self, params: Dict, context=None):
|
|
591
|
+
"""사용자 정의 옵션으로 검증 전략을 생성합니다."""
|
|
592
|
+
validation_type = params.get('custom_validation_type', 'default')
|
|
593
|
+
|
|
594
|
+
if validation_type == 'strict':
|
|
595
|
+
return CustomValidationStrategy()
|
|
596
|
+
else:
|
|
597
|
+
return super().create_validation_strategy(params, context)
|
|
598
|
+
|
|
599
|
+
def create_file_discovery_strategy(self, params: Dict, context=None):
|
|
600
|
+
"""사용자 정의 옵션으로 파일 검색 전략을 생성합니다."""
|
|
601
|
+
discovery_mode = params.get('discovery_mode', 'default')
|
|
602
|
+
|
|
603
|
+
if discovery_mode == 'advanced':
|
|
604
|
+
return CustomFileDiscoveryStrategy()
|
|
605
|
+
else:
|
|
606
|
+
return super().create_file_discovery_strategy(params, context)
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### 사용자 정의 업로드 액션
|
|
610
|
+
|
|
611
|
+
포괄적인 사용자 정의를 위해 UploadAction 자체를 확장합니다:
|
|
612
|
+
|
|
613
|
+
```python
|
|
614
|
+
from synapse_sdk.plugins.categories.upload.actions.upload.action import UploadAction
|
|
615
|
+
from synapse_sdk.plugins.categories.decorators import register_action
|
|
616
|
+
from typing import Dict, Any
|
|
617
|
+
|
|
618
|
+
@register_action
|
|
619
|
+
class CustomUploadAction(UploadAction):
|
|
620
|
+
"""확장된 워크플로우를 가진 사용자 정의 업로드 액션."""
|
|
621
|
+
|
|
622
|
+
name = 'custom_upload'
|
|
623
|
+
|
|
624
|
+
def __init__(self, *args, **kwargs):
|
|
625
|
+
super().__init__(*args, **kwargs)
|
|
626
|
+
# 사용자 정의 전략 팩토리 사용
|
|
627
|
+
self.strategy_factory = CustomStrategyFactory()
|
|
628
|
+
|
|
629
|
+
def _configure_workflow(self) -> None:
|
|
630
|
+
"""추가 단계로 사용자 정의 워크플로우를 구성합니다."""
|
|
631
|
+
# 표준 단계 등록
|
|
632
|
+
super()._configure_workflow()
|
|
633
|
+
|
|
634
|
+
# 사용자 정의 처리 단계 추가
|
|
635
|
+
self.step_registry.register(CustomProcessingStep())
|
|
636
|
+
|
|
637
|
+
def _configure_strategies(self, context=None) -> Dict[str, Any]:
|
|
638
|
+
"""사용자 정의 매개변수로 전략을 구성합니다."""
|
|
639
|
+
strategies = super()._configure_strategies(context)
|
|
640
|
+
|
|
641
|
+
# 사용자 정의 전략 추가
|
|
642
|
+
strategies['custom_processing'] = self._create_custom_processing_strategy()
|
|
643
|
+
|
|
644
|
+
return strategies
|
|
645
|
+
|
|
646
|
+
def _create_custom_processing_strategy(self):
|
|
647
|
+
"""사용자 정의 처리 전략을 생성합니다."""
|
|
648
|
+
return CustomProcessingStrategy(self.params)
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### 사용자 정의 구성 요소 테스트
|
|
652
|
+
|
|
653
|
+
#### 사용자 정의 전략 테스트
|
|
654
|
+
|
|
655
|
+
```python
|
|
656
|
+
import pytest
|
|
657
|
+
from unittest.mock import Mock
|
|
658
|
+
from pathlib import Path
|
|
659
|
+
|
|
660
|
+
class TestCustomValidationStrategy:
|
|
661
|
+
|
|
662
|
+
def setup_method(self):
|
|
663
|
+
self.strategy = CustomValidationStrategy()
|
|
664
|
+
self.context = Mock()
|
|
665
|
+
|
|
666
|
+
def test_validate_files_success(self):
|
|
667
|
+
"""성공적인 파일 검증 테스트."""
|
|
668
|
+
files = [Path('/test/file1.txt'), Path('/test/file2.jpg')]
|
|
669
|
+
result = self.strategy.validate_files(files, self.context)
|
|
670
|
+
assert result is True
|
|
671
|
+
|
|
672
|
+
def test_validate_files_security_failure(self):
|
|
673
|
+
"""보안상의 이유로 검증 실패 테스트."""
|
|
674
|
+
files = [Path('/test/malware.exe')]
|
|
675
|
+
result = self.strategy.validate_files(files, self.context)
|
|
676
|
+
assert result is False
|
|
677
|
+
|
|
678
|
+
def test_validate_large_file_failure(self):
|
|
679
|
+
"""큰 파일에 대한 검증 실패 테스트."""
|
|
680
|
+
# 큰 크기를 반환하도록 파일 상태 모의
|
|
681
|
+
large_file = Mock(spec=Path)
|
|
682
|
+
large_file.suffix = '.txt'
|
|
683
|
+
large_file.stat.return_value.st_size = 200 * 1024 * 1024 # 200MB
|
|
684
|
+
|
|
685
|
+
result = self.strategy.validate_security(large_file)
|
|
686
|
+
assert result is False
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
#### 사용자 정의 단계 테스트
|
|
690
|
+
|
|
691
|
+
```python
|
|
692
|
+
class TestCustomProcessingStep:
|
|
693
|
+
|
|
694
|
+
def setup_method(self):
|
|
695
|
+
self.step = CustomProcessingStep()
|
|
696
|
+
self.context = Mock()
|
|
697
|
+
self.context.organized_files = [
|
|
698
|
+
{'file_path': '/test/file1.txt'},
|
|
699
|
+
{'file_path': '/test/file2.jpg'}
|
|
700
|
+
]
|
|
701
|
+
|
|
702
|
+
def test_execute_success(self):
|
|
703
|
+
"""성공적인 단계 실행 테스트."""
|
|
704
|
+
result = self.step.execute(self.context)
|
|
705
|
+
|
|
706
|
+
assert result.success is True
|
|
707
|
+
assert 'processed_files' in result.data
|
|
708
|
+
assert len(result.data['processed_files']) == 2
|
|
709
|
+
|
|
710
|
+
def test_can_skip_with_no_files(self):
|
|
711
|
+
"""단계 건너뛰기 로직 테스트."""
|
|
712
|
+
self.context.organized_files = []
|
|
713
|
+
assert self.step.can_skip(self.context) is True
|
|
714
|
+
|
|
715
|
+
def test_rollback_cleanup(self):
|
|
716
|
+
"""롤백 정리 테스트."""
|
|
717
|
+
# 예외가 발생하지 않아야 함
|
|
718
|
+
self.step.rollback(self.context)
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
## API 참조
|
|
722
|
+
|
|
723
|
+
### 핵심 구성 요소
|
|
724
|
+
|
|
725
|
+
#### UploadAction
|
|
726
|
+
|
|
727
|
+
전략 및 퍼사드 패턴을 구현하는 주요 업로드 액션 클래스.
|
|
728
|
+
|
|
729
|
+
**클래스 속성:**
|
|
730
|
+
|
|
731
|
+
- `name = 'upload'` - 액션 식별자
|
|
732
|
+
- `category = PluginCategory.UPLOAD` - 플러그인 카테고리
|
|
733
|
+
- `method = RunMethod.JOB` - 실행 방법
|
|
734
|
+
- `run_class = UploadRun` - 전문화된 실행 관리
|
|
735
|
+
- `params_model = UploadParams` - 매개변수 검증 모델
|
|
736
|
+
- `strategy_factory: StrategyFactory` - 전략 구현 생성
|
|
737
|
+
- `step_registry: StepRegistry` - 워크플로우 단계 관리
|
|
738
|
+
|
|
739
|
+
**주요 메서드:**
|
|
740
|
+
|
|
741
|
+
- `start() -> Dict[str, Any]` - 조정된 업로드 워크플로우 실행
|
|
742
|
+
- `get_workflow_summary() -> Dict[str, Any]` - 구성된 워크플로우 요약 가져오기
|
|
743
|
+
- `_configure_workflow() -> None` - 워크플로우 단계 등록
|
|
744
|
+
- `_configure_strategies(context=None) -> Dict[str, Any]` - 전략 인스턴스 생성
|
|
745
|
+
|
|
746
|
+
#### UploadOrchestrator
|
|
747
|
+
|
|
748
|
+
자동 롤백 기능으로 전체 업로드 워크플로우를 조정하는 퍼사드 구성 요소.
|
|
749
|
+
|
|
750
|
+
**속성:**
|
|
751
|
+
|
|
752
|
+
- `context: UploadContext` - 공유 상태
|
|
753
|
+
- `step_registry: StepRegistry` - 워크플로우 단계
|
|
754
|
+
- `strategies: Dict[str, Any]` - 전략 구현
|
|
755
|
+
- `executed_steps: List[BaseStep]` - 성공적으로 실행된 단계
|
|
756
|
+
- `rollback_executed: bool` - 롤백 수행 여부
|
|
757
|
+
|
|
758
|
+
**주요 메서드:**
|
|
759
|
+
|
|
760
|
+
- `execute() -> Dict[str, Any]` - 전체 워크플로우 실행
|
|
761
|
+
- `get_workflow_summary() -> Dict[str, Any]` - 실행 요약 가져오기
|
|
762
|
+
- `_execute_step(step: BaseStep) -> StepResult` - 개별 단계 실행
|
|
763
|
+
- `_rollback_executed_steps() -> None` - 역순으로 롤백
|
|
764
|
+
|
|
765
|
+
#### UploadContext
|
|
766
|
+
|
|
767
|
+
워크플로우 구성 요소 간의 공유 상태를 유지하는 컨텍스트 객체.
|
|
768
|
+
|
|
769
|
+
**상태 속성:**
|
|
770
|
+
|
|
771
|
+
- `params: Dict` - 업로드 매개변수
|
|
772
|
+
- `storage: Any` - 스토리지 구성
|
|
773
|
+
- `metadata: Dict[str, Dict[str, Any]]` - 파일 메타데이터
|
|
774
|
+
- `file_specifications: Dict[str, Any]` - 데이터 컬렉션 사양
|
|
775
|
+
- `organized_files: List[Dict[str, Any]]` - 구성된 파일
|
|
776
|
+
- `uploaded_files: List[Dict[str, Any]]` - 업로드된 파일
|
|
777
|
+
- `data_units: List[Dict[str, Any]]` - 생성된 데이터 단위
|
|
778
|
+
|
|
779
|
+
**주요 메서드:**
|
|
780
|
+
|
|
781
|
+
- `update(result: StepResult) -> None` - 단계 결과로 업데이트
|
|
782
|
+
- `get_result() -> Dict[str, Any]` - 최종 결과 생성
|
|
783
|
+
- `has_errors() -> bool` - 오류 확인
|
|
784
|
+
- `update_metrics(category: str, metrics: Dict) -> None` - 메트릭 업데이트
|
|
785
|
+
|
|
786
|
+
### 워크플로우 단계
|
|
787
|
+
|
|
788
|
+
#### BaseStep (추상)
|
|
789
|
+
|
|
790
|
+
모든 워크플로우 단계의 기본 클래스.
|
|
791
|
+
|
|
792
|
+
**추상 속성:**
|
|
793
|
+
|
|
794
|
+
- `name: str` - 고유한 단계 식별자
|
|
795
|
+
- `progress_weight: float` - 진행률 계산을 위한 가중치
|
|
796
|
+
|
|
797
|
+
**추상 메서드:**
|
|
798
|
+
|
|
799
|
+
- `execute(context: UploadContext) -> StepResult` - 단계 로직 실행
|
|
800
|
+
- `can_skip(context: UploadContext) -> bool` - 건너뛸 수 있는지 결정
|
|
801
|
+
- `rollback(context: UploadContext) -> None` - 작업 롤백
|
|
802
|
+
|
|
803
|
+
**유틸리티 메서드:**
|
|
804
|
+
|
|
805
|
+
- `create_success_result(data: Dict = None) -> StepResult`
|
|
806
|
+
- `create_error_result(error: str, exception: Exception = None) -> StepResult`
|
|
807
|
+
- `create_skip_result() -> StepResult`
|
|
808
|
+
|
|
809
|
+
#### 구체적인 단계
|
|
810
|
+
|
|
811
|
+
**InitializeStep** (`name: "initialize"`, `weight: 0.05`)
|
|
812
|
+
|
|
813
|
+
- 스토리지 및 작업 디렉토리 설정
|
|
814
|
+
|
|
815
|
+
**ProcessMetadataStep** (`name: "process_metadata"`, `weight: 0.05`)
|
|
816
|
+
|
|
817
|
+
- 제공된 경우 Excel 메타데이터 처리
|
|
818
|
+
|
|
819
|
+
**AnalyzeCollectionStep** (`name: "analyze_collection"`, `weight: 0.05`)
|
|
820
|
+
|
|
821
|
+
- 데이터 컬렉션 사양 검색
|
|
822
|
+
|
|
823
|
+
**OrganizeFilesStep** (`name: "organize_files"`, `weight: 0.10`)
|
|
824
|
+
|
|
825
|
+
- 유형별 파일 검색 및 구성
|
|
826
|
+
|
|
827
|
+
**ValidateFilesStep** (`name: "validate_files"`, `weight: 0.05`)
|
|
828
|
+
|
|
829
|
+
- 검증 전략을 사용하여 파일 검증
|
|
830
|
+
|
|
831
|
+
**UploadFilesStep** (`name: "upload_files"`, `weight: 0.30`)
|
|
832
|
+
|
|
833
|
+
- 업로드 전략을 사용하여 파일 업로드
|
|
834
|
+
|
|
835
|
+
**GenerateDataUnitsStep** (`name: "generate_data_units"`, `weight: 0.35`)
|
|
836
|
+
|
|
837
|
+
- 데이터 단위 전략을 사용하여 데이터 단위 생성
|
|
838
|
+
|
|
839
|
+
**CleanupStep** (`name: "cleanup"`, `weight: 0.05`)
|
|
840
|
+
|
|
841
|
+
- 임시 리소스 정리
|
|
842
|
+
|
|
843
|
+
### 전략 기본 클래스
|
|
844
|
+
|
|
845
|
+
#### BaseValidationStrategy (추상)
|
|
846
|
+
|
|
847
|
+
**추상 메서드:**
|
|
848
|
+
|
|
849
|
+
- `validate_files(files: List[Path], context: UploadContext) -> bool`
|
|
850
|
+
- `validate_security(file_path: Path) -> bool`
|
|
851
|
+
|
|
852
|
+
#### BaseFileDiscoveryStrategy (추상)
|
|
853
|
+
|
|
854
|
+
**추상 메서드:**
|
|
855
|
+
|
|
856
|
+
- `discover_files(path: Path, context: UploadContext) -> List[Path]`
|
|
857
|
+
- `organize_files(files: List[Path], specs: Dict, context: UploadContext) -> List[Dict]`
|
|
858
|
+
|
|
859
|
+
#### BaseMetadataStrategy (추상)
|
|
860
|
+
|
|
861
|
+
**추상 메서드:**
|
|
862
|
+
|
|
863
|
+
- `process_metadata(context: UploadContext) -> Dict[str, Any]`
|
|
864
|
+
- `extract_metadata(file_path: Path) -> Dict[str, Any]`
|
|
865
|
+
|
|
866
|
+
#### BaseUploadStrategy (추상)
|
|
867
|
+
|
|
868
|
+
**추상 메서드:**
|
|
869
|
+
|
|
870
|
+
- `upload_files(files: List[Dict], context: UploadContext) -> List[Dict]`
|
|
871
|
+
- `upload_batch(batch: List[Dict], context: UploadContext) -> List[Dict]`
|
|
872
|
+
|
|
873
|
+
#### BaseDataUnitStrategy (추상)
|
|
874
|
+
|
|
875
|
+
**추상 메서드:**
|
|
876
|
+
|
|
877
|
+
- `generate_data_units(files: List[Dict], context: UploadContext) -> List[Dict]`
|
|
878
|
+
- `create_data_unit_batch(batch: List[Dict], context: UploadContext) -> List[Dict]`
|
|
879
|
+
|
|
880
|
+
## 모범 사례
|
|
881
|
+
|
|
882
|
+
### 아키텍처 패턴
|
|
883
|
+
|
|
884
|
+
1. **전략 선택**: 요구 사항에 따라 적절한 전략 선택
|
|
885
|
+
2. **단계 순서**: 논리적 단계 종속성 유지
|
|
886
|
+
3. **컨텍스트 관리**: 상태 공유를 위해 UploadContext 활용
|
|
887
|
+
|
|
888
|
+
### 성능 최적화
|
|
889
|
+
|
|
890
|
+
1. **배치 처리**: 최적의 배치 크기 구성
|
|
891
|
+
2. **비동기 작업**: I/O 바운드 작업에 비동기 활성화
|
|
892
|
+
3. **메모리 관리**: 사용자 정의 전략에서 메모리 사용량 모니터링
|
|
893
|
+
|
|
894
|
+
### 보안 고려 사항
|
|
895
|
+
|
|
896
|
+
1. **입력 검증**: 모든 매개변수 및 파일 경로 검증
|
|
897
|
+
2. **파일 내용 보안**: 내용 기반 검사 구현
|
|
898
|
+
3. **경로 살균**: 모든 경로 검증 및 살균
|
|
899
|
+
|
|
900
|
+
### 오류 처리 및 복구
|
|
901
|
+
|
|
902
|
+
1. **점진적 성능 저하**: 부분적 실패 시나리오를 위한 설계
|
|
903
|
+
2. **롤백 설계**: 포괄적인 롤백 전략 구현
|
|
904
|
+
3. **상세 로깅**: 디버깅을 위한 구조화된 로깅 사용
|
|
905
|
+
|
|
906
|
+
## 레거시로부터의 마이그레이션
|
|
907
|
+
|
|
908
|
+
업로드 액션은 100% 하위 호환성을 유지합니다. 기존 코드는 변경 없이 계속 작동합니다.
|
|
909
|
+
|
|
910
|
+
### 주요 변경 사항
|
|
911
|
+
|
|
912
|
+
**이전 (레거시):**
|
|
913
|
+
|
|
914
|
+
- 단일 900+ 라인 액션 클래스
|
|
915
|
+
- 하드 코딩된 동작
|
|
916
|
+
- 확장성 없음
|
|
917
|
+
|
|
918
|
+
**이후 (리팩토링):**
|
|
919
|
+
|
|
920
|
+
- 8개의 워크플로우 단계로 깔끔하게 분리
|
|
921
|
+
- 플러그형 전략
|
|
922
|
+
- 자동 롤백
|
|
923
|
+
|
|
924
|
+
### 이점
|
|
925
|
+
|
|
926
|
+
- 자동 롤백으로 더 나은 오류 처리
|
|
927
|
+
- 상세한 진행 상황 추적
|
|
928
|
+
- 사용자 정의 전략으로 확장성
|
|
929
|
+
- 더 나은 테스트 용이성
|
|
930
|
+
|
|
931
|
+
## 최근 아키텍처 개선사항
|
|
932
|
+
|
|
933
|
+
### 업로드 로직 리팩토링
|
|
934
|
+
|
|
935
|
+
**문제**: 원래 구현에서 검증 단계가 파일 처리/필터링 작업을 수행하는 불분명한 관심사 분리가 있었습니다.
|
|
936
|
+
|
|
937
|
+
**해결책**: 파일 처리 로직을 검증 단계에서 업로드 전략으로 이동하여 적절한 관심사 분리를 보장하도록 리팩토링했습니다.
|
|
938
|
+
|
|
939
|
+
**변경사항**:
|
|
940
|
+
|
|
941
|
+
1. **ValidateFilesStep** - 이제 사양 기반 검증만 수행
|
|
942
|
+
2. **UploadStrategy** - 업로드 전에 플러그인 파일 처리를 처리하기 위해 `_process_files_with_uploader()` 통합
|
|
943
|
+
3. **이점**: 명확한 워크플로우, 더 나은 유지보수성, 하위 호환성 유지
|
|
944
|
+
|
|
945
|
+
## 참조
|
|
946
|
+
|
|
947
|
+
- [업로드 플러그인 개요](./upload-plugin-overview.md) - 사용자 가이드 및 구성 참조
|
|
948
|
+
- [BaseUploader 템플릿 가이드](./upload-plugin-template.md) - BaseUploader 템플릿을 사용한 플러그인 개발
|