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,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Spark-based validation backend for large datasets.
|
|
3
|
+
|
|
4
|
+
Supports:
|
|
5
|
+
- Local mode: Single machine, no cluster needed (default)
|
|
6
|
+
- Remote mode: Submit to external Spark service
|
|
7
|
+
- Cluster mode: Connect to Spark cluster (future)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import pickle
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
|
+
|
|
15
|
+
from pyspark.sql import DataFrame, SparkSession
|
|
16
|
+
from pyspark.sql.functions import col, udf
|
|
17
|
+
from pyspark.sql.types import (
|
|
18
|
+
ArrayType,
|
|
19
|
+
BooleanType,
|
|
20
|
+
StringType,
|
|
21
|
+
StructField,
|
|
22
|
+
StructType,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from pycharter import build_contract_from_store, get_model_from_contract
|
|
26
|
+
from pycharter.metadata_store import MetadataStoreClient
|
|
27
|
+
from pycharter.runtime_validator.validator_core import ValidationResult, validate
|
|
28
|
+
|
|
29
|
+
from pycharter.worker.backends.base import ValidationBackend
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SparkValidationBackend(ValidationBackend):
|
|
33
|
+
"""
|
|
34
|
+
Spark-compatible validation backend.
|
|
35
|
+
|
|
36
|
+
Works in three modes:
|
|
37
|
+
1. Local mode (default) - Single machine, no cluster needed
|
|
38
|
+
2. Remote mode - Submit to external Spark service
|
|
39
|
+
3. Cluster mode - Connect to Spark cluster (future)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
mode: str = "local", # "local", "remote", "cluster"
|
|
45
|
+
master: Optional[str] = None, # "local[*]", "spark://host:port", "yarn", etc.
|
|
46
|
+
app_name: str = "pycharter-validation",
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Initialize Spark validation backend.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
mode: Spark mode ("local", "remote", "cluster")
|
|
53
|
+
master: Spark master URL (auto-detected if not provided)
|
|
54
|
+
app_name: Spark application name
|
|
55
|
+
"""
|
|
56
|
+
self.mode = mode
|
|
57
|
+
self.master = master or self._get_default_master()
|
|
58
|
+
self.app_name = app_name
|
|
59
|
+
self.spark: Optional[SparkSession] = None
|
|
60
|
+
|
|
61
|
+
def _get_default_master(self) -> str:
|
|
62
|
+
"""Get default Spark master based on mode."""
|
|
63
|
+
if self.mode == "local":
|
|
64
|
+
return "local[*]" # Use all CPU cores locally
|
|
65
|
+
elif self.mode == "remote":
|
|
66
|
+
# For remote Spark service (e.g., Spark on Kubernetes)
|
|
67
|
+
return os.getenv("SPARK_MASTER", "spark://localhost:7077")
|
|
68
|
+
else: # cluster
|
|
69
|
+
return os.getenv("SPARK_MASTER", "yarn")
|
|
70
|
+
|
|
71
|
+
def get_spark_session(self) -> SparkSession:
|
|
72
|
+
"""Get or create Spark session."""
|
|
73
|
+
if self.spark is None:
|
|
74
|
+
self.spark = (
|
|
75
|
+
SparkSession.builder.appName(self.app_name)
|
|
76
|
+
.master(self.master)
|
|
77
|
+
.config("spark.sql.adaptive.enabled", "true")
|
|
78
|
+
.config("spark.sql.adaptive.coalescePartitions.enabled", "true")
|
|
79
|
+
.getOrCreate()
|
|
80
|
+
)
|
|
81
|
+
return self.spark
|
|
82
|
+
|
|
83
|
+
def validate(
|
|
84
|
+
self,
|
|
85
|
+
schema_id: str,
|
|
86
|
+
data_source: str, # S3 path, file path, database table, etc.
|
|
87
|
+
store: MetadataStoreClient,
|
|
88
|
+
options: Optional[Dict[str, Any]] = None,
|
|
89
|
+
) -> Dict[str, Any]:
|
|
90
|
+
"""
|
|
91
|
+
Validate data using Spark.
|
|
92
|
+
|
|
93
|
+
This method:
|
|
94
|
+
1. Loads contract from metadata store
|
|
95
|
+
2. Reads data from source
|
|
96
|
+
3. Validates using PyCharter validation logic
|
|
97
|
+
4. Returns aggregated results
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
schema_id: Schema identifier
|
|
101
|
+
data_source: Data source path or identifier
|
|
102
|
+
store: Metadata store client
|
|
103
|
+
options: Validation options
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Dictionary with validation results
|
|
107
|
+
"""
|
|
108
|
+
spark = self.get_spark_session()
|
|
109
|
+
options = options or {}
|
|
110
|
+
|
|
111
|
+
# Load contract from metadata store
|
|
112
|
+
contract = build_contract_from_store(store, schema_title=schema_id)
|
|
113
|
+
model = get_model_from_contract(contract)
|
|
114
|
+
|
|
115
|
+
# Serialize model for Spark UDF
|
|
116
|
+
# Note: Pydantic models need to be pickled and broadcast
|
|
117
|
+
model_pickle = pickle.dumps(model)
|
|
118
|
+
model_broadcast = spark.sparkContext.broadcast(model_pickle)
|
|
119
|
+
|
|
120
|
+
# Read data
|
|
121
|
+
df = self._read_data(spark, data_source)
|
|
122
|
+
|
|
123
|
+
# Define validation UDF
|
|
124
|
+
@udf(
|
|
125
|
+
returnType=StructType(
|
|
126
|
+
[
|
|
127
|
+
StructField("is_valid", BooleanType()),
|
|
128
|
+
StructField("errors", ArrayType(StringType())),
|
|
129
|
+
StructField("data_json", StringType()),
|
|
130
|
+
]
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
def validate_record(record_json: str) -> Dict[str, Any]:
|
|
134
|
+
"""Validate a single record using PyCharter."""
|
|
135
|
+
import pickle
|
|
136
|
+
|
|
137
|
+
# Unpickle model
|
|
138
|
+
model = pickle.loads(model_broadcast.value)
|
|
139
|
+
|
|
140
|
+
# Parse record
|
|
141
|
+
record = json.loads(record_json)
|
|
142
|
+
|
|
143
|
+
# Validate using PyCharter
|
|
144
|
+
result = validate(model, record, strict=False)
|
|
145
|
+
|
|
146
|
+
# Return result
|
|
147
|
+
return {
|
|
148
|
+
"is_valid": result.is_valid,
|
|
149
|
+
"errors": result.errors or [],
|
|
150
|
+
"data_json": json.dumps(result.data.model_dump())
|
|
151
|
+
if result.data
|
|
152
|
+
else None,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Convert DataFrame to JSON strings for validation
|
|
156
|
+
# Assuming data is already in a format we can work with
|
|
157
|
+
# If data is in columns, we need to convert to JSON
|
|
158
|
+
if "value" in df.columns:
|
|
159
|
+
# Data is already in JSON format
|
|
160
|
+
validation_df = df.withColumn(
|
|
161
|
+
"validation_result", validate_record(col("value"))
|
|
162
|
+
)
|
|
163
|
+
else:
|
|
164
|
+
# Convert row to JSON
|
|
165
|
+
from pyspark.sql.functions import to_json, struct
|
|
166
|
+
|
|
167
|
+
json_cols = [col(c) for c in df.columns]
|
|
168
|
+
df_with_json = df.withColumn("value", to_json(struct(*json_cols)))
|
|
169
|
+
validation_df = df_with_json.withColumn(
|
|
170
|
+
"validation_result", validate_record(col("value"))
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Aggregate results
|
|
174
|
+
results = validation_df.select(
|
|
175
|
+
col("validation_result.is_valid").alias("is_valid"),
|
|
176
|
+
col("validation_result.errors").alias("errors"),
|
|
177
|
+
).collect()
|
|
178
|
+
|
|
179
|
+
# Calculate metrics
|
|
180
|
+
total_count = len(results)
|
|
181
|
+
valid_count = sum(1 for r in results if r.is_valid)
|
|
182
|
+
invalid_count = total_count - valid_count
|
|
183
|
+
|
|
184
|
+
# Collect violations
|
|
185
|
+
violations = []
|
|
186
|
+
for result in results:
|
|
187
|
+
if not result.is_valid and result.errors:
|
|
188
|
+
violations.extend(result.errors)
|
|
189
|
+
|
|
190
|
+
# Calculate quality score
|
|
191
|
+
quality_score = valid_count / total_count if total_count > 0 else 0.0
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
"total_count": total_count,
|
|
195
|
+
"valid_count": valid_count,
|
|
196
|
+
"invalid_count": invalid_count,
|
|
197
|
+
"violations": violations,
|
|
198
|
+
"quality_score": quality_score,
|
|
199
|
+
"data_source": data_source,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
def _read_data(self, spark: SparkSession, data_source: str) -> DataFrame:
|
|
203
|
+
"""Read data from various sources."""
|
|
204
|
+
if data_source.startswith("s3://") or data_source.startswith("s3a://"):
|
|
205
|
+
# Read from S3
|
|
206
|
+
return spark.read.parquet(data_source)
|
|
207
|
+
elif data_source.startswith("hdfs://"):
|
|
208
|
+
# Read from HDFS
|
|
209
|
+
return spark.read.parquet(data_source)
|
|
210
|
+
elif data_source.endswith(".parquet"):
|
|
211
|
+
# Local parquet file
|
|
212
|
+
return spark.read.parquet(data_source)
|
|
213
|
+
elif data_source.endswith(".json"):
|
|
214
|
+
# JSON file
|
|
215
|
+
return spark.read.json(data_source)
|
|
216
|
+
elif data_source.endswith(".csv"):
|
|
217
|
+
# CSV file
|
|
218
|
+
return spark.read.csv(data_source, header=True, inferSchema=True)
|
|
219
|
+
else:
|
|
220
|
+
# Assume it's a database table or try to infer format
|
|
221
|
+
# For now, try JSON first
|
|
222
|
+
try:
|
|
223
|
+
return spark.read.json(data_source)
|
|
224
|
+
except Exception:
|
|
225
|
+
# Fallback to parquet
|
|
226
|
+
return spark.read.parquet(data_source)
|
|
227
|
+
|
|
228
|
+
def close(self):
|
|
229
|
+
"""Close Spark session."""
|
|
230
|
+
if self.spark:
|
|
231
|
+
self.spark.stop()
|
|
232
|
+
self.spark = None
|
|
233
|
+
|
|
@@ -16,8 +16,8 @@ from pycharter.metadata_store import (
|
|
|
16
16
|
from pycharter.db.models.base import get_session
|
|
17
17
|
from sqlalchemy.orm import Session
|
|
18
18
|
|
|
19
|
-
from worker.backends.spark import SparkValidationBackend
|
|
20
|
-
from worker.queue.redis_queue import ValidationJobQueue
|
|
19
|
+
from pycharter.worker.backends.spark import SparkValidationBackend
|
|
20
|
+
from pycharter.worker.queue.redis_queue import ValidationJobQueue
|
|
21
21
|
|
|
22
22
|
logger = logging.getLogger(__name__)
|
|
23
23
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Redis-based message queue for async validation jobs.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
import uuid
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import redis.asyncio as redis
|
|
13
|
+
REDIS_AVAILABLE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
REDIS_AVAILABLE = False
|
|
16
|
+
redis = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ValidationJobQueue:
|
|
20
|
+
"""Message queue for async validation jobs using Redis."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, redis_url: str = "redis://localhost:6379"):
|
|
23
|
+
"""
|
|
24
|
+
Initialize Redis job queue.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
redis_url: Redis connection URL
|
|
28
|
+
"""
|
|
29
|
+
if not REDIS_AVAILABLE:
|
|
30
|
+
raise ImportError(
|
|
31
|
+
"redis package is required. Install with: pip install redis>=5.0.0"
|
|
32
|
+
)
|
|
33
|
+
self.redis_url = redis_url
|
|
34
|
+
self.redis_client: Optional[redis.Redis] = None
|
|
35
|
+
|
|
36
|
+
async def connect(self):
|
|
37
|
+
"""Connect to Redis."""
|
|
38
|
+
if not REDIS_AVAILABLE:
|
|
39
|
+
raise ImportError("redis package is required")
|
|
40
|
+
self.redis_client = await redis.from_url(self.redis_url)
|
|
41
|
+
|
|
42
|
+
async def disconnect(self):
|
|
43
|
+
"""Disconnect from Redis."""
|
|
44
|
+
if self.redis_client:
|
|
45
|
+
await self.redis_client.close()
|
|
46
|
+
self.redis_client = None
|
|
47
|
+
|
|
48
|
+
async def enqueue_job(self, job: Dict[str, Any]) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Enqueue a validation job.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
job: Job dictionary with schema_id, data_source, options
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Job ID
|
|
57
|
+
"""
|
|
58
|
+
if not self.redis_client:
|
|
59
|
+
await self.connect()
|
|
60
|
+
|
|
61
|
+
job_id = str(uuid.uuid4())
|
|
62
|
+
job["job_id"] = job_id
|
|
63
|
+
job["status"] = "queued"
|
|
64
|
+
job["created_at"] = datetime.utcnow().isoformat()
|
|
65
|
+
|
|
66
|
+
# Push to queue
|
|
67
|
+
await self.redis_client.lpush("validation-jobs", json.dumps(job))
|
|
68
|
+
|
|
69
|
+
# Store job metadata
|
|
70
|
+
await self.redis_client.setex(
|
|
71
|
+
f"job:{job_id}",
|
|
72
|
+
3600, # 1 hour TTL
|
|
73
|
+
json.dumps({"status": "queued", "created_at": job["created_at"]}),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return job_id
|
|
77
|
+
|
|
78
|
+
async def get_job_status(self, job_id: str) -> Optional[Dict[str, Any]]:
|
|
79
|
+
"""
|
|
80
|
+
Get job status.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
job_id: Job identifier
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Job status dictionary or None if not found
|
|
87
|
+
"""
|
|
88
|
+
if not self.redis_client:
|
|
89
|
+
await self.connect()
|
|
90
|
+
|
|
91
|
+
status_json = await self.redis_client.get(f"job:{job_id}")
|
|
92
|
+
if status_json:
|
|
93
|
+
return json.loads(status_json)
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
async def update_job_status(
|
|
97
|
+
self,
|
|
98
|
+
job_id: str,
|
|
99
|
+
status: str,
|
|
100
|
+
result: Optional[Dict[str, Any]] = None,
|
|
101
|
+
error: Optional[str] = None,
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
Update job status.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
job_id: Job identifier
|
|
108
|
+
status: New status ("queued", "processing", "completed", "failed")
|
|
109
|
+
result: Optional result dictionary
|
|
110
|
+
error: Optional error message
|
|
111
|
+
"""
|
|
112
|
+
if not self.redis_client:
|
|
113
|
+
await self.connect()
|
|
114
|
+
|
|
115
|
+
status_data = {
|
|
116
|
+
"status": status,
|
|
117
|
+
"updated_at": datetime.utcnow().isoformat(),
|
|
118
|
+
}
|
|
119
|
+
if result:
|
|
120
|
+
status_data["result"] = result
|
|
121
|
+
if error:
|
|
122
|
+
status_data["error"] = error
|
|
123
|
+
|
|
124
|
+
await self.redis_client.setex(
|
|
125
|
+
f"job:{job_id}",
|
|
126
|
+
3600, # 1 hour TTL
|
|
127
|
+
json.dumps(status_data),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
async def dequeue_job(self, timeout: int = 1) -> Optional[Dict[str, Any]]:
|
|
131
|
+
"""
|
|
132
|
+
Dequeue a job from the queue (blocking).
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
timeout: Blocking timeout in seconds
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Job dictionary or None if timeout
|
|
139
|
+
"""
|
|
140
|
+
if not self.redis_client:
|
|
141
|
+
await self.connect()
|
|
142
|
+
|
|
143
|
+
result = await self.redis_client.brpop("validation-jobs", timeout=timeout)
|
|
144
|
+
if result:
|
|
145
|
+
return json.loads(result[1])
|
|
146
|
+
return None
|
|
147
|
+
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycharter
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.26
|
|
4
4
|
Summary: A Python package for data contract management with five core services: contract parsing, metadata storage, Pydantic generation, JSON Schema conversion, and runtime validation
|
|
5
5
|
Author-email: semantic developers <na@example.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/auscheng/pycharter
|
|
8
|
-
Project-URL: Documentation, https://github.
|
|
8
|
+
Project-URL: Documentation, https://auscheng.github.io/pycharter/
|
|
9
9
|
Project-URL: Repository, https://github.com/auscheng/pycharter
|
|
10
10
|
Project-URL: Issues, https://github.com/auscheng/pycharter/issues
|
|
11
11
|
Keywords: semantic,python,pydantic,contract,validation,schema
|
|
12
12
|
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.
|
|
16
|
-
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Requires-Python: >=3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Python: >=3.13
|
|
19
17
|
Description-Content-Type: text/markdown
|
|
20
18
|
License-File: LICENSE
|
|
21
19
|
Requires-Dist: pydantic>=2.0.0
|
|
@@ -39,11 +37,18 @@ Requires-Dist: ipykernel>=6.0.0; extra == "dev"
|
|
|
39
37
|
Requires-Dist: psycopg2-binary>=2.9.0; extra == "dev"
|
|
40
38
|
Requires-Dist: pymongo>=4.0.0; extra == "dev"
|
|
41
39
|
Requires-Dist: boto3>=1.26.0; extra == "dev"
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
42
|
+
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
|
|
43
|
+
Requires-Dist: pymdown-extensions>=10.0.0; extra == "docs"
|
|
44
|
+
Requires-Dist: mike>=2.0.0; extra == "docs"
|
|
42
45
|
Provides-Extra: api
|
|
43
46
|
Requires-Dist: fastapi>=0.104.0; extra == "api"
|
|
44
47
|
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "api"
|
|
45
48
|
Requires-Dist: pydantic-settings>=2.0.0; extra == "api"
|
|
46
49
|
Requires-Dist: python-multipart>=0.0.6; extra == "api"
|
|
50
|
+
Requires-Dist: PyJWT>=2.8.0; extra == "api"
|
|
51
|
+
Requires-Dist: httpx>=0.24.0; extra == "api"
|
|
47
52
|
Provides-Extra: ui
|
|
48
53
|
Requires-Dist: fastapi>=0.104.0; extra == "ui"
|
|
49
54
|
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "ui"
|
|
@@ -59,13 +64,14 @@ Requires-Dist: google-cloud-storage>=2.0.0; extra == "etl"
|
|
|
59
64
|
Requires-Dist: azure-storage-blob>=12.0.0; extra == "etl"
|
|
60
65
|
Requires-Dist: openpyxl>=3.0.0; extra == "etl"
|
|
61
66
|
Requires-Dist: lxml>=4.9.0; extra == "etl"
|
|
67
|
+
Requires-Dist: pymongo>=4.0.0; extra == "etl"
|
|
62
68
|
Dynamic: license-file
|
|
63
69
|
|
|
64
70
|
# PyCharter
|
|
65
71
|
|
|
66
|
-
> **
|
|
72
|
+
> **Data contract management and validation for Python: define schemas, enforce quality, and run ETL with contracts.**
|
|
67
73
|
|
|
68
|
-
[](https://www.python.org/downloads/)
|
|
69
75
|
[](https://opensource.org/licenses/MIT)
|
|
70
76
|
[](https://github.com/psf/black)
|
|
71
77
|
|
|
@@ -162,6 +168,27 @@ pycharter ui dev # Development mode with hot reload
|
|
|
162
168
|
|
|
163
169
|
**Note**: When installed from pip, the UI works immediately without Node.js. For development, Node.js is required. See `ui/INSTALLATION.md` for detailed instructions.
|
|
164
170
|
|
|
171
|
+
### Database setup (for metadata store, API, and seed data)
|
|
172
|
+
|
|
173
|
+
If you use the **metadata store** (SQLite or PostgreSQL), the **REST API**, or **seed data**, initialize the database once:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Initialize database schema (SQLite default: sqlite:///pycharter.db)
|
|
177
|
+
pycharter db init
|
|
178
|
+
|
|
179
|
+
# Seed reference data (owners, domains, systems, environments, data feeds, compliance frameworks, tags)
|
|
180
|
+
pycharter db seed
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**From the project source** (e.g. after cloning the repo), you can run the full seed script to also load example aviation contracts into the metadata store:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
python bin/seed # seed data/seed + data/aviation_examples
|
|
187
|
+
python bin/seed --skip-aviation # seed only data/seed
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use `pycharter db init --force` to drop and recreate all tables (SQLite only; **destroys existing data**). See [Configuration Guide](docs/guides/configuration.md) for connection options and migrations.
|
|
191
|
+
|
|
165
192
|
## 🚀 Quick Start
|
|
166
193
|
|
|
167
194
|
### Quick Start: ETL Pipelines
|
|
@@ -398,19 +425,20 @@ artifacts = ContractArtifacts(
|
|
|
398
425
|
|
|
399
426
|
contract = build_contract(artifacts)
|
|
400
427
|
# Contract now has:
|
|
401
|
-
# - schema
|
|
428
|
+
# - schema (RAW - rules NOT merged into it)
|
|
429
|
+
# - coercion_rules, validation_rules (separate)
|
|
402
430
|
# - metadata, ownership, governance_rules
|
|
403
431
|
# - versions tracking all components
|
|
404
432
|
|
|
405
433
|
# Or build from metadata store
|
|
406
434
|
contract = build_contract_from_store(store, "user_schema_v1")
|
|
407
435
|
|
|
408
|
-
# Use for validation
|
|
436
|
+
# Use for validation - Validator merges rules internally
|
|
409
437
|
from pycharter import validate_with_contract
|
|
410
438
|
result = validate_with_contract(contract, {"name": "Alice", "age": "30"})
|
|
411
439
|
```
|
|
412
440
|
|
|
413
|
-
**Contribution to Journey**: The contract builder is the **consolidation layer** that combines separate artifacts (stored independently in the database) into a single contract artifact.
|
|
441
|
+
**Contribution to Journey**: The contract builder is the **consolidation layer** that combines separate artifacts (stored independently in the database) into a single contract artifact. The contract contains **raw schema + separate rules** (not merged). The Validator class handles merging internally during validation, keeping the contract structure clear and editable.
|
|
414
442
|
|
|
415
443
|
---
|
|
416
444
|
|
|
@@ -469,7 +497,7 @@ validation_rules = store.get_validation_rules(schema_id)
|
|
|
469
497
|
|
|
470
498
|
**Contribution to Journey**: The metadata store is the **persistence layer** that ensures contracts and their components are versioned, searchable, and accessible across your organization. It enables governance, audit trails, and schema evolution tracking.
|
|
471
499
|
|
|
472
|
-
**See [Configuration Guide](
|
|
500
|
+
**See [Configuration Guide](docs/guides/configuration.md) for database setup and initialization instructions.**
|
|
473
501
|
|
|
474
502
|
---
|
|
475
503
|
|
|
@@ -808,7 +836,7 @@ def process_user_data_quick(raw_data):
|
|
|
808
836
|
|
|
809
837
|
---
|
|
810
838
|
|
|
811
|
-
### 7. 🌐 REST API (`api
|
|
839
|
+
### 7. 🌐 REST API (`pycharter.api`)
|
|
812
840
|
|
|
813
841
|
**Purpose**: Expose all PyCharter services as REST API endpoints.
|
|
814
842
|
|
|
@@ -824,11 +852,11 @@ def process_user_data_quick(raw_data):
|
|
|
824
852
|
|
|
825
853
|
**Example**:
|
|
826
854
|
```bash
|
|
827
|
-
# Start the API server
|
|
855
|
+
# Start the API server (uses PYCHARTER_DATABASE_URL or sqlite:///pycharter.db)
|
|
828
856
|
pycharter api
|
|
829
857
|
|
|
830
|
-
#
|
|
831
|
-
|
|
858
|
+
# With host/port
|
|
859
|
+
pycharter api --host 0.0.0.0 --port 8080
|
|
832
860
|
```
|
|
833
861
|
|
|
834
862
|
**Endpoints**:
|
|
@@ -863,11 +891,11 @@ Each service is designed to be **independent** yet **composable**, allowing you
|
|
|
863
891
|
|
|
864
892
|
## 📖 Documentation
|
|
865
893
|
|
|
866
|
-
- **[
|
|
867
|
-
- **[
|
|
868
|
-
- **[Database ERD](
|
|
869
|
-
- **[Examples](
|
|
870
|
-
- **[API
|
|
894
|
+
- **[Configuration Guide](docs/guides/configuration.md)** - Database connection, `pycharter db init` / `upgrade` / `seed`, migrations, and variable injection
|
|
895
|
+
- **[Data Journey Guide](docs/guides/data-journey.md)** - Data production journey: contract specification → storage → validation → quality
|
|
896
|
+
- **[Database ERD](docs/guides/database-erd.md)** - Database schema and entity relationship diagrams
|
|
897
|
+
- **[Examples & Notebooks](docs/notebooks/README.md)** - Jupyter notebooks (ETL, contracts, validation, quality, metadata store, schema conversion)
|
|
898
|
+
- **[REST API](api/README.md)** - API endpoints and usage (install with `pip install pycharter[api]`)
|
|
871
899
|
|
|
872
900
|
## 📚 Usage Examples
|
|
873
901
|
|
|
@@ -1334,7 +1362,7 @@ The workflow automatically:
|
|
|
1334
1362
|
make build # Builds package (UI built automatically via setup.py)
|
|
1335
1363
|
```
|
|
1336
1364
|
|
|
1337
|
-
See
|
|
1365
|
+
See [Publishing guide](docs/contributing/publishing.md) for complete documentation.
|
|
1338
1366
|
|
|
1339
1367
|
## 📋 JSON Schema Compliance
|
|
1340
1368
|
|
|
@@ -1347,10 +1375,12 @@ PyCharter is fully compliant with **JSON Schema Draft 2020-12** standard:
|
|
|
1347
1375
|
|
|
1348
1376
|
## 🔗 Requirements
|
|
1349
1377
|
|
|
1350
|
-
- Python 3.
|
|
1378
|
+
- **Python 3.13+**
|
|
1351
1379
|
- Pydantic >= 2.0.0
|
|
1352
1380
|
- jsonschema >= 4.0.0 (optional, for enhanced validation)
|
|
1353
1381
|
|
|
1382
|
+
See [pyproject.toml](pyproject.toml) for full dependencies and optional extras (`api`, `ui`, `dev`, `etl`, etc.).
|
|
1383
|
+
|
|
1354
1384
|
## 🤝 Contributing
|
|
1355
1385
|
|
|
1356
1386
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -1367,9 +1397,10 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
1367
1397
|
|
|
1368
1398
|
## 🔗 Links
|
|
1369
1399
|
|
|
1370
|
-
- **
|
|
1371
|
-
- **
|
|
1372
|
-
- **
|
|
1400
|
+
- **Homepage**: [https://github.com/auscheng/pycharter](https://github.com/auscheng/pycharter)
|
|
1401
|
+
- **Repository**: [GitHub](https://github.com/auscheng/pycharter)
|
|
1402
|
+
- **Issues**: [GitHub Issues](https://github.com/auscheng/pycharter/issues)
|
|
1403
|
+
- **Documentation**: [Configuration & guides](docs/guides/configuration.md) · [API docs](api/README.md)
|
|
1373
1404
|
|
|
1374
1405
|
---
|
|
1375
1406
|
|