pycharter 0.0.22__py3-none-any.whl → 0.0.24__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.
- api/main.py +27 -1
- api/models/docs.py +68 -0
- api/models/evolution.py +117 -0
- api/models/tracking.py +111 -0
- api/models/validation.py +46 -6
- api/routes/v1/__init__.py +14 -1
- api/routes/v1/docs.py +187 -0
- api/routes/v1/evolution.py +337 -0
- api/routes/v1/templates.py +211 -27
- api/routes/v1/tracking.py +301 -0
- api/routes/v1/validation.py +68 -31
- pycharter/__init__.py +268 -58
- pycharter/data/templates/contract/template_coercion_rules.yaml +57 -0
- pycharter/data/templates/contract/template_contract.yaml +122 -0
- pycharter/data/templates/contract/template_metadata.yaml +68 -0
- pycharter/data/templates/contract/template_schema.yaml +100 -0
- pycharter/data/templates/contract/template_validation_rules.yaml +75 -0
- pycharter/data/templates/etl/README.md +224 -0
- pycharter/data/templates/etl/extract_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/extract_cloud_gcs.yaml +25 -0
- pycharter/data/templates/etl/extract_cloud_s3.yaml +30 -0
- pycharter/data/templates/etl/extract_database.yaml +34 -0
- pycharter/data/templates/etl/extract_database_ssh.yaml +40 -0
- pycharter/data/templates/etl/extract_file_csv.yaml +21 -0
- pycharter/data/templates/etl/extract_file_glob.yaml +25 -0
- pycharter/data/templates/etl/extract_file_json.yaml +24 -0
- pycharter/data/templates/etl/extract_file_parquet.yaml +20 -0
- pycharter/data/templates/etl/extract_http_paginated.yaml +79 -0
- pycharter/data/templates/etl/extract_http_path_params.yaml +38 -0
- pycharter/data/templates/etl/extract_http_simple.yaml +62 -0
- pycharter/data/templates/etl/load_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/load_cloud_gcs.yaml +22 -0
- pycharter/data/templates/etl/load_cloud_s3.yaml +27 -0
- pycharter/data/templates/etl/load_file.yaml +34 -0
- pycharter/data/templates/etl/load_insert.yaml +18 -0
- pycharter/data/templates/etl/load_postgresql.yaml +39 -0
- pycharter/data/templates/etl/load_sqlite.yaml +21 -0
- pycharter/data/templates/etl/load_truncate_and_load.yaml +20 -0
- pycharter/data/templates/etl/load_upsert.yaml +25 -0
- pycharter/data/templates/etl/load_with_dlq.yaml +34 -0
- pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +35 -0
- pycharter/data/templates/etl/pipeline_http_to_db.yaml +75 -0
- pycharter/data/templates/etl/transform_combined.yaml +48 -0
- pycharter/data/templates/etl/transform_custom_function.yaml +58 -0
- pycharter/data/templates/etl/transform_jsonata.yaml +51 -0
- pycharter/data/templates/etl/transform_simple.yaml +59 -0
- pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +160 -0
- pycharter/docs_generator/__init__.py +43 -0
- pycharter/docs_generator/generator.py +465 -0
- pycharter/docs_generator/renderers.py +247 -0
- pycharter/etl_generator/__init__.py +168 -80
- pycharter/etl_generator/builder.py +121 -0
- pycharter/etl_generator/config_loader.py +394 -0
- pycharter/etl_generator/config_validator.py +418 -0
- pycharter/etl_generator/context.py +132 -0
- pycharter/etl_generator/expression.py +499 -0
- pycharter/etl_generator/extractors/__init__.py +30 -0
- pycharter/etl_generator/extractors/base.py +70 -0
- pycharter/etl_generator/extractors/cloud_storage.py +530 -0
- pycharter/etl_generator/extractors/database.py +221 -0
- pycharter/etl_generator/extractors/factory.py +185 -0
- pycharter/etl_generator/extractors/file.py +475 -0
- pycharter/etl_generator/extractors/http.py +895 -0
- pycharter/etl_generator/extractors/streaming.py +57 -0
- pycharter/etl_generator/loaders/__init__.py +41 -0
- pycharter/etl_generator/loaders/base.py +35 -0
- pycharter/etl_generator/loaders/cloud.py +87 -0
- pycharter/etl_generator/loaders/cloud_storage_loader.py +275 -0
- pycharter/etl_generator/loaders/database.py +274 -0
- pycharter/etl_generator/loaders/factory.py +180 -0
- pycharter/etl_generator/loaders/file.py +72 -0
- pycharter/etl_generator/loaders/file_loader.py +130 -0
- pycharter/etl_generator/pipeline.py +743 -0
- pycharter/etl_generator/protocols.py +54 -0
- pycharter/etl_generator/result.py +63 -0
- pycharter/etl_generator/schemas/__init__.py +49 -0
- pycharter/etl_generator/transformers/__init__.py +49 -0
- pycharter/etl_generator/transformers/base.py +63 -0
- pycharter/etl_generator/transformers/config.py +45 -0
- pycharter/etl_generator/transformers/custom_function.py +101 -0
- pycharter/etl_generator/transformers/jsonata_transformer.py +56 -0
- pycharter/etl_generator/transformers/operations.py +218 -0
- pycharter/etl_generator/transformers/pipeline.py +54 -0
- pycharter/etl_generator/transformers/simple_operations.py +131 -0
- pycharter/quality/__init__.py +25 -0
- pycharter/quality/tracking/__init__.py +64 -0
- pycharter/quality/tracking/collector.py +318 -0
- pycharter/quality/tracking/exporters.py +238 -0
- pycharter/quality/tracking/models.py +194 -0
- pycharter/quality/tracking/store.py +385 -0
- pycharter/runtime_validator/__init__.py +20 -7
- pycharter/runtime_validator/builder.py +328 -0
- pycharter/runtime_validator/validator.py +311 -7
- pycharter/runtime_validator/validator_core.py +61 -0
- pycharter/schema_evolution/__init__.py +61 -0
- pycharter/schema_evolution/compatibility.py +270 -0
- pycharter/schema_evolution/diff.py +496 -0
- pycharter/schema_evolution/models.py +201 -0
- pycharter/shared/__init__.py +56 -0
- pycharter/shared/errors.py +296 -0
- pycharter/shared/protocols.py +234 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/METADATA +146 -26
- pycharter-0.0.24.dist-info/RECORD +543 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/WHEEL +1 -1
- ui/static/404/index.html +1 -1
- ui/static/404.html +1 -1
- ui/static/__next.__PAGE__.txt +1 -1
- ui/static/__next._full.txt +1 -1
- ui/static/__next._head.txt +1 -1
- ui/static/__next._index.txt +1 -1
- ui/static/__next._tree.txt +1 -1
- ui/static/_next/static/chunks/26dfc590f7714c03.js +1 -0
- ui/static/_next/static/chunks/34d289e6db2ef551.js +1 -0
- ui/static/_next/static/chunks/99508d9d5869cc27.js +1 -0
- ui/static/_next/static/chunks/b313c35a6ba76574.js +1 -0
- ui/static/_not-found/__next._full.txt +1 -1
- ui/static/_not-found/__next._head.txt +1 -1
- ui/static/_not-found/__next._index.txt +1 -1
- ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/_not-found/__next._not-found.txt +1 -1
- ui/static/_not-found/__next._tree.txt +1 -1
- ui/static/_not-found/index.html +1 -1
- ui/static/_not-found/index.txt +1 -1
- ui/static/contracts/__next._full.txt +2 -2
- ui/static/contracts/__next._head.txt +1 -1
- ui/static/contracts/__next._index.txt +1 -1
- ui/static/contracts/__next._tree.txt +1 -1
- ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/contracts/__next.contracts.txt +1 -1
- ui/static/contracts/index.html +1 -1
- ui/static/contracts/index.txt +2 -2
- ui/static/documentation/__next._full.txt +1 -1
- ui/static/documentation/__next._head.txt +1 -1
- ui/static/documentation/__next._index.txt +1 -1
- ui/static/documentation/__next._tree.txt +1 -1
- ui/static/documentation/__next.documentation.__PAGE__.txt +1 -1
- ui/static/documentation/__next.documentation.txt +1 -1
- ui/static/documentation/index.html +2 -2
- ui/static/documentation/index.txt +1 -1
- ui/static/index.html +1 -1
- ui/static/index.txt +1 -1
- ui/static/metadata/__next._full.txt +1 -1
- ui/static/metadata/__next._head.txt +1 -1
- ui/static/metadata/__next._index.txt +1 -1
- ui/static/metadata/__next._tree.txt +1 -1
- ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/metadata/__next.metadata.txt +1 -1
- ui/static/metadata/index.html +1 -1
- ui/static/metadata/index.txt +1 -1
- ui/static/quality/__next._full.txt +2 -2
- ui/static/quality/__next._head.txt +1 -1
- ui/static/quality/__next._index.txt +1 -1
- ui/static/quality/__next._tree.txt +1 -1
- ui/static/quality/__next.quality.__PAGE__.txt +2 -2
- ui/static/quality/__next.quality.txt +1 -1
- ui/static/quality/index.html +2 -2
- ui/static/quality/index.txt +2 -2
- ui/static/rules/__next._full.txt +1 -1
- ui/static/rules/__next._head.txt +1 -1
- ui/static/rules/__next._index.txt +1 -1
- ui/static/rules/__next._tree.txt +1 -1
- ui/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/rules/__next.rules.txt +1 -1
- ui/static/rules/index.html +1 -1
- ui/static/rules/index.txt +1 -1
- ui/static/schemas/__next._full.txt +1 -1
- ui/static/schemas/__next._head.txt +1 -1
- ui/static/schemas/__next._index.txt +1 -1
- ui/static/schemas/__next._tree.txt +1 -1
- ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/schemas/__next.schemas.txt +1 -1
- ui/static/schemas/index.html +1 -1
- ui/static/schemas/index.txt +1 -1
- ui/static/settings/__next._full.txt +1 -1
- ui/static/settings/__next._head.txt +1 -1
- ui/static/settings/__next._index.txt +1 -1
- ui/static/settings/__next._tree.txt +1 -1
- ui/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/settings/__next.settings.txt +1 -1
- ui/static/settings/index.html +1 -1
- ui/static/settings/index.txt +1 -1
- ui/static/static/404/index.html +1 -1
- ui/static/static/404.html +1 -1
- ui/static/static/__next.__PAGE__.txt +1 -1
- ui/static/static/__next._full.txt +2 -2
- ui/static/static/__next._head.txt +1 -1
- ui/static/static/__next._index.txt +2 -2
- ui/static/static/__next._tree.txt +2 -2
- ui/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
- ui/static/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
- ui/static/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
- ui/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- ui/static/static/_not-found/__next._full.txt +2 -2
- ui/static/static/_not-found/__next._head.txt +1 -1
- ui/static/static/_not-found/__next._index.txt +2 -2
- ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/static/_not-found/__next._not-found.txt +1 -1
- ui/static/static/_not-found/__next._tree.txt +2 -2
- ui/static/static/_not-found/index.html +1 -1
- ui/static/static/_not-found/index.txt +2 -2
- ui/static/static/contracts/__next._full.txt +3 -3
- ui/static/static/contracts/__next._head.txt +1 -1
- ui/static/static/contracts/__next._index.txt +2 -2
- ui/static/static/contracts/__next._tree.txt +2 -2
- ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/static/contracts/__next.contracts.txt +1 -1
- ui/static/static/contracts/index.html +1 -1
- ui/static/static/contracts/index.txt +3 -3
- ui/static/static/documentation/__next._full.txt +3 -3
- ui/static/static/documentation/__next._head.txt +1 -1
- ui/static/static/documentation/__next._index.txt +2 -2
- ui/static/static/documentation/__next._tree.txt +2 -2
- ui/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- ui/static/static/documentation/__next.documentation.txt +1 -1
- ui/static/static/documentation/index.html +2 -2
- ui/static/static/documentation/index.txt +3 -3
- ui/static/static/index.html +1 -1
- ui/static/static/index.txt +2 -2
- ui/static/static/metadata/__next._full.txt +2 -2
- ui/static/static/metadata/__next._head.txt +1 -1
- ui/static/static/metadata/__next._index.txt +2 -2
- ui/static/static/metadata/__next._tree.txt +2 -2
- ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/static/metadata/__next.metadata.txt +1 -1
- ui/static/static/metadata/index.html +1 -1
- ui/static/static/metadata/index.txt +2 -2
- ui/static/static/quality/__next._full.txt +2 -2
- ui/static/static/quality/__next._head.txt +1 -1
- ui/static/static/quality/__next._index.txt +2 -2
- ui/static/static/quality/__next._tree.txt +2 -2
- ui/static/static/quality/__next.quality.__PAGE__.txt +1 -1
- ui/static/static/quality/__next.quality.txt +1 -1
- ui/static/static/quality/index.html +2 -2
- ui/static/static/quality/index.txt +2 -2
- ui/static/static/rules/__next._full.txt +2 -2
- ui/static/static/rules/__next._head.txt +1 -1
- ui/static/static/rules/__next._index.txt +2 -2
- ui/static/static/rules/__next._tree.txt +2 -2
- ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/static/rules/__next.rules.txt +1 -1
- ui/static/static/rules/index.html +1 -1
- ui/static/static/rules/index.txt +2 -2
- ui/static/static/schemas/__next._full.txt +2 -2
- ui/static/static/schemas/__next._head.txt +1 -1
- ui/static/static/schemas/__next._index.txt +2 -2
- ui/static/static/schemas/__next._tree.txt +2 -2
- ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/static/schemas/__next.schemas.txt +1 -1
- ui/static/static/schemas/index.html +1 -1
- ui/static/static/schemas/index.txt +2 -2
- ui/static/static/settings/__next._full.txt +2 -2
- ui/static/static/settings/__next._head.txt +1 -1
- ui/static/static/settings/__next._index.txt +2 -2
- ui/static/static/settings/__next._tree.txt +2 -2
- ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/static/settings/__next.settings.txt +1 -1
- ui/static/static/settings/index.html +1 -1
- ui/static/static/settings/index.txt +2 -2
- ui/static/static/static/.gitkeep +0 -0
- ui/static/static/static/404/index.html +1 -0
- ui/static/static/static/404.html +1 -0
- ui/static/static/static/__next.__PAGE__.txt +10 -0
- ui/static/static/static/__next._full.txt +30 -0
- ui/static/static/static/__next._head.txt +7 -0
- ui/static/static/static/__next._index.txt +9 -0
- ui/static/static/static/__next._tree.txt +2 -0
- ui/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- ui/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- ui/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- ui/static/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
- ui/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- ui/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
- ui/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- ui/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
- ui/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- ui/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- ui/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- ui/static/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
- ui/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- ui/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
- ui/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- ui/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- ui/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- ui/static/static/static/_not-found/__next._full.txt +17 -0
- ui/static/static/static/_not-found/__next._head.txt +7 -0
- ui/static/static/static/_not-found/__next._index.txt +9 -0
- ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- ui/static/static/static/_not-found/__next._not-found.txt +4 -0
- ui/static/static/static/_not-found/__next._tree.txt +2 -0
- ui/static/static/static/_not-found/index.html +1 -0
- ui/static/static/static/_not-found/index.txt +17 -0
- ui/static/static/static/contracts/__next._full.txt +21 -0
- ui/static/static/static/contracts/__next._head.txt +7 -0
- ui/static/static/static/contracts/__next._index.txt +9 -0
- ui/static/static/static/contracts/__next._tree.txt +2 -0
- ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- ui/static/static/static/contracts/__next.contracts.txt +4 -0
- ui/static/static/static/contracts/index.html +1 -0
- ui/static/static/static/contracts/index.txt +21 -0
- ui/static/static/static/documentation/__next._full.txt +21 -0
- ui/static/static/static/documentation/__next._head.txt +7 -0
- ui/static/static/static/documentation/__next._index.txt +9 -0
- ui/static/static/static/documentation/__next._tree.txt +2 -0
- ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- ui/static/static/static/documentation/__next.documentation.txt +4 -0
- ui/static/static/static/documentation/index.html +93 -0
- ui/static/static/static/documentation/index.txt +21 -0
- ui/static/static/static/index.html +1 -0
- ui/static/static/static/index.txt +30 -0
- ui/static/static/static/metadata/__next._full.txt +21 -0
- ui/static/static/static/metadata/__next._head.txt +7 -0
- ui/static/static/static/metadata/__next._index.txt +9 -0
- ui/static/static/static/metadata/__next._tree.txt +2 -0
- ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- ui/static/static/static/metadata/__next.metadata.txt +4 -0
- ui/static/static/static/metadata/index.html +1 -0
- ui/static/static/static/metadata/index.txt +21 -0
- ui/static/static/static/quality/__next._full.txt +21 -0
- ui/static/static/static/quality/__next._head.txt +7 -0
- ui/static/static/static/quality/__next._index.txt +9 -0
- ui/static/static/static/quality/__next._tree.txt +2 -0
- ui/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
- ui/static/static/static/quality/__next.quality.txt +4 -0
- ui/static/static/static/quality/index.html +2 -0
- ui/static/static/static/quality/index.txt +21 -0
- ui/static/static/static/rules/__next._full.txt +21 -0
- ui/static/static/static/rules/__next._head.txt +7 -0
- ui/static/static/static/rules/__next._index.txt +9 -0
- ui/static/static/static/rules/__next._tree.txt +2 -0
- ui/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
- ui/static/static/static/rules/__next.rules.txt +4 -0
- ui/static/static/static/rules/index.html +1 -0
- ui/static/static/static/rules/index.txt +21 -0
- ui/static/static/static/schemas/__next._full.txt +21 -0
- ui/static/static/static/schemas/__next._head.txt +7 -0
- ui/static/static/static/schemas/__next._index.txt +9 -0
- ui/static/static/static/schemas/__next._tree.txt +2 -0
- ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- ui/static/static/static/schemas/__next.schemas.txt +4 -0
- ui/static/static/static/schemas/index.html +1 -0
- ui/static/static/static/schemas/index.txt +21 -0
- ui/static/static/static/settings/__next._full.txt +21 -0
- ui/static/static/static/settings/__next._head.txt +7 -0
- ui/static/static/static/settings/__next._index.txt +9 -0
- ui/static/static/static/settings/__next._tree.txt +2 -0
- ui/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
- ui/static/static/static/settings/__next.settings.txt +4 -0
- ui/static/static/static/settings/index.html +1 -0
- ui/static/static/static/settings/index.txt +21 -0
- ui/static/static/static/validation/__next._full.txt +21 -0
- ui/static/static/static/validation/__next._head.txt +7 -0
- ui/static/static/static/validation/__next._index.txt +9 -0
- ui/static/static/static/validation/__next._tree.txt +2 -0
- ui/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
- ui/static/static/static/validation/__next.validation.txt +4 -0
- ui/static/static/static/validation/index.html +1 -0
- ui/static/static/static/validation/index.txt +21 -0
- ui/static/static/validation/__next._full.txt +2 -2
- ui/static/static/validation/__next._head.txt +1 -1
- ui/static/static/validation/__next._index.txt +2 -2
- ui/static/static/validation/__next._tree.txt +2 -2
- ui/static/static/validation/__next.validation.__PAGE__.txt +1 -1
- ui/static/static/validation/__next.validation.txt +1 -1
- ui/static/static/validation/index.html +1 -1
- ui/static/static/validation/index.txt +2 -2
- ui/static/validation/__next._full.txt +2 -2
- ui/static/validation/__next._head.txt +1 -1
- ui/static/validation/__next._index.txt +1 -1
- ui/static/validation/__next._tree.txt +1 -1
- ui/static/validation/__next.validation.__PAGE__.txt +2 -2
- ui/static/validation/__next.validation.txt +1 -1
- ui/static/validation/index.html +1 -1
- ui/static/validation/index.txt +2 -2
- pycharter/data/templates/template_coercion_rules.yaml +0 -15
- pycharter/data/templates/template_contract.yaml +0 -587
- pycharter/data/templates/template_metadata.yaml +0 -38
- pycharter/data/templates/template_schema.yaml +0 -22
- pycharter/data/templates/template_transform_advanced.yaml +0 -50
- pycharter/data/templates/template_transform_simple.yaml +0 -59
- pycharter/data/templates/template_validation_rules.yaml +0 -29
- pycharter/etl_generator/extraction.py +0 -916
- pycharter/etl_generator/factory.py +0 -174
- pycharter/etl_generator/orchestrator.py +0 -1650
- pycharter/integrations/__init__.py +0 -19
- pycharter/integrations/kafka.py +0 -178
- pycharter/integrations/streaming.py +0 -100
- pycharter-0.0.22.dist-info/RECORD +0 -358
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/licenses/LICENSE +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/top_level.txt +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Documentation Renderers - Output format implementations.
|
|
3
|
+
|
|
4
|
+
Provides Protocol definition and concrete implementations for
|
|
5
|
+
rendering documentation in various formats.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional, Protocol
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DocsRenderer(Protocol):
|
|
12
|
+
"""Protocol for documentation renderers."""
|
|
13
|
+
|
|
14
|
+
def render_header(self, title: str, version: Optional[str] = None) -> str:
|
|
15
|
+
"""Render document header with title and optional version."""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def render_section(self, title: str, content: str) -> str:
|
|
19
|
+
"""Render a section with title and content."""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def render_table(
|
|
23
|
+
self, headers: List[str], rows: List[List[str]], caption: Optional[str] = None
|
|
24
|
+
) -> str:
|
|
25
|
+
"""Render a table with headers and rows."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
def render_code_block(self, code: str, language: str = "") -> str:
|
|
29
|
+
"""Render a code block."""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
def render_list(self, items: List[str], ordered: bool = False) -> str:
|
|
33
|
+
"""Render a list of items."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
def render_description(self, text: str) -> str:
|
|
37
|
+
"""Render description/paragraph text."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def render_badge(self, label: str, value: str) -> str:
|
|
41
|
+
"""Render a badge/tag (e.g., required, optional)."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MarkdownRenderer:
|
|
46
|
+
"""Render documentation as Markdown."""
|
|
47
|
+
|
|
48
|
+
def render_header(self, title: str, version: Optional[str] = None) -> str:
|
|
49
|
+
"""Render document header with title and optional version."""
|
|
50
|
+
header = f"# {title}\n\n"
|
|
51
|
+
if version:
|
|
52
|
+
header += f"**Version:** `{version}`\n\n"
|
|
53
|
+
return header
|
|
54
|
+
|
|
55
|
+
def render_section(self, title: str, content: str) -> str:
|
|
56
|
+
"""Render a section with title and content."""
|
|
57
|
+
return f"## {title}\n\n{content}\n\n"
|
|
58
|
+
|
|
59
|
+
def render_subsection(self, title: str, content: str) -> str:
|
|
60
|
+
"""Render a subsection with title and content."""
|
|
61
|
+
return f"### {title}\n\n{content}\n\n"
|
|
62
|
+
|
|
63
|
+
def render_table(
|
|
64
|
+
self, headers: List[str], rows: List[List[str]], caption: Optional[str] = None
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Render a Markdown table."""
|
|
67
|
+
if not headers or not rows:
|
|
68
|
+
return ""
|
|
69
|
+
|
|
70
|
+
lines = []
|
|
71
|
+
if caption:
|
|
72
|
+
lines.append(f"*{caption}*\n")
|
|
73
|
+
|
|
74
|
+
# Header row
|
|
75
|
+
lines.append("| " + " | ".join(headers) + " |")
|
|
76
|
+
# Separator
|
|
77
|
+
lines.append("| " + " | ".join(["---"] * len(headers)) + " |")
|
|
78
|
+
# Data rows
|
|
79
|
+
for row in rows:
|
|
80
|
+
# Ensure row has same length as headers
|
|
81
|
+
padded_row = row + [""] * (len(headers) - len(row))
|
|
82
|
+
lines.append("| " + " | ".join(padded_row[: len(headers)]) + " |")
|
|
83
|
+
|
|
84
|
+
return "\n".join(lines) + "\n"
|
|
85
|
+
|
|
86
|
+
def render_code_block(self, code: str, language: str = "") -> str:
|
|
87
|
+
"""Render a fenced code block."""
|
|
88
|
+
return f"```{language}\n{code}\n```\n"
|
|
89
|
+
|
|
90
|
+
def render_list(self, items: List[str], ordered: bool = False) -> str:
|
|
91
|
+
"""Render a list of items."""
|
|
92
|
+
if not items:
|
|
93
|
+
return ""
|
|
94
|
+
if ordered:
|
|
95
|
+
return "\n".join(f"{i+1}. {item}" for i, item in enumerate(items)) + "\n"
|
|
96
|
+
return "\n".join(f"- {item}" for item in items) + "\n"
|
|
97
|
+
|
|
98
|
+
def render_description(self, text: str) -> str:
|
|
99
|
+
"""Render description/paragraph text."""
|
|
100
|
+
return f"{text}\n\n"
|
|
101
|
+
|
|
102
|
+
def render_badge(self, label: str, value: str) -> str:
|
|
103
|
+
"""Render a badge as inline code."""
|
|
104
|
+
return f"`{label}: {value}`"
|
|
105
|
+
|
|
106
|
+
def render_key_value(self, key: str, value: Any) -> str:
|
|
107
|
+
"""Render a key-value pair."""
|
|
108
|
+
if isinstance(value, (dict, list)):
|
|
109
|
+
return f"**{key}:**\n{self.render_code_block(str(value), 'json')}"
|
|
110
|
+
return f"**{key}:** {value}\n"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class HTMLRenderer:
|
|
114
|
+
"""Render documentation as HTML."""
|
|
115
|
+
|
|
116
|
+
def __init__(self, include_styles: bool = True):
|
|
117
|
+
"""
|
|
118
|
+
Initialize HTML renderer.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
include_styles: If True, include inline CSS styles
|
|
122
|
+
"""
|
|
123
|
+
self.include_styles = include_styles
|
|
124
|
+
|
|
125
|
+
def _get_styles(self) -> str:
|
|
126
|
+
"""Get CSS styles for HTML output."""
|
|
127
|
+
return """
|
|
128
|
+
<style>
|
|
129
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
130
|
+
line-height: 1.6; max-width: 900px; margin: 0 auto; padding: 20px; }
|
|
131
|
+
h1 { border-bottom: 2px solid #333; padding-bottom: 10px; }
|
|
132
|
+
h2 { border-bottom: 1px solid #ddd; padding-bottom: 5px; margin-top: 30px; }
|
|
133
|
+
table { border-collapse: collapse; width: 100%; margin: 15px 0; }
|
|
134
|
+
th, td { border: 1px solid #ddd; padding: 10px; text-align: left; }
|
|
135
|
+
th { background: #f5f5f5; font-weight: 600; }
|
|
136
|
+
tr:nth-child(even) { background: #fafafa; }
|
|
137
|
+
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
|
|
138
|
+
pre { background: #f4f4f4; padding: 15px; border-radius: 5px; overflow-x: auto; }
|
|
139
|
+
pre code { background: none; padding: 0; }
|
|
140
|
+
.badge { display: inline-block; padding: 2px 8px; border-radius: 3px;
|
|
141
|
+
font-size: 0.8em; font-weight: 500; }
|
|
142
|
+
.badge-required { background: #fee2e2; color: #991b1b; }
|
|
143
|
+
.badge-optional { background: #e0f2fe; color: #0369a1; }
|
|
144
|
+
.version { color: #666; font-size: 0.9em; }
|
|
145
|
+
</style>
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def render_header(self, title: str, version: Optional[str] = None) -> str:
|
|
149
|
+
"""Render document header with title and optional version."""
|
|
150
|
+
styles = self._get_styles() if self.include_styles else ""
|
|
151
|
+
version_html = (
|
|
152
|
+
f'<p class="version">Version: <code>{version}</code></p>'
|
|
153
|
+
if version
|
|
154
|
+
else ""
|
|
155
|
+
)
|
|
156
|
+
return f"""<!DOCTYPE html>
|
|
157
|
+
<html>
|
|
158
|
+
<head>
|
|
159
|
+
<meta charset="UTF-8">
|
|
160
|
+
<title>{title}</title>
|
|
161
|
+
{styles}
|
|
162
|
+
</head>
|
|
163
|
+
<body>
|
|
164
|
+
<h1>{title}</h1>
|
|
165
|
+
{version_html}
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
def render_footer(self) -> str:
|
|
169
|
+
"""Render document footer."""
|
|
170
|
+
return "</body>\n</html>"
|
|
171
|
+
|
|
172
|
+
def render_section(self, title: str, content: str) -> str:
|
|
173
|
+
"""Render a section with title and content."""
|
|
174
|
+
return f"<h2>{title}</h2>\n{content}\n"
|
|
175
|
+
|
|
176
|
+
def render_subsection(self, title: str, content: str) -> str:
|
|
177
|
+
"""Render a subsection with title and content."""
|
|
178
|
+
return f"<h3>{title}</h3>\n{content}\n"
|
|
179
|
+
|
|
180
|
+
def render_table(
|
|
181
|
+
self, headers: List[str], rows: List[List[str]], caption: Optional[str] = None
|
|
182
|
+
) -> str:
|
|
183
|
+
"""Render an HTML table."""
|
|
184
|
+
if not headers or not rows:
|
|
185
|
+
return ""
|
|
186
|
+
|
|
187
|
+
lines = ["<table>"]
|
|
188
|
+
if caption:
|
|
189
|
+
lines.append(f"<caption>{caption}</caption>")
|
|
190
|
+
|
|
191
|
+
# Header
|
|
192
|
+
lines.append("<thead><tr>")
|
|
193
|
+
for h in headers:
|
|
194
|
+
lines.append(f"<th>{h}</th>")
|
|
195
|
+
lines.append("</tr></thead>")
|
|
196
|
+
|
|
197
|
+
# Body
|
|
198
|
+
lines.append("<tbody>")
|
|
199
|
+
for row in rows:
|
|
200
|
+
lines.append("<tr>")
|
|
201
|
+
for i, cell in enumerate(row):
|
|
202
|
+
if i < len(headers):
|
|
203
|
+
lines.append(f"<td>{cell}</td>")
|
|
204
|
+
# Pad if needed
|
|
205
|
+
for _ in range(len(headers) - len(row)):
|
|
206
|
+
lines.append("<td></td>")
|
|
207
|
+
lines.append("</tr>")
|
|
208
|
+
lines.append("</tbody>")
|
|
209
|
+
lines.append("</table>")
|
|
210
|
+
|
|
211
|
+
return "\n".join(lines) + "\n"
|
|
212
|
+
|
|
213
|
+
def render_code_block(self, code: str, language: str = "") -> str:
|
|
214
|
+
"""Render a code block."""
|
|
215
|
+
return f"<pre><code>{self._escape_html(code)}</code></pre>\n"
|
|
216
|
+
|
|
217
|
+
def render_list(self, items: List[str], ordered: bool = False) -> str:
|
|
218
|
+
"""Render a list of items."""
|
|
219
|
+
if not items:
|
|
220
|
+
return ""
|
|
221
|
+
tag = "ol" if ordered else "ul"
|
|
222
|
+
items_html = "\n".join(f"<li>{item}</li>" for item in items)
|
|
223
|
+
return f"<{tag}>\n{items_html}\n</{tag}>\n"
|
|
224
|
+
|
|
225
|
+
def render_description(self, text: str) -> str:
|
|
226
|
+
"""Render description/paragraph text."""
|
|
227
|
+
return f"<p>{text}</p>\n"
|
|
228
|
+
|
|
229
|
+
def render_badge(self, label: str, value: str) -> str:
|
|
230
|
+
"""Render a badge span."""
|
|
231
|
+
css_class = "badge-required" if value.lower() == "required" else "badge-optional"
|
|
232
|
+
return f'<span class="badge {css_class}">{label}: {value}</span>'
|
|
233
|
+
|
|
234
|
+
def render_key_value(self, key: str, value: Any) -> str:
|
|
235
|
+
"""Render a key-value pair."""
|
|
236
|
+
if isinstance(value, (dict, list)):
|
|
237
|
+
return f"<p><strong>{key}:</strong></p>\n{self.render_code_block(str(value), 'json')}"
|
|
238
|
+
return f"<p><strong>{key}:</strong> {value}</p>\n"
|
|
239
|
+
|
|
240
|
+
def _escape_html(self, text: str) -> str:
|
|
241
|
+
"""Escape HTML special characters."""
|
|
242
|
+
return (
|
|
243
|
+
text.replace("&", "&")
|
|
244
|
+
.replace("<", "<")
|
|
245
|
+
.replace(">", ">")
|
|
246
|
+
.replace('"', """)
|
|
247
|
+
)
|
|
@@ -1,94 +1,182 @@
|
|
|
1
1
|
"""
|
|
2
|
-
ETL
|
|
2
|
+
ETL Generator - Modern ETL pipeline framework.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Two APIs:
|
|
5
|
+
=========
|
|
6
|
+
1. Programmatic API (Pipeline with | operator):
|
|
7
|
+
>>> pipeline = Pipeline(HTTPExtractor(url="...")) | Rename(...) | PostgresLoader(...)
|
|
8
|
+
>>> result = await pipeline.run()
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
2. Config-driven API (YAML files):
|
|
11
|
+
>>> # From explicit files (most flexible)
|
|
12
|
+
>>> pipeline = Pipeline.from_config_files(
|
|
13
|
+
... extract="configs/extract.yaml",
|
|
14
|
+
... load="configs/load.yaml",
|
|
15
|
+
... variables={"API_KEY": "secret"}
|
|
16
|
+
... )
|
|
17
|
+
>>>
|
|
18
|
+
>>> # From directory (expects extract.yaml, transform.yaml, load.yaml)
|
|
19
|
+
>>> pipeline = Pipeline.from_config_dir("pipelines/users/")
|
|
20
|
+
>>>
|
|
21
|
+
>>> # From single file (pipeline.yaml)
|
|
22
|
+
>>> pipeline = Pipeline.from_config_file("pipelines/users/pipeline.yaml")
|
|
9
23
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
... transform_file="custom/transform.yaml",
|
|
34
|
-
... load_file="custom/load.yaml"
|
|
35
|
-
... )
|
|
36
|
-
>>> await orchestrator.run()
|
|
37
|
-
>>>
|
|
38
|
-
>>> # Mixed approach - override specific configs
|
|
39
|
-
>>> orchestrator = ETLOrchestrator(
|
|
40
|
-
... contract_dir="data/contracts/user",
|
|
41
|
-
... extract_config={"base_url": "https://custom.api.com"} # Override extract only
|
|
42
|
-
... # transform.yaml and load.yaml loaded from contract_dir
|
|
43
|
-
... )
|
|
44
|
-
>>> await orchestrator.run()
|
|
24
|
+
Quick Start:
|
|
25
|
+
============
|
|
26
|
+
from pycharter.etl_generator import (
|
|
27
|
+
Pipeline, HTTPExtractor, PostgresLoader,
|
|
28
|
+
Rename, AddField, Filter
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Programmatic
|
|
32
|
+
pipeline = (
|
|
33
|
+
Pipeline(HTTPExtractor(url="https://api.example.com/users"))
|
|
34
|
+
| Rename({"user_name": "name"})
|
|
35
|
+
| AddField("full_name", "${first_name} ${last_name}")
|
|
36
|
+
| PostgresLoader(connection_string="...", table="users")
|
|
37
|
+
)
|
|
38
|
+
result = await pipeline.run()
|
|
39
|
+
|
|
40
|
+
# Config-driven (explicit files)
|
|
41
|
+
pipeline = Pipeline.from_config_files(
|
|
42
|
+
extract="pipelines/users/extract.yaml",
|
|
43
|
+
load="pipelines/users/load.yaml",
|
|
44
|
+
variables={"DATA_DIR": "./data"}
|
|
45
|
+
)
|
|
46
|
+
result = await pipeline.run()
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
48
|
+
Config Format:
|
|
49
|
+
==============
|
|
50
|
+
Single-file (pipeline.yaml):
|
|
51
|
+
name: users_pipeline
|
|
52
|
+
extract:
|
|
53
|
+
type: http # Required: http | file | database | cloud_storage
|
|
54
|
+
url: https://api.example.com/users
|
|
55
|
+
transform:
|
|
56
|
+
- rename: {userId: user_id}
|
|
57
|
+
- add:
|
|
58
|
+
full_name: "${first_name} ${last_name}"
|
|
59
|
+
load:
|
|
60
|
+
type: postgres # Required: postgres | sqlite | file | cloud_storage
|
|
61
|
+
table: users
|
|
62
|
+
database:
|
|
63
|
+
url: ${DATABASE_URL}
|
|
49
64
|
"""
|
|
50
65
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
# Core pipeline classes
|
|
67
|
+
from pycharter.etl_generator.pipeline import Pipeline
|
|
68
|
+
from pycharter.etl_generator.builder import PipelineBuilder
|
|
69
|
+
from pycharter.etl_generator.context import PipelineContext
|
|
70
|
+
from pycharter.etl_generator.result import PipelineResult, BatchResult, LoadResult
|
|
71
|
+
from pycharter.etl_generator.protocols import Extractor, Transformer, Loader
|
|
72
|
+
|
|
73
|
+
# Config loading and validation
|
|
74
|
+
from pycharter.etl_generator.config_loader import (
|
|
75
|
+
ConfigLoader,
|
|
76
|
+
PipelineConfig,
|
|
77
|
+
load_pipeline_config,
|
|
78
|
+
ConfigLoadError,
|
|
54
79
|
)
|
|
55
|
-
from pycharter.etl_generator.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
80
|
+
from pycharter.etl_generator.config_validator import (
|
|
81
|
+
ConfigValidator,
|
|
82
|
+
ConfigValidationError,
|
|
83
|
+
validate_config,
|
|
59
84
|
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
|
|
86
|
+
# Expression evaluation
|
|
87
|
+
from pycharter.etl_generator.expression import (
|
|
88
|
+
ExpressionEvaluator,
|
|
89
|
+
ExpressionError,
|
|
90
|
+
evaluate_expression,
|
|
91
|
+
is_expression,
|
|
64
92
|
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
|
|
94
|
+
# Extractors
|
|
95
|
+
from pycharter.etl_generator.extractors import (
|
|
96
|
+
BaseExtractor,
|
|
97
|
+
HTTPExtractor,
|
|
98
|
+
FileExtractor,
|
|
99
|
+
DatabaseExtractor,
|
|
100
|
+
CloudStorageExtractor,
|
|
101
|
+
ExtractorFactory,
|
|
68
102
|
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
103
|
+
|
|
104
|
+
# Transformers
|
|
105
|
+
from pycharter.etl_generator.transformers import (
|
|
106
|
+
BaseTransformer,
|
|
107
|
+
TransformerChain,
|
|
108
|
+
Rename,
|
|
109
|
+
AddField,
|
|
110
|
+
Drop,
|
|
111
|
+
Select,
|
|
112
|
+
Filter,
|
|
113
|
+
Convert,
|
|
114
|
+
Default,
|
|
115
|
+
Map,
|
|
116
|
+
FlatMap,
|
|
117
|
+
CustomFunction,
|
|
73
118
|
)
|
|
119
|
+
|
|
120
|
+
# Loaders
|
|
121
|
+
from pycharter.etl_generator.loaders import (
|
|
122
|
+
BaseLoader,
|
|
123
|
+
PostgresLoader,
|
|
124
|
+
DatabaseLoader,
|
|
125
|
+
FileLoader,
|
|
126
|
+
CloudStorageLoader,
|
|
127
|
+
LoaderFactory,
|
|
128
|
+
)
|
|
129
|
+
|
|
74
130
|
__all__ = [
|
|
75
|
-
#
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
131
|
+
# Core
|
|
132
|
+
"Pipeline",
|
|
133
|
+
"PipelineBuilder",
|
|
134
|
+
"PipelineContext",
|
|
135
|
+
"PipelineResult",
|
|
136
|
+
"BatchResult",
|
|
137
|
+
"LoadResult",
|
|
138
|
+
# Protocols
|
|
139
|
+
"Extractor",
|
|
140
|
+
"Transformer",
|
|
141
|
+
"Loader",
|
|
142
|
+
# Config loading
|
|
143
|
+
"ConfigLoader",
|
|
144
|
+
"PipelineConfig",
|
|
145
|
+
"load_pipeline_config",
|
|
146
|
+
"ConfigLoadError",
|
|
147
|
+
"ConfigValidator",
|
|
148
|
+
"ConfigValidationError",
|
|
149
|
+
"validate_config",
|
|
150
|
+
# Expressions
|
|
151
|
+
"ExpressionEvaluator",
|
|
152
|
+
"ExpressionError",
|
|
153
|
+
"evaluate_expression",
|
|
154
|
+
"is_expression",
|
|
155
|
+
# Extractors
|
|
156
|
+
"BaseExtractor",
|
|
157
|
+
"HTTPExtractor",
|
|
158
|
+
"FileExtractor",
|
|
159
|
+
"DatabaseExtractor",
|
|
160
|
+
"CloudStorageExtractor",
|
|
161
|
+
"ExtractorFactory",
|
|
162
|
+
# Transformers
|
|
163
|
+
"BaseTransformer",
|
|
164
|
+
"TransformerChain",
|
|
165
|
+
"Rename",
|
|
166
|
+
"AddField",
|
|
167
|
+
"Drop",
|
|
168
|
+
"Select",
|
|
169
|
+
"Filter",
|
|
170
|
+
"Convert",
|
|
171
|
+
"Default",
|
|
172
|
+
"Map",
|
|
173
|
+
"FlatMap",
|
|
174
|
+
"CustomFunction",
|
|
175
|
+
# Loaders
|
|
176
|
+
"BaseLoader",
|
|
177
|
+
"PostgresLoader",
|
|
178
|
+
"DatabaseLoader",
|
|
179
|
+
"FileLoader",
|
|
180
|
+
"CloudStorageLoader",
|
|
181
|
+
"LoaderFactory",
|
|
94
182
|
]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fluent PipelineBuilder API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from pycharter.etl_generator.context import PipelineContext
|
|
8
|
+
from pycharter.etl_generator.pipeline import Pipeline
|
|
9
|
+
from pycharter.etl_generator.protocols import Extractor, Transformer, Loader
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PipelineBuilder:
|
|
13
|
+
"""
|
|
14
|
+
Fluent API for building ETL pipelines.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> pipeline = (
|
|
18
|
+
... PipelineBuilder()
|
|
19
|
+
... .name("user_pipeline")
|
|
20
|
+
... .extract_from("http", url="https://api.example.com/users")
|
|
21
|
+
... .transform(Rename({"old": "new"}))
|
|
22
|
+
... .load_to("postgres", table="users", connection_string="...")
|
|
23
|
+
... .build()
|
|
24
|
+
... )
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self._name: Optional[str] = None
|
|
29
|
+
self._extractor: Optional[Extractor] = None
|
|
30
|
+
self._transformers: List[Transformer] = []
|
|
31
|
+
self._loader: Optional[Loader] = None
|
|
32
|
+
self._context: Optional[PipelineContext] = None
|
|
33
|
+
|
|
34
|
+
def name(self, name: str) -> "PipelineBuilder":
|
|
35
|
+
"""Set pipeline name."""
|
|
36
|
+
self._name = name
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def with_context(self, context: PipelineContext) -> "PipelineBuilder":
|
|
40
|
+
"""Set pipeline context."""
|
|
41
|
+
self._context = context
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
def extract_from(self, source_type: str, **config) -> "PipelineBuilder":
|
|
45
|
+
"""Configure extractor by type."""
|
|
46
|
+
self._extractor = self._create_extractor(source_type, config)
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def extractor(self, extractor: Extractor) -> "PipelineBuilder":
|
|
50
|
+
"""Set extractor directly."""
|
|
51
|
+
self._extractor = extractor
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
def transform(self, transformer: Transformer) -> "PipelineBuilder":
|
|
55
|
+
"""Add transformer to pipeline."""
|
|
56
|
+
self._transformers.append(transformer)
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
def load_to(self, target_type: str, **config) -> "PipelineBuilder":
|
|
60
|
+
"""Configure loader by type."""
|
|
61
|
+
self._loader = self._create_loader(target_type, config)
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
def loader(self, loader: Loader) -> "PipelineBuilder":
|
|
65
|
+
"""Set loader directly."""
|
|
66
|
+
self._loader = loader
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def build(self) -> Pipeline:
|
|
70
|
+
"""Build the pipeline."""
|
|
71
|
+
if not self._extractor:
|
|
72
|
+
raise ValueError("Extractor required. Call extract_from() or extractor()")
|
|
73
|
+
|
|
74
|
+
return Pipeline(
|
|
75
|
+
extractor=self._extractor,
|
|
76
|
+
transformers=self._transformers,
|
|
77
|
+
loader=self._loader,
|
|
78
|
+
context=self._context or PipelineContext(),
|
|
79
|
+
name=self._name,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def _create_extractor(self, source_type: str, config: Dict[str, Any]) -> Extractor:
|
|
83
|
+
"""Create extractor from type and config."""
|
|
84
|
+
from pycharter.etl_generator.extractors import (
|
|
85
|
+
HTTPExtractor,
|
|
86
|
+
FileExtractor,
|
|
87
|
+
DatabaseExtractor,
|
|
88
|
+
CloudStorageExtractor,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
source_type = source_type.lower()
|
|
92
|
+
|
|
93
|
+
if source_type == "http":
|
|
94
|
+
return HTTPExtractor(**config)
|
|
95
|
+
elif source_type == "file":
|
|
96
|
+
return FileExtractor(**config)
|
|
97
|
+
elif source_type in ("database", "db", "sql"):
|
|
98
|
+
return DatabaseExtractor(**config)
|
|
99
|
+
elif source_type in ("s3", "cloud", "cloud_storage"):
|
|
100
|
+
return CloudStorageExtractor(**config)
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(f"Unknown extractor type: {source_type}")
|
|
103
|
+
|
|
104
|
+
def _create_loader(self, target_type: str, config: Dict[str, Any]) -> Loader:
|
|
105
|
+
"""Create loader from type and config."""
|
|
106
|
+
from pycharter.etl_generator.loaders import (
|
|
107
|
+
PostgresLoader,
|
|
108
|
+
FileLoader,
|
|
109
|
+
CloudStorageLoader,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
target_type = target_type.lower()
|
|
113
|
+
|
|
114
|
+
if target_type in ("postgres", "postgresql", "database", "db"):
|
|
115
|
+
return PostgresLoader(**config)
|
|
116
|
+
elif target_type == "file":
|
|
117
|
+
return FileLoader(**config)
|
|
118
|
+
elif target_type in ("s3", "cloud", "cloud_storage"):
|
|
119
|
+
return CloudStorageLoader(**config)
|
|
120
|
+
else:
|
|
121
|
+
raise ValueError(f"Unknown loader type: {target_type}")
|