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
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Template: PostgreSQL Load with Target Validation
|
|
2
|
+
# Copy to your pipeline directory as load.yaml
|
|
3
|
+
#
|
|
4
|
+
# This template demonstrates target contract validation before loading.
|
|
5
|
+
# Data is validated against a contract (schema + coercion + validation rules)
|
|
6
|
+
# to ensure it meets the target system's requirements.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# pipeline = Pipeline.from_config_dir("path/to/pipeline")
|
|
10
|
+
# result = await pipeline.run()
|
|
11
|
+
# print(f"Records loaded: {result.rows_loaded}")
|
|
12
|
+
# print(f"Quarantined at load: {result.rows_quarantined_load}")
|
|
13
|
+
|
|
14
|
+
title: postgres_load_with_validation
|
|
15
|
+
description: Load to PostgreSQL with target contract validation
|
|
16
|
+
version: "1.0.0"
|
|
17
|
+
|
|
18
|
+
# Destination type
|
|
19
|
+
type: postgres
|
|
20
|
+
|
|
21
|
+
# Database connection
|
|
22
|
+
connection_string: ${DATABASE_URL}
|
|
23
|
+
# Or use database object:
|
|
24
|
+
# database:
|
|
25
|
+
# url: ${DATABASE_URL}
|
|
26
|
+
|
|
27
|
+
# Target table
|
|
28
|
+
schema: public
|
|
29
|
+
table: orders
|
|
30
|
+
|
|
31
|
+
# Write method
|
|
32
|
+
write_method: upsert # insert, upsert, replace, truncate_and_load
|
|
33
|
+
primary_key: order_id
|
|
34
|
+
|
|
35
|
+
# Batch size
|
|
36
|
+
batch_size: 500
|
|
37
|
+
|
|
38
|
+
# ============================================================================
|
|
39
|
+
# Target Contract Validation (Optional)
|
|
40
|
+
# ============================================================================
|
|
41
|
+
# Validate transformed data against a contract before loading.
|
|
42
|
+
# This ensures data conforms to the target table schema and business rules.
|
|
43
|
+
|
|
44
|
+
validation:
|
|
45
|
+
# Option 1: Path to contract directory or file
|
|
46
|
+
# Contract directory should contain: schema.yaml, coercion_rules.yaml (optional),
|
|
47
|
+
# validation_rules.yaml (optional)
|
|
48
|
+
contract: ./contracts/orders
|
|
49
|
+
|
|
50
|
+
# Option 2: Reference contract from metadata store (requires settings.yaml metadata_store)
|
|
51
|
+
# contract:
|
|
52
|
+
# type: database
|
|
53
|
+
# name: orders_contract
|
|
54
|
+
# version: "2.0" # Optional, defaults to latest
|
|
55
|
+
|
|
56
|
+
# Option 3: Use default contract from settings.yaml
|
|
57
|
+
# use_contract: true
|
|
58
|
+
|
|
59
|
+
# Action when validation fails:
|
|
60
|
+
# - fail: Stop pipeline immediately (default)
|
|
61
|
+
# - warn: Log warning but continue with all records
|
|
62
|
+
# - skip: Skip invalid records (don't load them)
|
|
63
|
+
# - quarantine: Send invalid records to DLQ
|
|
64
|
+
on_error: quarantine
|
|
65
|
+
|
|
66
|
+
# DLQ configuration (optional, overrides settings.yaml)
|
|
67
|
+
dlq: true # Use default DLQ from settings.yaml
|
|
68
|
+
|
|
69
|
+
# ============================================================================
|
|
70
|
+
# Example Contract Structure (create in ./contracts/orders/)
|
|
71
|
+
# ============================================================================
|
|
72
|
+
#
|
|
73
|
+
# contracts/orders/schema.yaml:
|
|
74
|
+
# ---
|
|
75
|
+
# version: "1.0.0"
|
|
76
|
+
# type: object
|
|
77
|
+
# properties:
|
|
78
|
+
# order_id:
|
|
79
|
+
# type: string
|
|
80
|
+
# maxLength: 20
|
|
81
|
+
# customer_id:
|
|
82
|
+
# type: integer
|
|
83
|
+
# status:
|
|
84
|
+
# type: string
|
|
85
|
+
# enum: ["pending", "processing", "shipped", "delivered", "cancelled"]
|
|
86
|
+
# total_amount:
|
|
87
|
+
# type: number
|
|
88
|
+
# minimum: 0
|
|
89
|
+
# created_at:
|
|
90
|
+
# type: string
|
|
91
|
+
# format: date-time
|
|
92
|
+
# required:
|
|
93
|
+
# - order_id
|
|
94
|
+
# - customer_id
|
|
95
|
+
# - status
|
|
96
|
+
#
|
|
97
|
+
# contracts/orders/coercion_rules.yaml:
|
|
98
|
+
# ---
|
|
99
|
+
# order_id: string
|
|
100
|
+
# customer_id: int
|
|
101
|
+
# total_amount: float
|
|
102
|
+
# created_at: datetime
|
|
103
|
+
#
|
|
104
|
+
# contracts/orders/validation_rules.yaml:
|
|
105
|
+
# ---
|
|
106
|
+
# order_id:
|
|
107
|
+
# - not_empty
|
|
108
|
+
# - max_length: 20
|
|
109
|
+
# total_amount:
|
|
110
|
+
# - min_value: 0
|
|
111
|
+
# - max_value: 1000000
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Template: Pipeline Settings
|
|
2
|
+
# Copy to your pipeline directory as settings.yaml
|
|
3
|
+
#
|
|
4
|
+
# This file provides shared configuration for the pipeline:
|
|
5
|
+
# - Dead Letter Queue (DLQ) settings for quarantining invalid records
|
|
6
|
+
# - Metadata store connection for database-backed contracts
|
|
7
|
+
# - Default contract name for load validation
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# pipeline = Pipeline.from_config_dir("path/to/pipeline")
|
|
11
|
+
# result = await pipeline.run()
|
|
12
|
+
|
|
13
|
+
title: pipeline_settings
|
|
14
|
+
description: Shared pipeline configuration
|
|
15
|
+
version: "1.0.0"
|
|
16
|
+
|
|
17
|
+
# ============================================================================
|
|
18
|
+
# Dead Letter Queue (DLQ) Configuration
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# Records that fail validation can be sent to a DLQ for later review.
|
|
21
|
+
# Configure this at the settings level to share across extract and load stages.
|
|
22
|
+
|
|
23
|
+
dlq:
|
|
24
|
+
# Enable/disable DLQ
|
|
25
|
+
enabled: true
|
|
26
|
+
|
|
27
|
+
# Storage backend: "file", "database", or "memory"
|
|
28
|
+
backend: file
|
|
29
|
+
|
|
30
|
+
# File backend options
|
|
31
|
+
path: ./dlq # Directory to store quarantined records
|
|
32
|
+
|
|
33
|
+
# Database backend options (uncomment to use)
|
|
34
|
+
# backend: database
|
|
35
|
+
# connection_string: ${DATABASE_URL}
|
|
36
|
+
# schema: pycharter # Database schema for DLQ table
|
|
37
|
+
# table: dead_letter_queue # Optional custom table name
|
|
38
|
+
|
|
39
|
+
# ============================================================================
|
|
40
|
+
# Metadata Store Configuration (Optional)
|
|
41
|
+
# ============================================================================
|
|
42
|
+
# If you store contracts in a database (using MetadataStoreClient), configure
|
|
43
|
+
# the connection here. This allows load validation to reference contracts by name.
|
|
44
|
+
|
|
45
|
+
# metadata_store:
|
|
46
|
+
# type: postgres # or sqlite, mongodb, redis, memory
|
|
47
|
+
# connection_string: ${DATABASE_URL}
|
|
48
|
+
|
|
49
|
+
# ============================================================================
|
|
50
|
+
# Default Contract (Optional)
|
|
51
|
+
# ============================================================================
|
|
52
|
+
# Specify a default contract name for load validation when using `use_contract: true`
|
|
53
|
+
# in your load config. The contract must exist in the metadata_store.
|
|
54
|
+
|
|
55
|
+
# contract: orders_v2
|
pycharter/db/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Database Management with Alembic
|
|
2
|
+
|
|
3
|
+
PyCharter uses **Alembic** for database schema migrations, following the same pattern as Apache Airflow.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
- **SQLAlchemy Models**: Database schema defined as Python models in `pycharter/db/models/`
|
|
8
|
+
- **Alembic Migrations**: Version-controlled schema changes in `pycharter/db/migrations/`
|
|
9
|
+
- **CLI Commands**: `pycharter db init`, `pycharter db upgrade`, etc.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Initialize Database
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Initialize a fresh database
|
|
17
|
+
pycharter db init postgresql://user:pass@localhost/pycharter
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This will:
|
|
21
|
+
1. Create all tables defined in SQLAlchemy models
|
|
22
|
+
2. Create the initial Alembic migration
|
|
23
|
+
3. Stamp the database with the current revision
|
|
24
|
+
|
|
25
|
+
### Upgrade Database
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Upgrade to latest version
|
|
29
|
+
pycharter db upgrade postgresql://user:pass@localhost/pycharter
|
|
30
|
+
|
|
31
|
+
# Or use environment variable
|
|
32
|
+
export PYCHARTER_DATABASE_URL=postgresql://user:pass@localhost/pycharter
|
|
33
|
+
pycharter db upgrade
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Check Current Version
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pycharter db current postgresql://user:pass@localhost/pycharter
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### View Migration History
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pycharter db history
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Commands
|
|
49
|
+
|
|
50
|
+
| Command | Description | Example |
|
|
51
|
+
|---------|-------------|---------|
|
|
52
|
+
| `pycharter db init` | Initialize database from scratch | `pycharter db init postgresql://...` |
|
|
53
|
+
| `pycharter db upgrade` | Upgrade to latest revision | `pycharter db upgrade postgresql://...` |
|
|
54
|
+
| `pycharter db downgrade` | Downgrade to previous revision | `pycharter db downgrade postgresql://... --revision -1` |
|
|
55
|
+
| `pycharter db current` | Show current revision | `pycharter db current postgresql://...` |
|
|
56
|
+
| `pycharter db history` | Show migration history | `pycharter db history` |
|
|
57
|
+
|
|
58
|
+
## Creating New Migrations
|
|
59
|
+
|
|
60
|
+
When you modify SQLAlchemy models, create a new migration:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export PYCHARTER_DATABASE_URL=postgresql://user:pass@localhost/pycharter
|
|
64
|
+
alembic revision --autogenerate -m "Description of changes"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then review and edit the generated migration file in `pycharter/db/migrations/versions/`.
|
|
68
|
+
|
|
69
|
+
## Database Models
|
|
70
|
+
|
|
71
|
+
All models are defined in `pycharter/db/models/`:
|
|
72
|
+
|
|
73
|
+
- `SchemaModel` - JSON Schema definitions
|
|
74
|
+
- `CoercionRuleModel` - Coercion rules
|
|
75
|
+
- `ValidationRuleModel` - Validation rules
|
|
76
|
+
- `MetadataRecordModel` - Comprehensive metadata storage (includes governance rules and ownership fields)
|
|
77
|
+
- `OwnerModel` - Direct ownership information (owner/team)
|
|
78
|
+
- `DataContractModel` - Central table linking all components
|
|
79
|
+
- `SystemModel` - System information
|
|
80
|
+
- `DomainModel` - Domain information
|
|
81
|
+
|
|
82
|
+
## Comparison with Airflow
|
|
83
|
+
|
|
84
|
+
| Feature | Airflow | PyCharter |
|
|
85
|
+
|---------|---------|-----------|
|
|
86
|
+
| **CLI Command** | `airflow db init` | `pycharter db init` |
|
|
87
|
+
| **Upgrade** | `airflow db upgrade` | `pycharter db upgrade` |
|
|
88
|
+
| **Migration Tool** | Alembic | Alembic |
|
|
89
|
+
| **Models** | SQLAlchemy | SQLAlchemy |
|
|
90
|
+
| **Auto-generate** | Yes | Yes |
|
|
91
|
+
|
|
92
|
+
## Best Practices
|
|
93
|
+
|
|
94
|
+
1. **Always create migrations** when changing models
|
|
95
|
+
2. **Test migrations** on a development database first
|
|
96
|
+
3. **Review autogenerated migrations** before applying
|
|
97
|
+
4. **Use version control** for all migration files
|
|
98
|
+
5. **Backup database** before running migrations in production
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
PyCharter follows Airflow's configuration pattern. Database connection can be configured via:
|
|
103
|
+
|
|
104
|
+
### 1. Environment Variables (Recommended)
|
|
105
|
+
|
|
106
|
+
**Airflow-style:**
|
|
107
|
+
```bash
|
|
108
|
+
export PYCHARTER__DATABASE__SQL_ALCHEMY_CONN=postgresql://user:password@localhost:5432/pycharter
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Simpler alternative:**
|
|
112
|
+
```bash
|
|
113
|
+
export PYCHARTER_DATABASE_URL=postgresql://user:password@localhost:5432/pycharter
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 2. Config File
|
|
117
|
+
|
|
118
|
+
Create `pycharter.cfg` in your project root or `~/.pycharter/`:
|
|
119
|
+
|
|
120
|
+
```ini
|
|
121
|
+
[database]
|
|
122
|
+
sql_alchemy_conn = postgresql://user:password@localhost:5432/pycharter
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 3. Alembic Config
|
|
126
|
+
|
|
127
|
+
Set in `alembic.ini`:
|
|
128
|
+
|
|
129
|
+
```ini
|
|
130
|
+
[alembic]
|
|
131
|
+
sqlalchemy.url = postgresql://user:password@localhost:5432/pycharter
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Priority Order
|
|
135
|
+
|
|
136
|
+
1. Command-line argument (if provided)
|
|
137
|
+
2. `PYCHARTER__DATABASE__SQL_ALCHEMY_CONN` environment variable
|
|
138
|
+
3. `PYCHARTER_DATABASE_URL` environment variable
|
|
139
|
+
4. `pycharter.cfg` config file
|
|
140
|
+
5. `alembic.ini` config file
|
|
141
|
+
|
|
142
|
+
### Usage
|
|
143
|
+
|
|
144
|
+
Once configured, you can run commands without passing the database URL:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Database URL from configuration
|
|
148
|
+
pycharter db init
|
|
149
|
+
pycharter db upgrade
|
|
150
|
+
pycharter db current
|
|
151
|
+
|
|
152
|
+
# Or override with command-line argument
|
|
153
|
+
pycharter db init postgresql://other:db@localhost:5432/other_db
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Troubleshooting
|
|
157
|
+
|
|
158
|
+
### Tables Already Exist
|
|
159
|
+
|
|
160
|
+
If tables already exist (from old migration system), stamp the database:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
alembic stamp head
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Migration Conflicts
|
|
167
|
+
|
|
168
|
+
If you have conflicts, you can:
|
|
169
|
+
1. Review the migration file
|
|
170
|
+
2. Manually edit if needed
|
|
171
|
+
3. Test on a development database first
|
|
172
|
+
|
|
173
|
+
### Connection Issues
|
|
174
|
+
|
|
175
|
+
Make sure:
|
|
176
|
+
- Database is running
|
|
177
|
+
- Connection string is correct
|
|
178
|
+
- User has proper permissions
|
|
179
|
+
|
pycharter/db/cli.py
CHANGED
|
@@ -27,15 +27,20 @@ except ImportError:
|
|
|
27
27
|
from pycharter.config import get_database_url, set_database_url
|
|
28
28
|
from pycharter.db.models import (
|
|
29
29
|
APIEndpointModel,
|
|
30
|
+
CoercionRuleModel,
|
|
30
31
|
ComplianceFrameworkModel,
|
|
32
|
+
DataContractModel,
|
|
31
33
|
DataFeedModel,
|
|
32
34
|
DomainModel,
|
|
33
35
|
EnvironmentModel,
|
|
36
|
+
MetadataRecordModel,
|
|
34
37
|
OwnerModel,
|
|
38
|
+
SchemaModel,
|
|
35
39
|
SystemModel,
|
|
36
40
|
TagModel,
|
|
41
|
+
ValidationRuleModel,
|
|
37
42
|
)
|
|
38
|
-
from pycharter.db.models.base import Base, get_session
|
|
43
|
+
from pycharter.db.models.base import Base, get_engine, get_session
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
# Helper functions
|
|
@@ -268,7 +273,7 @@ def _init_sqlite(database_url: str, force: bool = False) -> int:
|
|
|
268
273
|
if db_path != ":memory:":
|
|
269
274
|
Path(db_path).parent.mkdir(parents=True, exist_ok=True)
|
|
270
275
|
|
|
271
|
-
engine =
|
|
276
|
+
engine = get_engine(database_url)
|
|
272
277
|
existing_tables = inspect(engine).get_table_names()
|
|
273
278
|
|
|
274
279
|
if existing_tables and not force:
|
|
@@ -276,6 +281,15 @@ def _init_sqlite(database_url: str, force: bool = False) -> int:
|
|
|
276
281
|
print(" Use --force to reinitialize, or run 'pycharter db upgrade' to apply migrations.")
|
|
277
282
|
return 1
|
|
278
283
|
|
|
284
|
+
if existing_tables and force:
|
|
285
|
+
with engine.connect() as conn:
|
|
286
|
+
conn.execute(text("PRAGMA foreign_keys=OFF"))
|
|
287
|
+
for name in existing_tables:
|
|
288
|
+
conn.execute(text(f'DROP TABLE IF EXISTS "{name}"'))
|
|
289
|
+
conn.execute(text("PRAGMA foreign_keys=ON"))
|
|
290
|
+
conn.commit()
|
|
291
|
+
print("✓ Dropped existing tables")
|
|
292
|
+
|
|
279
293
|
Base.metadata.create_all(engine)
|
|
280
294
|
print("✓ Created tables using SQLAlchemy models")
|
|
281
295
|
|
|
@@ -502,8 +516,9 @@ def cmd_seed(seed_dir: Optional[str] = None, database_url: Optional[str] = None)
|
|
|
502
516
|
if seed_dir:
|
|
503
517
|
seed_path = Path(seed_dir)
|
|
504
518
|
else:
|
|
505
|
-
|
|
506
|
-
|
|
519
|
+
# Default: package-bundled seed under src/pycharter/data/seed
|
|
520
|
+
pkg_root = Path(__file__).resolve().parent.parent
|
|
521
|
+
seed_path = pkg_root / "data" / "seed"
|
|
507
522
|
|
|
508
523
|
if not seed_path.exists():
|
|
509
524
|
print(f"❌ Error: Seed directory not found: {seed_path}")
|
|
@@ -522,6 +537,112 @@ def cmd_seed(seed_dir: Optional[str] = None, database_url: Optional[str] = None)
|
|
|
522
537
|
return 1
|
|
523
538
|
|
|
524
539
|
|
|
540
|
+
def _seed_contract_artifacts(seed_path: Path, session: Any) -> int:
|
|
541
|
+
"""Seed contract artifacts from contracts.yaml (data contract + schema, coercion, validation, metadata)."""
|
|
542
|
+
contracts_file = seed_path / "contracts.yaml"
|
|
543
|
+
if not contracts_file.exists():
|
|
544
|
+
print("⚠ No contracts.yaml found, skipping contract artifacts")
|
|
545
|
+
return 0
|
|
546
|
+
with open(contracts_file, "r") as f:
|
|
547
|
+
contracts = yaml.safe_load(f) or []
|
|
548
|
+
if not contracts:
|
|
549
|
+
return 0
|
|
550
|
+
print("Loading contract artifacts...")
|
|
551
|
+
count = 0
|
|
552
|
+
for entry in contracts:
|
|
553
|
+
name = entry.get("name")
|
|
554
|
+
version = entry.get("version")
|
|
555
|
+
if not name or not version:
|
|
556
|
+
print("⚠ Skipping contract entry missing name or version")
|
|
557
|
+
continue
|
|
558
|
+
existing = session.query(DataContractModel).filter(
|
|
559
|
+
DataContractModel.name == name,
|
|
560
|
+
DataContractModel.version == version,
|
|
561
|
+
).first()
|
|
562
|
+
if existing:
|
|
563
|
+
print(f" Skipped (exists): {name}@{version}")
|
|
564
|
+
continue
|
|
565
|
+
# Create data contract with null FKs
|
|
566
|
+
contract = DataContractModel(
|
|
567
|
+
name=name,
|
|
568
|
+
version=version,
|
|
569
|
+
status=entry.get("status") or "active",
|
|
570
|
+
description=entry.get("description"),
|
|
571
|
+
)
|
|
572
|
+
session.add(contract)
|
|
573
|
+
session.flush()
|
|
574
|
+
data_contract_id = contract.id
|
|
575
|
+
# Schema
|
|
576
|
+
schema_def = entry.get("schema") or {}
|
|
577
|
+
schema_title = schema_def.get("title") or f"{name}_schema"
|
|
578
|
+
schema_version = schema_def.get("version") or version
|
|
579
|
+
schema_data = {k: v for k, v in schema_def.items() if k not in ("title", "version")}
|
|
580
|
+
schema_data["title"] = schema_def.get("title", schema_title)
|
|
581
|
+
schema_data["version"] = schema_version
|
|
582
|
+
schema_row = SchemaModel(
|
|
583
|
+
title=schema_title,
|
|
584
|
+
data_contract_id=data_contract_id,
|
|
585
|
+
version=schema_version,
|
|
586
|
+
schema_data=schema_data,
|
|
587
|
+
)
|
|
588
|
+
session.add(schema_row)
|
|
589
|
+
session.flush()
|
|
590
|
+
schema_id = schema_row.id
|
|
591
|
+
# Coercion rules
|
|
592
|
+
cr_def = entry.get("coercion_rules") or {}
|
|
593
|
+
cr_title = cr_def.get("title") or f"{name}_coercion"
|
|
594
|
+
cr_version = cr_def.get("version") or version
|
|
595
|
+
cr_row = CoercionRuleModel(
|
|
596
|
+
title=cr_title,
|
|
597
|
+
data_contract_id=data_contract_id,
|
|
598
|
+
version=cr_version,
|
|
599
|
+
rules=cr_def.get("rules") or {},
|
|
600
|
+
description=cr_def.get("description"),
|
|
601
|
+
schema_id=schema_id,
|
|
602
|
+
)
|
|
603
|
+
session.add(cr_row)
|
|
604
|
+
session.flush()
|
|
605
|
+
# Validation rules
|
|
606
|
+
vr_def = entry.get("validation_rules") or {}
|
|
607
|
+
vr_title = vr_def.get("title") or f"{name}_validation"
|
|
608
|
+
vr_version = vr_def.get("version") or version
|
|
609
|
+
vr_row = ValidationRuleModel(
|
|
610
|
+
title=vr_title,
|
|
611
|
+
data_contract_id=data_contract_id,
|
|
612
|
+
version=vr_version,
|
|
613
|
+
rules=vr_def.get("rules") or {},
|
|
614
|
+
description=vr_def.get("description"),
|
|
615
|
+
schema_id=schema_id,
|
|
616
|
+
)
|
|
617
|
+
session.add(vr_row)
|
|
618
|
+
session.flush()
|
|
619
|
+
# Metadata record
|
|
620
|
+
meta_def = entry.get("metadata") or {}
|
|
621
|
+
meta_title = meta_def.get("title") or f"{name}_metadata"
|
|
622
|
+
meta_version = meta_def.get("version") or version
|
|
623
|
+
meta_row = MetadataRecordModel(
|
|
624
|
+
title=meta_title,
|
|
625
|
+
data_contract_id=data_contract_id,
|
|
626
|
+
version=meta_version,
|
|
627
|
+
status=meta_def.get("status"),
|
|
628
|
+
description=meta_def.get("description"),
|
|
629
|
+
governance_rules=meta_def.get("governance_rules"),
|
|
630
|
+
)
|
|
631
|
+
session.add(meta_row)
|
|
632
|
+
session.flush()
|
|
633
|
+
# Link contract to artifacts
|
|
634
|
+
contract.schema_id = schema_id
|
|
635
|
+
contract.coercion_rules_id = cr_row.id
|
|
636
|
+
contract.validation_rules_id = vr_row.id
|
|
637
|
+
contract.metadata_record_id = meta_row.id
|
|
638
|
+
session.commit()
|
|
639
|
+
print(f" Created contract: {name}@{version}")
|
|
640
|
+
count += 1
|
|
641
|
+
if count:
|
|
642
|
+
print(f"✓ Loaded {count} contract(s)")
|
|
643
|
+
return count
|
|
644
|
+
|
|
645
|
+
|
|
525
646
|
def _seed_postgresql(seed_path: Path, db_url: str) -> int:
|
|
526
647
|
"""Seed PostgreSQL database with data from YAML files."""
|
|
527
648
|
_require_alembic()
|
|
@@ -535,6 +656,7 @@ def _seed_postgresql(seed_path: Path, db_url: str) -> int:
|
|
|
535
656
|
_seed_model(session, DataFeedModel, seed_path / "data_feeds.yaml", "data_feeds", "name")
|
|
536
657
|
_seed_model(session, ComplianceFrameworkModel, seed_path / "compliance_frameworks.yaml", "compliance_frameworks", "name")
|
|
537
658
|
_seed_model(session, TagModel, seed_path / "tags.yaml", "tags", "name")
|
|
659
|
+
_seed_contract_artifacts(seed_path, session)
|
|
538
660
|
print("\n✓ Seed data loaded successfully!")
|
|
539
661
|
return 0
|
|
540
662
|
except Exception as e:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Change artifact unique constraints from (data_contract_id, version) to (title, version)
|
|
2
2
|
|
|
3
|
-
Revision ID:
|
|
3
|
+
Revision ID: change_artifact_uniq_constraints
|
|
4
4
|
Revises: remove_artifact_versions
|
|
5
5
|
Create Date: 2026-01-22 00:00:00.000000
|
|
6
6
|
|
|
@@ -20,7 +20,7 @@ from alembic import op
|
|
|
20
20
|
import sqlalchemy as sa
|
|
21
21
|
|
|
22
22
|
# revision identifiers, used by Alembic.
|
|
23
|
-
revision: str = '
|
|
23
|
+
revision: str = 'change_artifact_uniq_constraints'
|
|
24
24
|
down_revision: Union[str, None] = 'remove_artifact_versions'
|
|
25
25
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
26
26
|
depends_on: Union[str, Sequence[str], None] = None
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Data Contract Validation Schemas
|
|
2
|
+
|
|
3
|
+
This directory contains Pydantic models for validating data contract structure.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The Pydantic models in this directory ensure that all data contracts parsed by `parse_contract()` and `parse_contract_file()` strictly adhere to the database table design.
|
|
8
|
+
|
|
9
|
+
## Files
|
|
10
|
+
|
|
11
|
+
- `data_contract.py` - Main Pydantic models for data contract validation
|
|
12
|
+
- `DataContractSchema` - Main model for validating complete contracts
|
|
13
|
+
- `SchemaComponent` - Validates the schema component (required)
|
|
14
|
+
- `MetadataComponent` - Validates metadata component (optional)
|
|
15
|
+
- `OwnershipComponent` - Validates ownership component (optional)
|
|
16
|
+
- `GovernanceRulesComponent` - Validates governance rules (optional)
|
|
17
|
+
- `CoercionRulesComponent` - Validates coercion rules (optional)
|
|
18
|
+
- `ValidationRulesComponent` - Validates validation rules (optional)
|
|
19
|
+
- `VersionsComponent` - Validates version tracking (optional)
|
|
20
|
+
|
|
21
|
+
## Database Alignment
|
|
22
|
+
|
|
23
|
+
All models are designed to match the database table structure exactly:
|
|
24
|
+
|
|
25
|
+
- **Field constraints** (max_length) match database column sizes
|
|
26
|
+
- **Required fields** match database NOT NULL constraints
|
|
27
|
+
- **Optional fields** match database nullable columns
|
|
28
|
+
- **Enum values** match database CHECK constraints
|
|
29
|
+
- **Field types** match database column types
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
The models are automatically used by the contract parser:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from pycharter import parse_contract, parse_contract_file
|
|
37
|
+
|
|
38
|
+
# Validation is enabled by default
|
|
39
|
+
contract_metadata = parse_contract_file("contract.yaml") # Validates automatically
|
|
40
|
+
|
|
41
|
+
# You can disable validation if needed (not recommended)
|
|
42
|
+
contract_metadata = parse_contract_file("contract.yaml", validate=False)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Validation Behavior
|
|
46
|
+
|
|
47
|
+
- **Valid contracts**: Pass validation and parse successfully
|
|
48
|
+
- **Invalid contracts**: Raise `ValueError` with detailed Pydantic error messages
|
|
49
|
+
- **Missing required fields**: Validation fails with clear error messages
|
|
50
|
+
- **Extra fields**: Rejected (except in flexible components like metadata)
|
|
51
|
+
|
|
52
|
+
## Model Design Principles
|
|
53
|
+
|
|
54
|
+
1. **Strict Top-Level**: `DataContractSchema` uses `extra = "forbid"` to prevent unexpected fields
|
|
55
|
+
2. **Flexible Components**: Metadata, governance_rules allow `extra = "allow"` for flexible JSON storage
|
|
56
|
+
3. **Database Constraints**: All field constraints match database column definitions
|
|
57
|
+
4. **Type Safety**: Full Python type hints for IDE support and static analysis
|
|
58
|
+
|
|
59
|
+
## Updating Models
|
|
60
|
+
|
|
61
|
+
When the database schema changes:
|
|
62
|
+
|
|
63
|
+
1. Update the corresponding Pydantic model in `data_contract.py`
|
|
64
|
+
2. Update field constraints to match new database columns
|
|
65
|
+
3. Add/remove fields as needed
|
|
66
|
+
4. Test with existing contracts to ensure backward compatibility
|
|
67
|
+
5. Update this README if model structure changes significantly
|
|
68
|
+
|
|
69
|
+
## Testing
|
|
70
|
+
|
|
71
|
+
To test the validation models:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pycharter.db.schemas.data_contract import DataContractSchema
|
|
75
|
+
|
|
76
|
+
# Valid contract
|
|
77
|
+
valid_contract = {
|
|
78
|
+
"schema": {"type": "object", "properties": {"name": {"type": "string"}}}
|
|
79
|
+
}
|
|
80
|
+
result = DataContractSchema.model_validate(valid_contract) # ✓ Passes
|
|
81
|
+
|
|
82
|
+
# Invalid contract (missing required schema)
|
|
83
|
+
invalid_contract = {
|
|
84
|
+
"metadata": {"version": "1.0.0"}
|
|
85
|
+
}
|
|
86
|
+
result = DataContractSchema.model_validate(invalid_contract) # ✗ Raises ValidationError
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|