pycharter 0.0.24__py3-none-any.whl → 0.0.26__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.
- pycharter/__init__.py +6 -0
- pycharter/api/README.md +340 -0
- {api → pycharter/api}/__init__.py +1 -1
- {api → pycharter/api}/dependencies/__init__.py +2 -2
- pycharter/api/dependencies/auth.py +158 -0
- {api → pycharter/api}/main.py +32 -4
- {api → pycharter/api}/models/__init__.py +4 -4
- pycharter/api/models/etl.py +66 -0
- {api → pycharter/api}/routes/v1/__init__.py +5 -1
- pycharter/api/routes/v1/auth.py +97 -0
- {api → pycharter/api}/routes/v1/contracts.py +14 -12
- {api → pycharter/api}/routes/v1/docs.py +2 -2
- pycharter/api/routes/v1/etl.py +131 -0
- {api → pycharter/api}/routes/v1/evolution.py +2 -2
- {api → pycharter/api}/routes/v1/metadata.py +5 -5
- {api → pycharter/api}/routes/v1/quality.py +3 -3
- {api → pycharter/api}/routes/v1/schemas.py +1 -1
- {api → pycharter/api}/routes/v1/settings.py +1 -1
- {api → pycharter/api}/routes/v1/tracking.py +1 -1
- {api → pycharter/api}/routes/v1/validation.py +2 -2
- {api → pycharter/api}/routes/v1/validation_jobs.py +3 -3
- pycharter/cli.py +9 -11
- pycharter/config.py +69 -0
- pycharter/contract_builder/builder.py +32 -37
- pycharter/data/seed/compliance_frameworks.yaml +22 -0
- pycharter/data/seed/contracts.yaml +130 -0
- pycharter/data/seed/data_feeds.yaml +22 -0
- pycharter/data/seed/domains.yaml +13 -0
- pycharter/data/seed/environments.yaml +19 -0
- pycharter/data/seed/owners.yaml +21 -0
- pycharter/data/seed/systems.yaml +13 -0
- pycharter/data/seed/tags.yaml +25 -0
- pycharter/data/templates/contract/README.md +161 -0
- pycharter/data/templates/contract/template_contract.yaml +37 -0
- pycharter/data/templates/etl/README.md +1 -1
- pycharter/data/templates/etl/extract_with_validation.yaml +86 -0
- pycharter/data/templates/etl/load_with_validation.yaml +111 -0
- pycharter/data/templates/etl/settings.yaml +55 -0
- pycharter/db/README.md +179 -0
- pycharter/db/cli.py +126 -4
- pycharter/db/migrations/versions/20260122000000_change_artifact_unique_constraints_to_title_version.py +2 -2
- pycharter/db/schemas/README.md +96 -0
- pycharter/etl_generator/ASYNC_AND_EXECUTION.md +91 -0
- pycharter/etl_generator/INTERFACES.md +142 -0
- pycharter/etl_generator/README.md +271 -0
- pycharter/etl_generator/TRANSFORMATION_GUIDE.md +452 -0
- pycharter/etl_generator/__init__.py +47 -11
- pycharter/etl_generator/config_models.py +673 -0
- pycharter/etl_generator/config_validator.py +133 -157
- pycharter/etl_generator/context.py +3 -0
- pycharter/etl_generator/database.py +5 -1
- pycharter/etl_generator/extractors/__init__.py +4 -2
- pycharter/etl_generator/extractors/cloud_storage.py +9 -9
- pycharter/etl_generator/extractors/database.py +2 -2
- pycharter/etl_generator/extractors/factory.py +15 -33
- pycharter/etl_generator/extractors/file.py +2 -2
- pycharter/etl_generator/extractors/http.py +2 -2
- pycharter/etl_generator/extractors/mongodb.py +393 -0
- pycharter/etl_generator/extractors/streaming.py +2 -2
- pycharter/etl_generator/loaders/__init__.py +15 -9
- pycharter/etl_generator/loaders/{cloud_storage_loader.py → cloud_storage.py} +95 -2
- pycharter/etl_generator/loaders/factory.py +16 -29
- pycharter/etl_generator/loaders/file.py +135 -1
- pycharter/etl_generator/loaders/mongodb.py +416 -0
- pycharter/etl_generator/pipeline.py +283 -164
- pycharter/etl_generator/result.py +16 -0
- pycharter/etl_generator/schemas/__init__.py +71 -42
- pycharter/etl_generator/transformers/config.py +3 -2
- pycharter/etl_generator/transformers/simple_operations.py +57 -4
- pycharter/etl_generator/validation.py +551 -0
- pycharter/metadata_store/README.md +229 -0
- pycharter/quality/README.md +235 -0
- pycharter/runtime_validator/__init__.py +7 -0
- pycharter/runtime_validator/utils.py +33 -0
- pycharter/runtime_validator/validator.py +13 -10
- pycharter/ui/.eslintrc.json +4 -0
- pycharter/ui/README.md +186 -0
- {ui → pycharter/ui}/__init__.py +3 -3
- pycharter/ui/components.json +17 -0
- pycharter/ui/package-lock.json +6617 -0
- pycharter/ui/package.json +37 -0
- {ui → pycharter/ui}/server.py +7 -8
- pycharter/ui/static/404/index.html +1 -0
- pycharter/ui/static/404.html +1 -0
- pycharter/ui/static/__next.__PAGE__.txt +10 -0
- pycharter/ui/static/__next._full.txt +30 -0
- pycharter/ui/static/__next._head.txt +7 -0
- pycharter/ui/static/__next._index.txt +9 -0
- pycharter/ui/static/__next._tree.txt +2 -0
- pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/_next/static/chunks/0fc1f70b787b8845.js +1 -0
- pycharter/ui/static/_next/static/chunks/17bb8075d7b75663.css +1 -0
- pycharter/ui/static/_next/static/chunks/381932864dcbfdb8.js +1 -0
- pycharter/ui/static/_next/static/chunks/4c951b8e4507e2b3.js +1 -0
- pycharter/ui/static/_next/static/chunks/68b87a6f65abd3ed.js +1 -0
- pycharter/ui/static/_next/static/chunks/78572617b8fae189.js +1 -0
- pycharter/ui/static/_next/static/chunks/8b7be2803e3fe184.js +1 -0
- pycharter/ui/static/_next/static/chunks/a8e529fd1e67f121.js +1 -0
- pycharter/ui/static/_next/static/chunks/c35d998f80be3ff5.js +1 -0
- pycharter/ui/static/_next/static/chunks/e453aa5d01c32c17.js +1 -0
- pycharter/ui/static/_next/static/chunks/f2d240eb057f898a.js +970 -0
- pycharter/ui/static/_next/static/chunks/f7722448f6040846.js +1 -0
- pycharter/ui/static/_not-found/__next._full.txt +17 -0
- pycharter/ui/static/_not-found/__next._head.txt +7 -0
- pycharter/ui/static/_not-found/__next._index.txt +9 -0
- pycharter/ui/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- pycharter/ui/static/_not-found/__next._not-found.txt +4 -0
- pycharter/ui/static/_not-found/__next._tree.txt +2 -0
- pycharter/ui/static/_not-found/index.html +1 -0
- pycharter/ui/static/_not-found/index.txt +17 -0
- pycharter/ui/static/contracts/__next._full.txt +21 -0
- pycharter/ui/static/contracts/__next._head.txt +7 -0
- pycharter/ui/static/contracts/__next._index.txt +9 -0
- pycharter/ui/static/contracts/__next._tree.txt +2 -0
- pycharter/ui/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- pycharter/ui/static/contracts/__next.contracts.txt +4 -0
- pycharter/ui/static/contracts/index.html +1 -0
- pycharter/ui/static/contracts/index.txt +21 -0
- pycharter/ui/static/documentation/__next._full.txt +21 -0
- pycharter/ui/static/documentation/__next._head.txt +7 -0
- pycharter/ui/static/documentation/__next._index.txt +9 -0
- pycharter/ui/static/documentation/__next._tree.txt +2 -0
- pycharter/ui/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- pycharter/ui/static/documentation/__next.documentation.txt +4 -0
- pycharter/ui/static/documentation/index.html +93 -0
- pycharter/ui/static/documentation/index.txt +21 -0
- pycharter/ui/static/etl/__next._full.txt +21 -0
- pycharter/ui/static/etl/__next._head.txt +7 -0
- pycharter/ui/static/etl/__next._index.txt +9 -0
- pycharter/ui/static/etl/__next._tree.txt +2 -0
- pycharter/ui/static/etl/__next.etl.__PAGE__.txt +9 -0
- pycharter/ui/static/etl/__next.etl.txt +4 -0
- pycharter/ui/static/etl/index.html +2 -0
- pycharter/ui/static/etl/index.txt +21 -0
- pycharter/ui/static/index.html +1 -0
- pycharter/ui/static/index.txt +30 -0
- pycharter/ui/static/metadata/__next._full.txt +21 -0
- pycharter/ui/static/metadata/__next._head.txt +7 -0
- pycharter/ui/static/metadata/__next._index.txt +9 -0
- pycharter/ui/static/metadata/__next._tree.txt +2 -0
- pycharter/ui/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- pycharter/ui/static/metadata/__next.metadata.txt +4 -0
- pycharter/ui/static/metadata/index.html +1 -0
- pycharter/ui/static/metadata/index.txt +21 -0
- pycharter/ui/static/quality/__next._full.txt +21 -0
- pycharter/ui/static/quality/__next._head.txt +7 -0
- pycharter/ui/static/quality/__next._index.txt +9 -0
- pycharter/ui/static/quality/__next._tree.txt +2 -0
- pycharter/ui/static/quality/__next.quality.__PAGE__.txt +9 -0
- pycharter/ui/static/quality/__next.quality.txt +4 -0
- pycharter/ui/static/quality/index.html +2 -0
- pycharter/ui/static/quality/index.txt +21 -0
- pycharter/ui/static/rules/__next._full.txt +21 -0
- pycharter/ui/static/rules/__next._head.txt +7 -0
- pycharter/ui/static/rules/__next._index.txt +9 -0
- pycharter/ui/static/rules/__next._tree.txt +2 -0
- pycharter/ui/static/rules/__next.rules.__PAGE__.txt +9 -0
- pycharter/ui/static/rules/__next.rules.txt +4 -0
- pycharter/ui/static/rules/index.html +1 -0
- pycharter/ui/static/rules/index.txt +21 -0
- pycharter/ui/static/schemas/__next._full.txt +21 -0
- pycharter/ui/static/schemas/__next._head.txt +7 -0
- pycharter/ui/static/schemas/__next._index.txt +9 -0
- pycharter/ui/static/schemas/__next._tree.txt +2 -0
- pycharter/ui/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- pycharter/ui/static/schemas/__next.schemas.txt +4 -0
- pycharter/ui/static/schemas/index.html +1 -0
- pycharter/ui/static/schemas/index.txt +21 -0
- pycharter/ui/static/settings/__next._full.txt +21 -0
- pycharter/ui/static/settings/__next._head.txt +7 -0
- pycharter/ui/static/settings/__next._index.txt +9 -0
- pycharter/ui/static/settings/__next._tree.txt +2 -0
- pycharter/ui/static/settings/__next.settings.__PAGE__.txt +9 -0
- pycharter/ui/static/settings/__next.settings.txt +4 -0
- pycharter/ui/static/settings/index.html +1 -0
- pycharter/ui/static/settings/index.txt +21 -0
- pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- pycharter/ui/static/static/static/static/.gitkeep +0 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- pycharter/ui/static/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_clientMiddlewareManifest.json +1 -0
- pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- pycharter/ui/static/validation/__next._full.txt +21 -0
- pycharter/ui/static/validation/__next._head.txt +7 -0
- pycharter/ui/static/validation/__next._index.txt +9 -0
- pycharter/ui/static/validation/__next._tree.txt +2 -0
- pycharter/ui/static/validation/__next.validation.__PAGE__.txt +9 -0
- pycharter/ui/static/validation/__next.validation.txt +4 -0
- pycharter/ui/static/validation/index.html +1 -0
- pycharter/ui/static/validation/index.txt +21 -0
- pycharter/ui/tsconfig.json +42 -0
- pycharter/worker/README.md +187 -0
- pycharter/worker/backends/__init__.py +8 -0
- pycharter/worker/backends/base.py +46 -0
- pycharter/worker/backends/spark.py +233 -0
- {worker → pycharter/worker}/cli.py +1 -1
- {worker → pycharter/worker}/processor.py +2 -2
- pycharter/worker/queue/__init__.py +8 -0
- pycharter/worker/queue/redis_queue.py +147 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/METADATA +57 -26
- pycharter-0.0.26.dist-info/RECORD +702 -0
- pycharter-0.0.26.dist-info/top_level.txt +1 -0
- pycharter/etl_generator/config_loader.py +0 -394
- pycharter/etl_generator/loaders/cloud.py +0 -87
- pycharter/etl_generator/loaders/file_loader.py +0 -130
- pycharter-0.0.24.dist-info/RECORD +0 -543
- pycharter-0.0.24.dist-info/top_level.txt +0 -4
- {api → pycharter/api}/dependencies/database.py +0 -0
- {api → pycharter/api}/dependencies/store.py +0 -0
- {api → pycharter/api}/models/contracts.py +0 -0
- {api → pycharter/api}/models/docs.py +0 -0
- {api → pycharter/api}/models/evolution.py +0 -0
- {api → pycharter/api}/models/metadata.py +0 -0
- {api → pycharter/api}/models/metadata_entities.py +0 -0
- {api → pycharter/api}/models/quality.py +0 -0
- {api → pycharter/api}/models/schemas.py +0 -0
- {api → pycharter/api}/models/tracking.py +0 -0
- {api → pycharter/api}/models/validation.py +0 -0
- {api → pycharter/api}/routes/__init__.py +0 -0
- {api → pycharter/api}/routes/v1/templates.py +0 -0
- {api → pycharter/api}/utils.py +0 -0
- {ui → pycharter/ui}/build.py +0 -0
- {ui → pycharter/ui}/dev.py +0 -0
- {ui → pycharter/ui}/static/.gitkeep +0 -0
- {ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs → pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB}/_buildManifest.js +0 -0
- {ui/static/_next/static/2gKjNv6YvE6BcIdFthBLs → pycharter/ui/static/_next/static/YCnlK66gA7FV5vvcixspB}/_ssgManifest.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui}/static/static/.gitkeep +0 -0
- {ui → pycharter/ui/static}/static/404/index.html +0 -0
- {ui → pycharter/ui/static}/static/404.html +0 -0
- {ui → pycharter/ui/static}/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/__next._tree.txt +0 -0
- {ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX → pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- {ui/static/static/_next/static/0rYA78L88aUyD2Uh38hhX → pycharter/ui/static/static/_next/static/2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/26dfc590f7714c03.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/34d289e6db2ef551.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/99508d9d5869cc27.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/b313c35a6ba76574.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui/static}/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/f7d1a90dd75d2572.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/_not-found/index.html +0 -0
- {ui → pycharter/ui/static}/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui/static}/static/contracts/index.html +0 -0
- {ui → pycharter/ui/static}/static/contracts/index.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui/static}/static/documentation/index.html +0 -0
- {ui → pycharter/ui/static}/static/documentation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/index.html +0 -0
- {ui → pycharter/ui/static}/static/index.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui/static}/static/metadata/index.html +0 -0
- {ui → pycharter/ui/static}/static/metadata/index.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui/static}/static/quality/index.html +0 -0
- {ui → pycharter/ui/static}/static/quality/index.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui/static}/static/rules/index.html +0 -0
- {ui → pycharter/ui/static}/static/rules/index.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui/static}/static/schemas/index.html +0 -0
- {ui → pycharter/ui/static}/static/schemas/index.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui/static}/static/settings/index.html +0 -0
- {ui → pycharter/ui/static}/static/settings/index.txt +0 -0
- {ui → pycharter/ui}/static/static/static/.gitkeep +0 -0
- {ui → pycharter/ui/static}/static/static/404/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/404.html +0 -0
- {ui → pycharter/ui/static}/static/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/__next._tree.txt +0 -0
- {ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl → pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- {ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl → pycharter/ui/static/static/static/_next/static/0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/222442f6da32302a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/247eb132b7f7b574.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/297d55555b71baba.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/2edb43b48432ac04.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/414e77373f8ff61c.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/652ad0aa26265c47.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/9c23f44fff36548a.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/b32a0963684b9933.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/c4fa4f4114b7c352.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/d2363397e1b2bcab.css +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/db913959c675cea6.js +0 -0
- {ui → pycharter/ui/static}/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/f2e7afeab1178138.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/ff1a16fafef87110.js +0 -0
- {ui → pycharter/ui}/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/contracts/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/documentation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/metadata/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui/static}/static/static/quality/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/quality/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui/static}/static/static/rules/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/rules/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/schemas/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui/static}/static/static/settings/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/settings/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/404/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/404.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/2ab439ce003cd691.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/4e310fe5005770a3.css +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/5e04d10c4a7b58a3.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/5fc14c00a2779dc5.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/75d88a058d8ffaa6.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/8c89634cf6bad76f.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/b584574fdc8ab13e.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/c69f6cba366bd988.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/d5989c94d3614b3a.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._not-found.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._not-found.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/_not-found/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next.contracts.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/__next.contracts.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/contracts/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next.documentation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/__next.documentation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/documentation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next.metadata.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/__next.metadata.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/metadata/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next.quality.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/__next.quality.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/quality/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next.rules.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/__next.rules.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/rules/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next.schemas.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/__next.schemas.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/schemas/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next.settings.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/__next.settings.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/settings/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/static/validation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui/static}/static/static/validation/index.html +0 -0
- {ui → pycharter/ui/static}/static/static/validation/index.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._full.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._head.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._index.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next._tree.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next.validation.__PAGE__.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/__next.validation.txt +0 -0
- {ui → pycharter/ui/static}/static/validation/index.html +0 -0
- {ui → pycharter/ui/static}/static/validation/index.txt +0 -0
- {worker → pycharter/worker}/__init__.py +0 -0
- {worker → pycharter/worker}/models.py +0 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/WHEEL +0 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.24.dist-info → pycharter-0.0.26.dist-info}/licenses/LICENSE +0 -0
|
@@ -22,27 +22,27 @@ from pycharter.etl_generator.extractors.cloud_storage import CloudStorageExtract
|
|
|
22
22
|
from pycharter.etl_generator.extractors.database import DatabaseExtractor
|
|
23
23
|
from pycharter.etl_generator.extractors.file import FileExtractor
|
|
24
24
|
from pycharter.etl_generator.extractors.http import HTTPExtractor
|
|
25
|
+
from pycharter.etl_generator.extractors.mongodb import MongoDBExtractor
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
class ExtractorFactory:
|
|
30
31
|
"""
|
|
31
|
-
Factory for creating extractor instances based on
|
|
32
|
+
Factory for creating extractor instances based on type.
|
|
32
33
|
|
|
33
34
|
Supports:
|
|
34
35
|
- Explicit 'type' field (recommended)
|
|
35
|
-
-
|
|
36
|
-
- Auto-detection from config keys (for backward compatibility)
|
|
36
|
+
- Auto-detection from config keys
|
|
37
37
|
|
|
38
38
|
Example:
|
|
39
39
|
# With explicit type (recommended)
|
|
40
40
|
config = {"type": "http", "url": "https://api.example.com/data"}
|
|
41
41
|
extractor = ExtractorFactory.create(config)
|
|
42
42
|
|
|
43
|
-
# Auto-detected
|
|
44
|
-
config = {"
|
|
45
|
-
extractor = ExtractorFactory.create(config) # Detected as
|
|
43
|
+
# Auto-detected from config keys
|
|
44
|
+
config = {"file_path": "/path/to/data.csv"}
|
|
45
|
+
extractor = ExtractorFactory.create(config) # Detected as file
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
48
|
# Registry of extractors by source type
|
|
@@ -51,6 +51,8 @@ class ExtractorFactory:
|
|
|
51
51
|
"file": FileExtractor,
|
|
52
52
|
"database": DatabaseExtractor,
|
|
53
53
|
"cloud_storage": CloudStorageExtractor,
|
|
54
|
+
"mongodb": MongoDBExtractor,
|
|
55
|
+
"mongo": MongoDBExtractor, # Alias
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
@classmethod
|
|
@@ -97,8 +99,8 @@ class ExtractorFactory:
|
|
|
97
99
|
Raises:
|
|
98
100
|
ValueError: If type cannot be determined or is not registered
|
|
99
101
|
"""
|
|
100
|
-
# Get type from config
|
|
101
|
-
extract_type = config.get("type")
|
|
102
|
+
# Get type from config
|
|
103
|
+
extract_type = config.get("type")
|
|
102
104
|
|
|
103
105
|
# Auto-detect if not specified
|
|
104
106
|
if not extract_type:
|
|
@@ -140,6 +142,10 @@ class ExtractorFactory:
|
|
|
140
142
|
|
|
141
143
|
This is for backward compatibility. New configs should use explicit 'type'.
|
|
142
144
|
"""
|
|
145
|
+
# MongoDB indicators (check first as it has specific 'mongodb' config key)
|
|
146
|
+
if "mongodb" in config:
|
|
147
|
+
return "mongodb"
|
|
148
|
+
|
|
143
149
|
# HTTP indicators
|
|
144
150
|
if any(key in config for key in ("url", "base_url", "api_endpoint", "endpoint")):
|
|
145
151
|
return "http"
|
|
@@ -148,7 +154,7 @@ class ExtractorFactory:
|
|
|
148
154
|
if any(key in config for key in ("path", "file_path")) and "storage" not in config:
|
|
149
155
|
return "file"
|
|
150
156
|
|
|
151
|
-
# Database indicators
|
|
157
|
+
# Database indicators (SQL databases)
|
|
152
158
|
if "query" in config or (
|
|
153
159
|
"database" in config and "connection_string" not in config.get("database", {}).get("url", "s3")
|
|
154
160
|
):
|
|
@@ -159,27 +165,3 @@ class ExtractorFactory:
|
|
|
159
165
|
return "cloud_storage"
|
|
160
166
|
|
|
161
167
|
return None
|
|
162
|
-
|
|
163
|
-
# Legacy method names for backward compatibility
|
|
164
|
-
@classmethod
|
|
165
|
-
def register_extractor(cls, source_type: str, extractor_class: Type[BaseExtractor]) -> None:
|
|
166
|
-
"""Legacy method. Use register() instead."""
|
|
167
|
-
cls.register(source_type, extractor_class)
|
|
168
|
-
|
|
169
|
-
@classmethod
|
|
170
|
-
def get_extractor(cls, extract_config: Dict[str, Any]) -> BaseExtractor:
|
|
171
|
-
"""Legacy method. Use create() instead."""
|
|
172
|
-
return cls.create(extract_config)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def get_extractor(extract_config: Dict[str, Any]) -> BaseExtractor:
|
|
176
|
-
"""
|
|
177
|
-
Convenience function to get extractor instance.
|
|
178
|
-
|
|
179
|
-
Args:
|
|
180
|
-
extract_config: Extract configuration dictionary
|
|
181
|
-
|
|
182
|
-
Returns:
|
|
183
|
-
Extractor instance
|
|
184
|
-
"""
|
|
185
|
-
return ExtractorFactory.create(extract_config)
|
|
@@ -99,8 +99,8 @@ class FileExtractor(BaseExtractor):
|
|
|
99
99
|
|
|
100
100
|
def validate_config(self, extract_config: Dict[str, Any]) -> None:
|
|
101
101
|
"""Validate file extractor configuration."""
|
|
102
|
-
if '
|
|
103
|
-
raise ValueError(f"FileExtractor requires
|
|
102
|
+
if 'type' in extract_config and extract_config['type'] != 'file':
|
|
103
|
+
raise ValueError(f"FileExtractor requires type='file', got '{extract_config.get('type')}'")
|
|
104
104
|
|
|
105
105
|
file_path = extract_config.get('file_path')
|
|
106
106
|
if not file_path:
|
|
@@ -121,8 +121,8 @@ class HTTPExtractor(BaseExtractor):
|
|
|
121
121
|
|
|
122
122
|
def validate_config(self, extract_config: Dict[str, Any]) -> None:
|
|
123
123
|
"""Validate HTTP extractor configuration."""
|
|
124
|
-
if '
|
|
125
|
-
raise ValueError(f"HTTPExtractor requires
|
|
124
|
+
if 'type' in extract_config and extract_config['type'] != 'http':
|
|
125
|
+
raise ValueError(f"HTTPExtractor requires type='http', got '{extract_config.get('type')}'")
|
|
126
126
|
|
|
127
127
|
# Check for required HTTP config fields
|
|
128
128
|
if not extract_config.get('api_endpoint') and not extract_config.get('base_url'):
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MongoDB extractor for ETL orchestrator.
|
|
3
|
+
|
|
4
|
+
Supports extracting data from MongoDB collections using:
|
|
5
|
+
- Simple find() queries with filters
|
|
6
|
+
- Aggregation pipelines for complex transformations
|
|
7
|
+
|
|
8
|
+
Configuration example:
|
|
9
|
+
```yaml
|
|
10
|
+
type: mongodb
|
|
11
|
+
mongodb:
|
|
12
|
+
url: mongodb://user:pass@localhost:27017
|
|
13
|
+
database: mydb
|
|
14
|
+
collection: users
|
|
15
|
+
ssh_tunnel:
|
|
16
|
+
enabled: false
|
|
17
|
+
host: bastion.example.com
|
|
18
|
+
username: tunnel_user
|
|
19
|
+
remote_host: mongo-internal.example.com
|
|
20
|
+
remote_port: 27017
|
|
21
|
+
|
|
22
|
+
# Option 1: Simple query filter
|
|
23
|
+
query:
|
|
24
|
+
status: active
|
|
25
|
+
created_at:
|
|
26
|
+
$gte: "2024-01-01"
|
|
27
|
+
|
|
28
|
+
# Option 2: Aggregation pipeline (takes precedence over query)
|
|
29
|
+
pipeline:
|
|
30
|
+
- $match:
|
|
31
|
+
status: active
|
|
32
|
+
- $project:
|
|
33
|
+
name: 1
|
|
34
|
+
email: 1
|
|
35
|
+
_id: 0
|
|
36
|
+
|
|
37
|
+
# Optional: projection for simple queries
|
|
38
|
+
projection:
|
|
39
|
+
name: 1
|
|
40
|
+
email: 1
|
|
41
|
+
_id: 0
|
|
42
|
+
|
|
43
|
+
# Optional: sorting
|
|
44
|
+
sort:
|
|
45
|
+
created_at: -1
|
|
46
|
+
|
|
47
|
+
# Optional: limit results
|
|
48
|
+
limit: 1000
|
|
49
|
+
```
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
import logging
|
|
53
|
+
from typing import Any, AsyncIterator, Dict, List, Optional
|
|
54
|
+
|
|
55
|
+
from bson import ObjectId
|
|
56
|
+
from pymongo import MongoClient
|
|
57
|
+
|
|
58
|
+
from pycharter.etl_generator.extractors.base import BaseExtractor
|
|
59
|
+
from pycharter.etl_generator.database import (
|
|
60
|
+
create_ssh_tunnel,
|
|
61
|
+
DEFAULT_TUNNEL_LOCAL_PORT,
|
|
62
|
+
)
|
|
63
|
+
from pycharter.utils.value_injector import resolve_values
|
|
64
|
+
|
|
65
|
+
logger = logging.getLogger(__name__)
|
|
66
|
+
|
|
67
|
+
# Default MongoDB port
|
|
68
|
+
DEFAULT_MONGODB_PORT = 27017
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _serialize_document(doc: Dict[str, Any]) -> Dict[str, Any]:
|
|
72
|
+
"""
|
|
73
|
+
Serialize a MongoDB document for JSON compatibility.
|
|
74
|
+
|
|
75
|
+
Converts:
|
|
76
|
+
- ObjectId to string
|
|
77
|
+
- Other BSON types as needed
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
doc: MongoDB document
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
JSON-serializable dictionary
|
|
84
|
+
"""
|
|
85
|
+
result = {}
|
|
86
|
+
for key, value in doc.items():
|
|
87
|
+
if isinstance(value, ObjectId):
|
|
88
|
+
result[key] = str(value)
|
|
89
|
+
elif isinstance(value, dict):
|
|
90
|
+
result[key] = _serialize_document(value)
|
|
91
|
+
elif isinstance(value, list):
|
|
92
|
+
result[key] = [
|
|
93
|
+
_serialize_document(item) if isinstance(item, dict)
|
|
94
|
+
else str(item) if isinstance(item, ObjectId)
|
|
95
|
+
else item
|
|
96
|
+
for item in value
|
|
97
|
+
]
|
|
98
|
+
else:
|
|
99
|
+
result[key] = value
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _modify_mongodb_url_for_tunnel(url: str, local_port: int) -> str:
|
|
104
|
+
"""
|
|
105
|
+
Modify MongoDB URL to use local tunnel port.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
url: Original MongoDB URL
|
|
109
|
+
local_port: Local tunnel port
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Modified URL pointing to localhost tunnel
|
|
113
|
+
"""
|
|
114
|
+
import re
|
|
115
|
+
# Match mongodb:// or mongodb+srv:// URLs
|
|
116
|
+
# Replace host:port with 127.0.0.1:local_port
|
|
117
|
+
# Handle URLs with or without port
|
|
118
|
+
pattern = r'mongodb(\+srv)?://([^@]+@)?([^/:]+)(:\d+)?'
|
|
119
|
+
|
|
120
|
+
def replace_host(match):
|
|
121
|
+
protocol = match.group(1) or ''
|
|
122
|
+
auth = match.group(2) or ''
|
|
123
|
+
# For tunneled connections, we don't use SRV
|
|
124
|
+
return f'mongodb://{auth}127.0.0.1:{local_port}'
|
|
125
|
+
|
|
126
|
+
return re.sub(pattern, replace_host, url)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class MongoDBExtractor(BaseExtractor):
|
|
130
|
+
"""
|
|
131
|
+
Extractor for MongoDB data sources.
|
|
132
|
+
|
|
133
|
+
Supports two query modes:
|
|
134
|
+
1. Simple find() queries with optional projection and sorting
|
|
135
|
+
2. Aggregation pipelines for complex data transformations
|
|
136
|
+
|
|
137
|
+
Example (programmatic API):
|
|
138
|
+
>>> extractor = MongoDBExtractor(
|
|
139
|
+
... connection_string="mongodb://localhost:27017",
|
|
140
|
+
... database="mydb",
|
|
141
|
+
... collection="users",
|
|
142
|
+
... query={"status": "active"},
|
|
143
|
+
... )
|
|
144
|
+
>>> async for batch in extractor.extract():
|
|
145
|
+
... process(batch)
|
|
146
|
+
|
|
147
|
+
Example (config-driven):
|
|
148
|
+
>>> extractor = MongoDBExtractor()
|
|
149
|
+
>>> async for batch in extractor.extract_streaming(config, params, headers):
|
|
150
|
+
... process(batch)
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
def __init__(
|
|
154
|
+
self,
|
|
155
|
+
connection_string: Optional[str] = None,
|
|
156
|
+
database: Optional[str] = None,
|
|
157
|
+
collection: Optional[str] = None,
|
|
158
|
+
query: Optional[Dict[str, Any]] = None,
|
|
159
|
+
pipeline: Optional[List[Dict[str, Any]]] = None,
|
|
160
|
+
projection: Optional[Dict[str, Any]] = None,
|
|
161
|
+
sort: Optional[Dict[str, int]] = None,
|
|
162
|
+
batch_size: int = 1000,
|
|
163
|
+
max_records: Optional[int] = None,
|
|
164
|
+
ssh_tunnel: Optional[Dict[str, Any]] = None,
|
|
165
|
+
):
|
|
166
|
+
"""
|
|
167
|
+
Initialize MongoDB extractor.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
connection_string: MongoDB connection URL
|
|
171
|
+
database: Database name
|
|
172
|
+
collection: Collection name
|
|
173
|
+
query: MongoDB query filter (for find operations)
|
|
174
|
+
pipeline: Aggregation pipeline (takes precedence over query)
|
|
175
|
+
projection: Fields to include/exclude (for find operations)
|
|
176
|
+
sort: Sort specification (for find operations)
|
|
177
|
+
batch_size: Number of records per batch
|
|
178
|
+
max_records: Maximum records to extract (None = all)
|
|
179
|
+
ssh_tunnel: SSH tunnel configuration
|
|
180
|
+
"""
|
|
181
|
+
self.connection_string = connection_string
|
|
182
|
+
self.database = database
|
|
183
|
+
self.collection = collection
|
|
184
|
+
self.query = query or {}
|
|
185
|
+
self.pipeline = pipeline
|
|
186
|
+
self.projection = projection
|
|
187
|
+
self.sort = sort
|
|
188
|
+
self.batch_size = batch_size
|
|
189
|
+
self.max_records = max_records
|
|
190
|
+
self.ssh_tunnel = ssh_tunnel
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def from_config(cls, config: Dict[str, Any]) -> "MongoDBExtractor":
|
|
194
|
+
"""Create extractor from configuration dict."""
|
|
195
|
+
mongo_config = config.get("mongodb", {})
|
|
196
|
+
return cls(
|
|
197
|
+
connection_string=mongo_config.get("url") or config.get("connection_string"),
|
|
198
|
+
database=mongo_config.get("database") or config.get("database"),
|
|
199
|
+
collection=mongo_config.get("collection") or config.get("collection"),
|
|
200
|
+
query=config.get("query", {}),
|
|
201
|
+
pipeline=config.get("pipeline"),
|
|
202
|
+
projection=config.get("projection"),
|
|
203
|
+
sort=config.get("sort"),
|
|
204
|
+
batch_size=config.get("batch_size", 1000),
|
|
205
|
+
max_records=config.get("max_records"),
|
|
206
|
+
ssh_tunnel=mongo_config.get("ssh_tunnel"),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
async def extract(self, **params) -> AsyncIterator[List[Dict[str, Any]]]:
|
|
210
|
+
"""
|
|
211
|
+
Extract data from MongoDB.
|
|
212
|
+
|
|
213
|
+
Yields:
|
|
214
|
+
Batches of records as dictionaries
|
|
215
|
+
"""
|
|
216
|
+
if not self.connection_string:
|
|
217
|
+
raise ValueError("MongoDB connection string is required")
|
|
218
|
+
if not self.database:
|
|
219
|
+
raise ValueError("MongoDB database name is required")
|
|
220
|
+
if not self.collection:
|
|
221
|
+
raise ValueError("MongoDB collection name is required")
|
|
222
|
+
|
|
223
|
+
extract_config = {
|
|
224
|
+
"mongodb": {
|
|
225
|
+
"url": self.connection_string,
|
|
226
|
+
"database": self.database,
|
|
227
|
+
"collection": self.collection,
|
|
228
|
+
"ssh_tunnel": self.ssh_tunnel,
|
|
229
|
+
},
|
|
230
|
+
"query": self.query,
|
|
231
|
+
"pipeline": self.pipeline,
|
|
232
|
+
"projection": self.projection,
|
|
233
|
+
"sort": self.sort,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async for batch in self.extract_streaming(
|
|
237
|
+
extract_config, params, {},
|
|
238
|
+
batch_size=self.batch_size,
|
|
239
|
+
max_records=self.max_records,
|
|
240
|
+
):
|
|
241
|
+
yield batch
|
|
242
|
+
|
|
243
|
+
def validate_config(self, extract_config: Dict[str, Any]) -> None:
|
|
244
|
+
"""Validate MongoDB extractor configuration."""
|
|
245
|
+
source_type = extract_config.get('type') or extract_config.get('source_type')
|
|
246
|
+
if source_type and source_type not in ('mongodb', 'mongo'):
|
|
247
|
+
raise ValueError(
|
|
248
|
+
f"MongoDBExtractor requires type='mongodb', got '{source_type}'"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
mongo_config = extract_config.get('mongodb', {})
|
|
252
|
+
if not mongo_config.get('url'):
|
|
253
|
+
raise ValueError("MongoDB extractor requires 'mongodb.url' in extract_config")
|
|
254
|
+
if not mongo_config.get('database'):
|
|
255
|
+
raise ValueError("MongoDB extractor requires 'mongodb.database' in extract_config")
|
|
256
|
+
if not mongo_config.get('collection'):
|
|
257
|
+
raise ValueError("MongoDB extractor requires 'mongodb.collection' in extract_config")
|
|
258
|
+
|
|
259
|
+
async def extract_streaming(
|
|
260
|
+
self,
|
|
261
|
+
extract_config: Dict[str, Any],
|
|
262
|
+
params: Dict[str, Any],
|
|
263
|
+
headers: Dict[str, Any],
|
|
264
|
+
contract_dir: Optional[Any] = None,
|
|
265
|
+
batch_size: int = 1000,
|
|
266
|
+
max_records: Optional[int] = None,
|
|
267
|
+
config_context: Optional[Dict[str, Any]] = None,
|
|
268
|
+
) -> AsyncIterator[List[Dict[str, Any]]]:
|
|
269
|
+
"""
|
|
270
|
+
Extract data from MongoDB using find() or aggregation pipeline.
|
|
271
|
+
|
|
272
|
+
Yields batches of records as lists of dictionaries.
|
|
273
|
+
"""
|
|
274
|
+
# Get MongoDB configuration
|
|
275
|
+
mongo_config = extract_config.get('mongodb', {})
|
|
276
|
+
|
|
277
|
+
# Resolve variables
|
|
278
|
+
source_file = str(contract_dir / "extract.yaml") if contract_dir else None
|
|
279
|
+
mongo_url = resolve_values(
|
|
280
|
+
mongo_config.get('url'),
|
|
281
|
+
context=config_context,
|
|
282
|
+
source_file=source_file
|
|
283
|
+
)
|
|
284
|
+
db_name = resolve_values(
|
|
285
|
+
mongo_config.get('database'),
|
|
286
|
+
context=config_context,
|
|
287
|
+
source_file=source_file
|
|
288
|
+
)
|
|
289
|
+
collection_name = resolve_values(
|
|
290
|
+
mongo_config.get('collection'),
|
|
291
|
+
context=config_context,
|
|
292
|
+
source_file=source_file
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if not mongo_url:
|
|
296
|
+
raise ValueError("MongoDB URL is required")
|
|
297
|
+
if not db_name:
|
|
298
|
+
raise ValueError("MongoDB database name is required")
|
|
299
|
+
if not collection_name:
|
|
300
|
+
raise ValueError("MongoDB collection name is required")
|
|
301
|
+
|
|
302
|
+
# Handle SSH tunnel if configured
|
|
303
|
+
ssh_config = mongo_config.get('ssh_tunnel', {})
|
|
304
|
+
tunnel = None
|
|
305
|
+
if ssh_config:
|
|
306
|
+
ssh_config = resolve_values(ssh_config, context=config_context, source_file=source_file)
|
|
307
|
+
enabled_value = ssh_config.get('enabled', False)
|
|
308
|
+
if isinstance(enabled_value, str):
|
|
309
|
+
enabled_lower = enabled_value.lower()
|
|
310
|
+
ssh_config['enabled'] = enabled_lower in ('true', '1', 'yes', 'on')
|
|
311
|
+
elif not isinstance(enabled_value, bool):
|
|
312
|
+
ssh_config['enabled'] = bool(enabled_value)
|
|
313
|
+
|
|
314
|
+
if ssh_config.get('enabled', False):
|
|
315
|
+
# Set default remote port for MongoDB if not specified
|
|
316
|
+
if 'remote_port' not in ssh_config:
|
|
317
|
+
ssh_config['remote_port'] = DEFAULT_MONGODB_PORT
|
|
318
|
+
|
|
319
|
+
tunnel = create_ssh_tunnel(ssh_config)
|
|
320
|
+
if tunnel:
|
|
321
|
+
local_port = int(ssh_config.get('local_port', DEFAULT_TUNNEL_LOCAL_PORT))
|
|
322
|
+
mongo_url = _modify_mongodb_url_for_tunnel(mongo_url, local_port)
|
|
323
|
+
|
|
324
|
+
# Get query/pipeline configuration
|
|
325
|
+
query = extract_config.get('query', {})
|
|
326
|
+
pipeline = extract_config.get('pipeline')
|
|
327
|
+
projection = extract_config.get('projection')
|
|
328
|
+
sort_spec = extract_config.get('sort')
|
|
329
|
+
limit = extract_config.get('limit')
|
|
330
|
+
|
|
331
|
+
# Merge params into query for parameterized queries
|
|
332
|
+
if params:
|
|
333
|
+
query = {**query, **params}
|
|
334
|
+
|
|
335
|
+
# Create MongoDB client
|
|
336
|
+
client = MongoClient(mongo_url)
|
|
337
|
+
db = client[db_name]
|
|
338
|
+
collection = db[collection_name]
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
logger.info(
|
|
342
|
+
f"Extracting from MongoDB: {db_name}.{collection_name} "
|
|
343
|
+
f"(pipeline: {bool(pipeline)}, query: {bool(query)})"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# Choose extraction method
|
|
347
|
+
if pipeline:
|
|
348
|
+
# Use aggregation pipeline
|
|
349
|
+
cursor = collection.aggregate(pipeline, batchSize=batch_size)
|
|
350
|
+
else:
|
|
351
|
+
# Use find() with optional projection, sort, limit
|
|
352
|
+
cursor = collection.find(query, projection)
|
|
353
|
+
|
|
354
|
+
if sort_spec:
|
|
355
|
+
# Convert dict to list of tuples for pymongo
|
|
356
|
+
sort_list = [(k, v) for k, v in sort_spec.items()]
|
|
357
|
+
cursor = cursor.sort(sort_list)
|
|
358
|
+
|
|
359
|
+
if limit:
|
|
360
|
+
cursor = cursor.limit(limit)
|
|
361
|
+
|
|
362
|
+
cursor = cursor.batch_size(batch_size)
|
|
363
|
+
|
|
364
|
+
# Stream results in batches
|
|
365
|
+
current_batch = []
|
|
366
|
+
total_extracted = 0
|
|
367
|
+
|
|
368
|
+
for doc in cursor:
|
|
369
|
+
if max_records and total_extracted >= max_records:
|
|
370
|
+
break
|
|
371
|
+
|
|
372
|
+
# Serialize document for JSON compatibility
|
|
373
|
+
record = _serialize_document(doc)
|
|
374
|
+
current_batch.append(record)
|
|
375
|
+
total_extracted += 1
|
|
376
|
+
|
|
377
|
+
if len(current_batch) >= batch_size:
|
|
378
|
+
yield current_batch
|
|
379
|
+
current_batch = []
|
|
380
|
+
|
|
381
|
+
# Yield remaining records
|
|
382
|
+
if current_batch:
|
|
383
|
+
yield current_batch
|
|
384
|
+
|
|
385
|
+
logger.info(f"MongoDB extraction completed: {total_extracted} records extracted")
|
|
386
|
+
|
|
387
|
+
except Exception as e:
|
|
388
|
+
logger.error(f"MongoDB extraction error: {e}", exc_info=True)
|
|
389
|
+
raise RuntimeError(f"MongoDB extraction failed: {e}") from e
|
|
390
|
+
finally:
|
|
391
|
+
client.close()
|
|
392
|
+
if tunnel:
|
|
393
|
+
tunnel.stop()
|
|
@@ -28,7 +28,7 @@ async def extract_with_pagination_streaming(
|
|
|
28
28
|
- Cloud storage (S3, GCS, Azure Blob)
|
|
29
29
|
|
|
30
30
|
The source type is auto-detected from extract_config or can be explicitly set
|
|
31
|
-
via '
|
|
31
|
+
via 'type' field.
|
|
32
32
|
|
|
33
33
|
Yields batches as they are extracted, preventing memory exhaustion for large datasets.
|
|
34
34
|
|
|
@@ -44,7 +44,7 @@ async def extract_with_pagination_streaming(
|
|
|
44
44
|
Yields:
|
|
45
45
|
Batches of extracted records (lists of dictionaries)
|
|
46
46
|
"""
|
|
47
|
-
extractor = ExtractorFactory.
|
|
47
|
+
extractor = ExtractorFactory.create(extract_config)
|
|
48
48
|
async for batch in extractor.extract_streaming(
|
|
49
49
|
extract_config,
|
|
50
50
|
params,
|
|
@@ -7,32 +7,38 @@ Two APIs:
|
|
|
7
7
|
|
|
8
8
|
Supports:
|
|
9
9
|
- Database (PostgreSQL, MySQL, SQLite, MSSQL)
|
|
10
|
+
- MongoDB (insert, upsert, replace, update, delete)
|
|
10
11
|
- File (local JSON, CSV, Parquet, JSONL)
|
|
11
12
|
- Cloud storage (AWS S3, Google Cloud Storage, Azure Blob)
|
|
12
13
|
"""
|
|
13
14
|
|
|
14
|
-
#
|
|
15
|
+
# Base class
|
|
15
16
|
from pycharter.etl_generator.loaders.base import BaseLoader
|
|
17
|
+
|
|
18
|
+
# Database loaders
|
|
16
19
|
from pycharter.etl_generator.loaders.database import PostgresLoader, DatabaseLoader
|
|
17
|
-
from pycharter.etl_generator.loaders.file import FileLoader
|
|
18
|
-
from pycharter.etl_generator.loaders.cloud import CloudStorageLoader
|
|
19
20
|
|
|
20
|
-
#
|
|
21
|
-
from pycharter.etl_generator.loaders.
|
|
21
|
+
# MongoDB loader
|
|
22
|
+
from pycharter.etl_generator.loaders.mongodb import MongoDBLoader
|
|
23
|
+
|
|
24
|
+
# File loaders (class and function APIs)
|
|
25
|
+
from pycharter.etl_generator.loaders.file import FileLoader, load_to_file
|
|
22
26
|
|
|
23
|
-
#
|
|
24
|
-
from pycharter.etl_generator.loaders.
|
|
25
|
-
|
|
27
|
+
# Cloud storage loaders (class and function APIs)
|
|
28
|
+
from pycharter.etl_generator.loaders.cloud_storage import CloudStorageLoader, load_to_cloud_storage
|
|
29
|
+
|
|
30
|
+
# Factory
|
|
31
|
+
from pycharter.etl_generator.loaders.factory import LoaderFactory
|
|
26
32
|
|
|
27
33
|
__all__ = [
|
|
28
34
|
# Base class
|
|
29
35
|
"BaseLoader",
|
|
30
36
|
# Factory
|
|
31
37
|
"LoaderFactory",
|
|
32
|
-
"get_loader",
|
|
33
38
|
# Class-based loaders
|
|
34
39
|
"PostgresLoader",
|
|
35
40
|
"DatabaseLoader",
|
|
41
|
+
"MongoDBLoader",
|
|
36
42
|
"FileLoader",
|
|
37
43
|
"CloudStorageLoader",
|
|
38
44
|
# Function-based loaders
|