pycharter 0.0.25__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 +1 -1
- pycharter/api/dependencies/auth.py +158 -0
- pycharter/api/main.py +30 -2
- pycharter/api/models/etl.py +66 -0
- pycharter/api/routes/v1/__init__.py +4 -0
- pycharter/api/routes/v1/auth.py +97 -0
- pycharter/api/routes/v1/contracts.py +10 -8
- pycharter/api/routes/v1/etl.py +131 -0
- pycharter/cli.py +1 -1
- 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 +31 -14
- 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/cli.py +126 -4
- pycharter/db/migrations/versions/20260122000000_change_artifact_unique_constraints_to_title_version.py +2 -2
- pycharter/etl_generator/INTERFACES.md +6 -7
- 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/runtime_validator/__init__.py +7 -0
- pycharter/runtime_validator/utils.py +33 -0
- pycharter/runtime_validator/validator.py +13 -10
- pycharter/ui/package-lock.json +50 -41
- pycharter/ui/package.json +2 -1
- pycharter/ui/static/404/index.html +1 -1
- pycharter/ui/static/404.html +1 -1
- pycharter/ui/static/__next.__PAGE__.txt +2 -2
- pycharter/ui/static/__next._full.txt +7 -7
- pycharter/ui/static/__next._head.txt +1 -1
- pycharter/ui/static/__next._index.txt +6 -6
- pycharter/ui/static/__next._tree.txt +2 -2
- 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 +12 -12
- pycharter/ui/static/_not-found/__next._head.txt +3 -3
- pycharter/ui/static/_not-found/__next._index.txt +8 -8
- pycharter/ui/static/_not-found/__next._not-found.__PAGE__.txt +2 -2
- pycharter/ui/static/_not-found/__next._not-found.txt +3 -3
- pycharter/ui/static/_not-found/__next._tree.txt +2 -2
- pycharter/ui/static/_not-found/index.html +1 -1
- pycharter/ui/static/_not-found/index.txt +12 -12
- pycharter/ui/static/contracts/__next._full.txt +7 -7
- pycharter/ui/static/contracts/__next._head.txt +1 -1
- pycharter/ui/static/contracts/__next._index.txt +6 -6
- pycharter/ui/static/contracts/__next._tree.txt +2 -2
- pycharter/ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- pycharter/ui/static/contracts/__next.contracts.txt +1 -1
- pycharter/ui/static/contracts/index.html +1 -1
- pycharter/ui/static/contracts/index.txt +7 -7
- pycharter/ui/static/documentation/__next._full.txt +7 -7
- pycharter/ui/static/documentation/__next._head.txt +1 -1
- pycharter/ui/static/documentation/__next._index.txt +6 -6
- pycharter/ui/static/documentation/__next._tree.txt +2 -2
- pycharter/ui/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- pycharter/ui/static/documentation/__next.documentation.txt +1 -1
- pycharter/ui/static/documentation/index.html +3 -3
- pycharter/ui/static/documentation/index.txt +7 -7
- 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 -1
- pycharter/ui/static/index.txt +7 -7
- pycharter/ui/static/metadata/__next._full.txt +7 -7
- pycharter/ui/static/metadata/__next._head.txt +1 -1
- pycharter/ui/static/metadata/__next._index.txt +6 -6
- pycharter/ui/static/metadata/__next._tree.txt +2 -2
- pycharter/ui/static/metadata/__next.metadata.__PAGE__.txt +2 -2
- pycharter/ui/static/metadata/__next.metadata.txt +1 -1
- pycharter/ui/static/metadata/index.html +1 -1
- pycharter/ui/static/metadata/index.txt +7 -7
- pycharter/ui/static/quality/__next._full.txt +7 -7
- pycharter/ui/static/quality/__next._head.txt +1 -1
- pycharter/ui/static/quality/__next._index.txt +6 -6
- pycharter/ui/static/quality/__next._tree.txt +2 -2
- pycharter/ui/static/quality/__next.quality.__PAGE__.txt +2 -2
- pycharter/ui/static/quality/__next.quality.txt +1 -1
- pycharter/ui/static/quality/index.html +2 -2
- pycharter/ui/static/quality/index.txt +7 -7
- pycharter/ui/static/rules/__next._full.txt +7 -7
- pycharter/ui/static/rules/__next._head.txt +1 -1
- pycharter/ui/static/rules/__next._index.txt +6 -6
- pycharter/ui/static/rules/__next._tree.txt +2 -2
- pycharter/ui/static/rules/__next.rules.__PAGE__.txt +2 -2
- pycharter/ui/static/rules/__next.rules.txt +1 -1
- pycharter/ui/static/rules/index.html +1 -1
- pycharter/ui/static/rules/index.txt +7 -7
- pycharter/ui/static/schemas/__next._full.txt +7 -7
- pycharter/ui/static/schemas/__next._head.txt +1 -1
- pycharter/ui/static/schemas/__next._index.txt +6 -6
- pycharter/ui/static/schemas/__next._tree.txt +2 -2
- pycharter/ui/static/schemas/__next.schemas.__PAGE__.txt +2 -2
- pycharter/ui/static/schemas/__next.schemas.txt +1 -1
- pycharter/ui/static/schemas/index.html +1 -1
- pycharter/ui/static/schemas/index.txt +7 -7
- pycharter/ui/static/settings/__next._full.txt +7 -7
- pycharter/ui/static/settings/__next._head.txt +1 -1
- pycharter/ui/static/settings/__next._index.txt +6 -6
- pycharter/ui/static/settings/__next._tree.txt +2 -2
- pycharter/ui/static/settings/__next.settings.__PAGE__.txt +2 -2
- pycharter/ui/static/settings/__next.settings.txt +1 -1
- pycharter/ui/static/settings/index.html +1 -1
- pycharter/ui/static/settings/index.txt +7 -7
- pycharter/ui/static/static/404/index.html +1 -1
- pycharter/ui/static/static/404.html +1 -1
- pycharter/ui/static/static/__next.__PAGE__.txt +1 -1
- pycharter/ui/static/static/__next._full.txt +1 -1
- pycharter/ui/static/static/__next._head.txt +1 -1
- pycharter/ui/static/static/__next._index.txt +1 -1
- pycharter/ui/static/static/__next._tree.txt +1 -1
- pycharter/ui/static/static/_not-found/__next._full.txt +1 -1
- pycharter/ui/static/static/_not-found/__next._head.txt +1 -1
- pycharter/ui/static/static/_not-found/__next._index.txt +1 -1
- pycharter/ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- pycharter/ui/static/static/_not-found/__next._not-found.txt +1 -1
- pycharter/ui/static/static/_not-found/__next._tree.txt +1 -1
- pycharter/ui/static/static/_not-found/index.html +1 -1
- pycharter/ui/static/static/_not-found/index.txt +1 -1
- pycharter/ui/static/static/contracts/__next._full.txt +2 -2
- pycharter/ui/static/static/contracts/__next._head.txt +1 -1
- pycharter/ui/static/static/contracts/__next._index.txt +1 -1
- pycharter/ui/static/static/contracts/__next._tree.txt +1 -1
- pycharter/ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- pycharter/ui/static/static/contracts/__next.contracts.txt +1 -1
- pycharter/ui/static/static/contracts/index.html +1 -1
- pycharter/ui/static/static/contracts/index.txt +2 -2
- pycharter/ui/static/static/documentation/__next._full.txt +1 -1
- pycharter/ui/static/static/documentation/__next._head.txt +1 -1
- pycharter/ui/static/static/documentation/__next._index.txt +1 -1
- pycharter/ui/static/static/documentation/__next._tree.txt +1 -1
- pycharter/ui/static/static/documentation/__next.documentation.__PAGE__.txt +1 -1
- pycharter/ui/static/static/documentation/__next.documentation.txt +1 -1
- pycharter/ui/static/static/documentation/index.html +2 -2
- pycharter/ui/static/static/documentation/index.txt +1 -1
- pycharter/ui/static/static/index.html +1 -1
- pycharter/ui/static/static/index.txt +1 -1
- pycharter/ui/static/static/metadata/__next._full.txt +1 -1
- pycharter/ui/static/static/metadata/__next._head.txt +1 -1
- pycharter/ui/static/static/metadata/__next._index.txt +1 -1
- pycharter/ui/static/static/metadata/__next._tree.txt +1 -1
- pycharter/ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- pycharter/ui/static/static/metadata/__next.metadata.txt +1 -1
- pycharter/ui/static/static/metadata/index.html +1 -1
- pycharter/ui/static/static/metadata/index.txt +1 -1
- pycharter/ui/static/static/quality/__next._full.txt +2 -2
- pycharter/ui/static/static/quality/__next._head.txt +1 -1
- pycharter/ui/static/static/quality/__next._index.txt +1 -1
- pycharter/ui/static/static/quality/__next._tree.txt +1 -1
- pycharter/ui/static/static/quality/__next.quality.__PAGE__.txt +2 -2
- pycharter/ui/static/static/quality/__next.quality.txt +1 -1
- pycharter/ui/static/static/quality/index.html +2 -2
- pycharter/ui/static/static/quality/index.txt +2 -2
- pycharter/ui/static/static/rules/__next._full.txt +1 -1
- pycharter/ui/static/static/rules/__next._head.txt +1 -1
- pycharter/ui/static/static/rules/__next._index.txt +1 -1
- pycharter/ui/static/static/rules/__next._tree.txt +1 -1
- pycharter/ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
- pycharter/ui/static/static/rules/__next.rules.txt +1 -1
- pycharter/ui/static/static/rules/index.html +1 -1
- pycharter/ui/static/static/rules/index.txt +1 -1
- pycharter/ui/static/static/schemas/__next._full.txt +1 -1
- pycharter/ui/static/static/schemas/__next._head.txt +1 -1
- pycharter/ui/static/static/schemas/__next._index.txt +1 -1
- pycharter/ui/static/static/schemas/__next._tree.txt +1 -1
- pycharter/ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- pycharter/ui/static/static/schemas/__next.schemas.txt +1 -1
- pycharter/ui/static/static/schemas/index.html +1 -1
- pycharter/ui/static/static/schemas/index.txt +1 -1
- pycharter/ui/static/static/settings/__next._full.txt +1 -1
- pycharter/ui/static/static/settings/__next._head.txt +1 -1
- pycharter/ui/static/static/settings/__next._index.txt +1 -1
- pycharter/ui/static/static/settings/__next._tree.txt +1 -1
- pycharter/ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
- pycharter/ui/static/static/settings/__next.settings.txt +1 -1
- pycharter/ui/static/static/settings/index.html +1 -1
- pycharter/ui/static/static/settings/index.txt +1 -1
- pycharter/ui/static/static/static/404/index.html +1 -1
- pycharter/ui/static/static/static/404.html +1 -1
- pycharter/ui/static/static/static/__next.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/__next._full.txt +2 -2
- pycharter/ui/static/static/static/__next._head.txt +1 -1
- pycharter/ui/static/static/static/__next._index.txt +2 -2
- pycharter/ui/static/static/static/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- pycharter/ui/static/static/static/_not-found/__next._full.txt +2 -2
- pycharter/ui/static/static/static/_not-found/__next._head.txt +1 -1
- pycharter/ui/static/static/static/_not-found/__next._index.txt +2 -2
- pycharter/ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/_not-found/__next._not-found.txt +1 -1
- pycharter/ui/static/static/static/_not-found/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/_not-found/index.html +1 -1
- pycharter/ui/static/static/static/_not-found/index.txt +2 -2
- pycharter/ui/static/static/static/contracts/__next._full.txt +3 -3
- pycharter/ui/static/static/static/contracts/__next._head.txt +1 -1
- pycharter/ui/static/static/static/contracts/__next._index.txt +2 -2
- pycharter/ui/static/static/static/contracts/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- pycharter/ui/static/static/static/contracts/__next.contracts.txt +1 -1
- pycharter/ui/static/static/static/contracts/index.html +1 -1
- pycharter/ui/static/static/static/contracts/index.txt +3 -3
- pycharter/ui/static/static/static/documentation/__next._full.txt +3 -3
- pycharter/ui/static/static/static/documentation/__next._head.txt +1 -1
- pycharter/ui/static/static/static/documentation/__next._index.txt +2 -2
- pycharter/ui/static/static/static/documentation/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- pycharter/ui/static/static/static/documentation/__next.documentation.txt +1 -1
- pycharter/ui/static/static/static/documentation/index.html +2 -2
- pycharter/ui/static/static/static/documentation/index.txt +3 -3
- pycharter/ui/static/static/static/index.html +1 -1
- pycharter/ui/static/static/static/index.txt +2 -2
- pycharter/ui/static/static/static/metadata/__next._full.txt +2 -2
- pycharter/ui/static/static/static/metadata/__next._head.txt +1 -1
- pycharter/ui/static/static/static/metadata/__next._index.txt +2 -2
- pycharter/ui/static/static/static/metadata/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/metadata/__next.metadata.txt +1 -1
- pycharter/ui/static/static/static/metadata/index.html +1 -1
- pycharter/ui/static/static/static/metadata/index.txt +2 -2
- pycharter/ui/static/static/static/quality/__next._full.txt +2 -2
- pycharter/ui/static/static/static/quality/__next._head.txt +1 -1
- pycharter/ui/static/static/static/quality/__next._index.txt +2 -2
- pycharter/ui/static/static/static/quality/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/quality/__next.quality.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/quality/__next.quality.txt +1 -1
- pycharter/ui/static/static/static/quality/index.html +2 -2
- pycharter/ui/static/static/static/quality/index.txt +2 -2
- pycharter/ui/static/static/static/rules/__next._full.txt +2 -2
- pycharter/ui/static/static/static/rules/__next._head.txt +1 -1
- pycharter/ui/static/static/static/rules/__next._index.txt +2 -2
- pycharter/ui/static/static/static/rules/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/rules/__next.rules.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/rules/__next.rules.txt +1 -1
- pycharter/ui/static/static/static/rules/index.html +1 -1
- pycharter/ui/static/static/static/rules/index.txt +2 -2
- pycharter/ui/static/static/static/schemas/__next._full.txt +2 -2
- pycharter/ui/static/static/static/schemas/__next._head.txt +1 -1
- pycharter/ui/static/static/static/schemas/__next._index.txt +2 -2
- pycharter/ui/static/static/static/schemas/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/schemas/__next.schemas.txt +1 -1
- pycharter/ui/static/static/static/schemas/index.html +1 -1
- pycharter/ui/static/static/static/schemas/index.txt +2 -2
- pycharter/ui/static/static/static/settings/__next._full.txt +2 -2
- pycharter/ui/static/static/static/settings/__next._head.txt +1 -1
- pycharter/ui/static/static/static/settings/__next._index.txt +2 -2
- pycharter/ui/static/static/static/settings/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/settings/__next.settings.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/settings/__next.settings.txt +1 -1
- pycharter/ui/static/static/static/settings/index.html +1 -1
- pycharter/ui/static/static/static/settings/index.txt +2 -2
- pycharter/ui/static/static/static/static/.gitkeep +0 -0
- pycharter/ui/static/static/static/static/404/index.html +1 -0
- pycharter/ui/static/static/static/static/404.html +1 -0
- pycharter/ui/static/static/static/static/__next.__PAGE__.txt +10 -0
- pycharter/ui/static/static/static/static/__next._full.txt +30 -0
- pycharter/ui/static/static/static/static/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/__next._tree.txt +2 -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/static/static/static/_not-found/__next._full.txt +17 -0
- pycharter/ui/static/static/static/static/_not-found/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/_not-found/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- pycharter/ui/static/static/static/static/_not-found/__next._not-found.txt +4 -0
- pycharter/ui/static/static/static/static/_not-found/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/_not-found/index.html +1 -0
- pycharter/ui/static/static/static/static/_not-found/index.txt +17 -0
- pycharter/ui/static/static/static/static/contracts/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/contracts/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/contracts/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/contracts/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/contracts/__next.contracts.txt +4 -0
- pycharter/ui/static/static/static/static/contracts/index.html +1 -0
- pycharter/ui/static/static/static/static/contracts/index.txt +21 -0
- pycharter/ui/static/static/static/static/documentation/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/documentation/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/documentation/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/documentation/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/documentation/__next.documentation.txt +4 -0
- pycharter/ui/static/static/static/static/documentation/index.html +93 -0
- pycharter/ui/static/static/static/static/documentation/index.txt +21 -0
- pycharter/ui/static/static/static/static/index.html +1 -0
- pycharter/ui/static/static/static/static/index.txt +30 -0
- pycharter/ui/static/static/static/static/metadata/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/metadata/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/metadata/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/metadata/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/metadata/__next.metadata.txt +4 -0
- pycharter/ui/static/static/static/static/metadata/index.html +1 -0
- pycharter/ui/static/static/static/static/metadata/index.txt +21 -0
- pycharter/ui/static/static/static/static/quality/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/quality/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/quality/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/quality/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/quality/__next.quality.txt +4 -0
- pycharter/ui/static/static/static/static/quality/index.html +2 -0
- pycharter/ui/static/static/static/static/quality/index.txt +21 -0
- pycharter/ui/static/static/static/static/rules/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/rules/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/rules/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/rules/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/rules/__next.rules.txt +4 -0
- pycharter/ui/static/static/static/static/rules/index.html +1 -0
- pycharter/ui/static/static/static/static/rules/index.txt +21 -0
- pycharter/ui/static/static/static/static/schemas/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/schemas/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/schemas/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/schemas/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/schemas/__next.schemas.txt +4 -0
- pycharter/ui/static/static/static/static/schemas/index.html +1 -0
- pycharter/ui/static/static/static/static/schemas/index.txt +21 -0
- pycharter/ui/static/static/static/static/settings/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/settings/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/settings/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/settings/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/settings/__next.settings.txt +4 -0
- pycharter/ui/static/static/static/static/settings/index.html +1 -0
- pycharter/ui/static/static/static/static/settings/index.txt +21 -0
- pycharter/ui/static/static/static/static/validation/__next._full.txt +21 -0
- pycharter/ui/static/static/static/static/validation/__next._head.txt +7 -0
- pycharter/ui/static/static/static/static/validation/__next._index.txt +9 -0
- pycharter/ui/static/static/static/static/validation/__next._tree.txt +2 -0
- pycharter/ui/static/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
- pycharter/ui/static/static/static/static/validation/__next.validation.txt +4 -0
- pycharter/ui/static/static/static/static/validation/index.html +1 -0
- pycharter/ui/static/static/static/static/validation/index.txt +21 -0
- pycharter/ui/static/static/static/validation/__next._full.txt +2 -2
- pycharter/ui/static/static/static/validation/__next._head.txt +1 -1
- pycharter/ui/static/static/static/validation/__next._index.txt +2 -2
- pycharter/ui/static/static/static/validation/__next._tree.txt +2 -2
- pycharter/ui/static/static/static/validation/__next.validation.__PAGE__.txt +1 -1
- pycharter/ui/static/static/static/validation/__next.validation.txt +1 -1
- pycharter/ui/static/static/static/validation/index.html +1 -1
- pycharter/ui/static/static/static/validation/index.txt +2 -2
- pycharter/ui/static/static/validation/__next._full.txt +2 -2
- pycharter/ui/static/static/validation/__next._head.txt +1 -1
- pycharter/ui/static/static/validation/__next._index.txt +1 -1
- pycharter/ui/static/static/validation/__next._tree.txt +1 -1
- pycharter/ui/static/static/validation/__next.validation.__PAGE__.txt +2 -2
- pycharter/ui/static/static/validation/__next.validation.txt +1 -1
- pycharter/ui/static/static/validation/index.html +1 -1
- pycharter/ui/static/static/validation/index.txt +2 -2
- pycharter/ui/static/validation/__next._full.txt +7 -7
- pycharter/ui/static/validation/__next._head.txt +1 -1
- pycharter/ui/static/validation/__next._index.txt +6 -6
- pycharter/ui/static/validation/__next._tree.txt +2 -2
- pycharter/ui/static/validation/__next.validation.__PAGE__.txt +2 -2
- pycharter/ui/static/validation/__next.validation.txt +1 -1
- pycharter/ui/static/validation/index.html +1 -1
- pycharter/ui/static/validation/index.txt +7 -7
- {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/METADATA +57 -26
- pycharter-0.0.26.dist-info/RECORD +702 -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/etl_generator/schemas/extract.json +0 -234
- pycharter/etl_generator/schemas/load.json +0 -202
- pycharter/etl_generator/schemas/pipeline.json +0 -94
- pycharter/etl_generator/schemas/transform.json +0 -171
- pycharter-0.0.25.dist-info/RECORD +0 -572
- /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_buildManifest.js +0 -0
- /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_clientMiddlewareManifest.json +0 -0
- /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_ssgManifest.js +0 -0
- /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_clientMiddlewareManifest.json +0 -0
- /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- /pycharter/ui/static/{_next → static/_next}/static/chunks/26dfc590f7714c03.js +0 -0
- /pycharter/ui/static/{_next → static/_next}/static/chunks/34d289e6db2ef551.js +0 -0
- /pycharter/ui/static/{_next → static/_next}/static/chunks/99508d9d5869cc27.js +0 -0
- /pycharter/ui/static/{_next → static/_next}/static/chunks/b313c35a6ba76574.js +0 -0
- /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_clientMiddlewareManifest.json +0 -0
- /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- /pycharter/ui/static/{_next → static/static/_next}/static/chunks/13d4a0fbd74c1ee4.js +0 -0
- /pycharter/ui/static/{_next → static/static/_next}/static/chunks/2edb43b48432ac04.js +0 -0
- /pycharter/ui/static/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
- /pycharter/ui/static/{_next → static/static/_next}/static/chunks/d2363397e1b2bcab.css +0 -0
- /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/2ab439ce003cd691.js +0 -0
- /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/49ca65abd26ae49e.js +0 -0
- /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
- /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
- /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
- /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
- /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
- /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/9667e7a3d359eb39.js +0 -0
- /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
- /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/c69f6cba366bd988.js +0 -0
- /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
- /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/f061a4be97bfc3b3.js +0 -0
- {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/WHEEL +0 -0
- {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/licenses/LICENSE +0 -0
- {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/top_level.txt +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/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
|
|
@@ -49,16 +49,15 @@ Discovers pipelines from a root directory: each subdir that contains `extract.ya
|
|
|
49
49
|
|
|
50
50
|
**Import:** `from pycharter.etl_generator.extractors import extract_with_pagination_streaming`
|
|
51
51
|
|
|
52
|
-
Async generator that yields batches of records. Dispatches to the right extractor via `ExtractorFactory` using `
|
|
52
|
+
Async generator that yields batches of records. Dispatches to the right extractor via `ExtractorFactory` using `type` (or auto-detection from `extract_config`).
|
|
53
53
|
|
|
54
54
|
- **Args:** `extract_config`, `params`, `headers`, `contract_dir=None`, `batch_size=1000`, `max_records=None`, `config_context=None`
|
|
55
55
|
|
|
56
|
-
### `ExtractorFactory
|
|
56
|
+
### `ExtractorFactory.create(extract_config) -> BaseExtractor`
|
|
57
57
|
|
|
58
|
-
**Import:** `from pycharter.etl_generator.extractors import ExtractorFactory
|
|
58
|
+
**Import:** `from pycharter.etl_generator.extractors import ExtractorFactory`
|
|
59
59
|
|
|
60
|
-
- `ExtractorFactory.
|
|
61
|
-
- `get_extractor(extract_config)` — Same, module-level helper.
|
|
60
|
+
- `ExtractorFactory.create(extract_config)` — Returns an extractor instance for the given config.
|
|
62
61
|
- **Auto-detection:** `base_url`/`api_endpoint` → http; `file_path` → file; `database` → database; `storage` → cloud_storage.
|
|
63
62
|
|
|
64
63
|
### Extractors (implementations of `BaseExtractor`)
|
|
@@ -71,7 +70,7 @@ Async generator that yields batches of records. Dispatches to the right extracto
|
|
|
71
70
|
- **DatabaseExtractor** — SQL over PostgreSQL, MySQL, SQLite, MSSQL, Oracle.
|
|
72
71
|
- **CloudStorageExtractor** — S3, GCS, Azure Blob.
|
|
73
72
|
|
|
74
|
-
Custom extractors: `ExtractorFactory.
|
|
73
|
+
Custom extractors: `ExtractorFactory.register(type_name, extractor_class)`.
|
|
75
74
|
|
|
76
75
|
---
|
|
77
76
|
|
|
@@ -136,7 +135,7 @@ Helpers to produce ETL config dicts (extract/transform/load) from contracts or f
|
|
|
136
135
|
|--------------------------|--------|
|
|
137
136
|
| Run ETL | `from pycharter.etl_generator import ETLOrchestrator, create_orchestrator` |
|
|
138
137
|
| Discover pipelines | `from pycharter.etl_generator import PipelineFactory` |
|
|
139
|
-
| Extract (streaming) | `from pycharter.etl_generator.extractors import extract_with_pagination_streaming, ExtractorFactory
|
|
138
|
+
| Extract (streaming) | `from pycharter.etl_generator.extractors import extract_with_pagination_streaming, ExtractorFactory` |
|
|
140
139
|
| Transform | `from pycharter.etl_generator.transformers import apply_transforms` |
|
|
141
140
|
| Load to file/cloud | `from pycharter.etl_generator.loaders import load_to_file, load_to_cloud_storage` |
|
|
142
141
|
| Config generation | `from pycharter.etl_generator import generate_etl_config, generate_etl_config_from_contract, generate_etl_config_from_store` |
|
|
@@ -70,19 +70,39 @@ from pycharter.etl_generator.context import PipelineContext
|
|
|
70
70
|
from pycharter.etl_generator.result import PipelineResult, BatchResult, LoadResult
|
|
71
71
|
from pycharter.etl_generator.protocols import Extractor, Transformer, Loader
|
|
72
72
|
|
|
73
|
-
# Config
|
|
74
|
-
from pycharter.
|
|
75
|
-
ConfigLoader,
|
|
76
|
-
PipelineConfig,
|
|
77
|
-
load_pipeline_config,
|
|
78
|
-
ConfigLoadError,
|
|
79
|
-
)
|
|
73
|
+
# Config validation and errors
|
|
74
|
+
from pycharter.shared.errors import ConfigLoadError
|
|
80
75
|
from pycharter.etl_generator.config_validator import (
|
|
81
76
|
ConfigValidator,
|
|
82
77
|
ConfigValidationError,
|
|
83
78
|
validate_config,
|
|
84
79
|
)
|
|
85
80
|
|
|
81
|
+
# Config models (Pydantic)
|
|
82
|
+
from pycharter.etl_generator.config_models import (
|
|
83
|
+
ExtractConfig,
|
|
84
|
+
LoadConfig,
|
|
85
|
+
TransformConfig,
|
|
86
|
+
SettingsConfig,
|
|
87
|
+
ExtractValidationConfig,
|
|
88
|
+
LoadValidationConfig,
|
|
89
|
+
DLQConfig,
|
|
90
|
+
parse_extract_config,
|
|
91
|
+
parse_load_config,
|
|
92
|
+
parse_transform_config,
|
|
93
|
+
parse_settings_config,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# ETL Validation
|
|
97
|
+
from pycharter.etl_generator.validation import (
|
|
98
|
+
ETLValidator,
|
|
99
|
+
ETLValidationError,
|
|
100
|
+
resolve_schema,
|
|
101
|
+
resolve_contract,
|
|
102
|
+
create_dlq,
|
|
103
|
+
create_etl_validator,
|
|
104
|
+
)
|
|
105
|
+
|
|
86
106
|
# Expression evaluation
|
|
87
107
|
from pycharter.etl_generator.expression import (
|
|
88
108
|
ExpressionEvaluator,
|
|
@@ -139,14 +159,30 @@ __all__ = [
|
|
|
139
159
|
"Extractor",
|
|
140
160
|
"Transformer",
|
|
141
161
|
"Loader",
|
|
142
|
-
# Config
|
|
143
|
-
"ConfigLoader",
|
|
144
|
-
"PipelineConfig",
|
|
145
|
-
"load_pipeline_config",
|
|
162
|
+
# Config validation
|
|
146
163
|
"ConfigLoadError",
|
|
147
164
|
"ConfigValidator",
|
|
148
165
|
"ConfigValidationError",
|
|
149
166
|
"validate_config",
|
|
167
|
+
# Config models
|
|
168
|
+
"ExtractConfig",
|
|
169
|
+
"LoadConfig",
|
|
170
|
+
"TransformConfig",
|
|
171
|
+
"SettingsConfig",
|
|
172
|
+
"ExtractValidationConfig",
|
|
173
|
+
"LoadValidationConfig",
|
|
174
|
+
"DLQConfig",
|
|
175
|
+
"parse_extract_config",
|
|
176
|
+
"parse_load_config",
|
|
177
|
+
"parse_transform_config",
|
|
178
|
+
"parse_settings_config",
|
|
179
|
+
# ETL Validation
|
|
180
|
+
"ETLValidator",
|
|
181
|
+
"ETLValidationError",
|
|
182
|
+
"resolve_schema",
|
|
183
|
+
"resolve_contract",
|
|
184
|
+
"create_dlq",
|
|
185
|
+
"create_etl_validator",
|
|
150
186
|
# Expressions
|
|
151
187
|
"ExpressionEvaluator",
|
|
152
188
|
"ExpressionError",
|