pycharter 0.0.22__py3-none-any.whl → 0.0.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api/main.py +27 -1
- api/models/docs.py +68 -0
- api/models/evolution.py +117 -0
- api/models/tracking.py +111 -0
- api/models/validation.py +46 -6
- api/routes/v1/__init__.py +14 -1
- api/routes/v1/docs.py +187 -0
- api/routes/v1/evolution.py +337 -0
- api/routes/v1/templates.py +211 -27
- api/routes/v1/tracking.py +301 -0
- api/routes/v1/validation.py +68 -31
- pycharter/__init__.py +268 -58
- pycharter/data/templates/contract/template_coercion_rules.yaml +57 -0
- pycharter/data/templates/contract/template_contract.yaml +122 -0
- pycharter/data/templates/contract/template_metadata.yaml +68 -0
- pycharter/data/templates/contract/template_schema.yaml +100 -0
- pycharter/data/templates/contract/template_validation_rules.yaml +75 -0
- pycharter/data/templates/etl/README.md +224 -0
- pycharter/data/templates/etl/extract_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/extract_cloud_gcs.yaml +25 -0
- pycharter/data/templates/etl/extract_cloud_s3.yaml +30 -0
- pycharter/data/templates/etl/extract_database.yaml +34 -0
- pycharter/data/templates/etl/extract_database_ssh.yaml +40 -0
- pycharter/data/templates/etl/extract_file_csv.yaml +21 -0
- pycharter/data/templates/etl/extract_file_glob.yaml +25 -0
- pycharter/data/templates/etl/extract_file_json.yaml +24 -0
- pycharter/data/templates/etl/extract_file_parquet.yaml +20 -0
- pycharter/data/templates/etl/extract_http_paginated.yaml +79 -0
- pycharter/data/templates/etl/extract_http_path_params.yaml +38 -0
- pycharter/data/templates/etl/extract_http_simple.yaml +62 -0
- pycharter/data/templates/etl/load_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/load_cloud_gcs.yaml +22 -0
- pycharter/data/templates/etl/load_cloud_s3.yaml +27 -0
- pycharter/data/templates/etl/load_file.yaml +34 -0
- pycharter/data/templates/etl/load_insert.yaml +18 -0
- pycharter/data/templates/etl/load_postgresql.yaml +39 -0
- pycharter/data/templates/etl/load_sqlite.yaml +21 -0
- pycharter/data/templates/etl/load_truncate_and_load.yaml +20 -0
- pycharter/data/templates/etl/load_upsert.yaml +25 -0
- pycharter/data/templates/etl/load_with_dlq.yaml +34 -0
- pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +35 -0
- pycharter/data/templates/etl/pipeline_http_to_db.yaml +75 -0
- pycharter/data/templates/etl/transform_combined.yaml +48 -0
- pycharter/data/templates/etl/transform_custom_function.yaml +58 -0
- pycharter/data/templates/etl/transform_jsonata.yaml +51 -0
- pycharter/data/templates/etl/transform_simple.yaml +59 -0
- pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +160 -0
- pycharter/docs_generator/__init__.py +43 -0
- pycharter/docs_generator/generator.py +465 -0
- pycharter/docs_generator/renderers.py +247 -0
- pycharter/etl_generator/__init__.py +168 -80
- pycharter/etl_generator/builder.py +121 -0
- pycharter/etl_generator/config_loader.py +394 -0
- pycharter/etl_generator/config_validator.py +418 -0
- pycharter/etl_generator/context.py +132 -0
- pycharter/etl_generator/expression.py +499 -0
- pycharter/etl_generator/extractors/__init__.py +30 -0
- pycharter/etl_generator/extractors/base.py +70 -0
- pycharter/etl_generator/extractors/cloud_storage.py +530 -0
- pycharter/etl_generator/extractors/database.py +221 -0
- pycharter/etl_generator/extractors/factory.py +185 -0
- pycharter/etl_generator/extractors/file.py +475 -0
- pycharter/etl_generator/extractors/http.py +895 -0
- pycharter/etl_generator/extractors/streaming.py +57 -0
- pycharter/etl_generator/loaders/__init__.py +41 -0
- pycharter/etl_generator/loaders/base.py +35 -0
- pycharter/etl_generator/loaders/cloud.py +87 -0
- pycharter/etl_generator/loaders/cloud_storage_loader.py +275 -0
- pycharter/etl_generator/loaders/database.py +274 -0
- pycharter/etl_generator/loaders/factory.py +180 -0
- pycharter/etl_generator/loaders/file.py +72 -0
- pycharter/etl_generator/loaders/file_loader.py +130 -0
- pycharter/etl_generator/pipeline.py +743 -0
- pycharter/etl_generator/protocols.py +54 -0
- pycharter/etl_generator/result.py +63 -0
- pycharter/etl_generator/schemas/__init__.py +49 -0
- pycharter/etl_generator/transformers/__init__.py +49 -0
- pycharter/etl_generator/transformers/base.py +63 -0
- pycharter/etl_generator/transformers/config.py +45 -0
- pycharter/etl_generator/transformers/custom_function.py +101 -0
- pycharter/etl_generator/transformers/jsonata_transformer.py +56 -0
- pycharter/etl_generator/transformers/operations.py +218 -0
- pycharter/etl_generator/transformers/pipeline.py +54 -0
- pycharter/etl_generator/transformers/simple_operations.py +131 -0
- pycharter/quality/__init__.py +25 -0
- pycharter/quality/tracking/__init__.py +64 -0
- pycharter/quality/tracking/collector.py +318 -0
- pycharter/quality/tracking/exporters.py +238 -0
- pycharter/quality/tracking/models.py +194 -0
- pycharter/quality/tracking/store.py +385 -0
- pycharter/runtime_validator/__init__.py +20 -7
- pycharter/runtime_validator/builder.py +328 -0
- pycharter/runtime_validator/validator.py +311 -7
- pycharter/runtime_validator/validator_core.py +61 -0
- pycharter/schema_evolution/__init__.py +61 -0
- pycharter/schema_evolution/compatibility.py +270 -0
- pycharter/schema_evolution/diff.py +496 -0
- pycharter/schema_evolution/models.py +201 -0
- pycharter/shared/__init__.py +56 -0
- pycharter/shared/errors.py +296 -0
- pycharter/shared/protocols.py +234 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/METADATA +146 -26
- pycharter-0.0.24.dist-info/RECORD +543 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/WHEEL +1 -1
- ui/static/404/index.html +1 -1
- ui/static/404.html +1 -1
- ui/static/__next.__PAGE__.txt +1 -1
- ui/static/__next._full.txt +1 -1
- ui/static/__next._head.txt +1 -1
- ui/static/__next._index.txt +1 -1
- ui/static/__next._tree.txt +1 -1
- ui/static/_next/static/chunks/26dfc590f7714c03.js +1 -0
- ui/static/_next/static/chunks/34d289e6db2ef551.js +1 -0
- ui/static/_next/static/chunks/99508d9d5869cc27.js +1 -0
- ui/static/_next/static/chunks/b313c35a6ba76574.js +1 -0
- ui/static/_not-found/__next._full.txt +1 -1
- ui/static/_not-found/__next._head.txt +1 -1
- ui/static/_not-found/__next._index.txt +1 -1
- ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/_not-found/__next._not-found.txt +1 -1
- ui/static/_not-found/__next._tree.txt +1 -1
- ui/static/_not-found/index.html +1 -1
- ui/static/_not-found/index.txt +1 -1
- ui/static/contracts/__next._full.txt +2 -2
- ui/static/contracts/__next._head.txt +1 -1
- ui/static/contracts/__next._index.txt +1 -1
- ui/static/contracts/__next._tree.txt +1 -1
- ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/contracts/__next.contracts.txt +1 -1
- ui/static/contracts/index.html +1 -1
- ui/static/contracts/index.txt +2 -2
- ui/static/documentation/__next._full.txt +1 -1
- ui/static/documentation/__next._head.txt +1 -1
- ui/static/documentation/__next._index.txt +1 -1
- ui/static/documentation/__next._tree.txt +1 -1
- ui/static/documentation/__next.documentation.__PAGE__.txt +1 -1
- ui/static/documentation/__next.documentation.txt +1 -1
- ui/static/documentation/index.html +2 -2
- ui/static/documentation/index.txt +1 -1
- ui/static/index.html +1 -1
- ui/static/index.txt +1 -1
- ui/static/metadata/__next._full.txt +1 -1
- ui/static/metadata/__next._head.txt +1 -1
- ui/static/metadata/__next._index.txt +1 -1
- ui/static/metadata/__next._tree.txt +1 -1
- ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/metadata/__next.metadata.txt +1 -1
- ui/static/metadata/index.html +1 -1
- ui/static/metadata/index.txt +1 -1
- ui/static/quality/__next._full.txt +2 -2
- ui/static/quality/__next._head.txt +1 -1
- ui/static/quality/__next._index.txt +1 -1
- ui/static/quality/__next._tree.txt +1 -1
- ui/static/quality/__next.quality.__PAGE__.txt +2 -2
- ui/static/quality/__next.quality.txt +1 -1
- ui/static/quality/index.html +2 -2
- ui/static/quality/index.txt +2 -2
- ui/static/rules/__next._full.txt +1 -1
- ui/static/rules/__next._head.txt +1 -1
- ui/static/rules/__next._index.txt +1 -1
- ui/static/rules/__next._tree.txt +1 -1
- ui/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/rules/__next.rules.txt +1 -1
- ui/static/rules/index.html +1 -1
- ui/static/rules/index.txt +1 -1
- ui/static/schemas/__next._full.txt +1 -1
- ui/static/schemas/__next._head.txt +1 -1
- ui/static/schemas/__next._index.txt +1 -1
- ui/static/schemas/__next._tree.txt +1 -1
- ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/schemas/__next.schemas.txt +1 -1
- ui/static/schemas/index.html +1 -1
- ui/static/schemas/index.txt +1 -1
- ui/static/settings/__next._full.txt +1 -1
- ui/static/settings/__next._head.txt +1 -1
- ui/static/settings/__next._index.txt +1 -1
- ui/static/settings/__next._tree.txt +1 -1
- ui/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/settings/__next.settings.txt +1 -1
- ui/static/settings/index.html +1 -1
- ui/static/settings/index.txt +1 -1
- ui/static/static/404/index.html +1 -1
- ui/static/static/404.html +1 -1
- ui/static/static/__next.__PAGE__.txt +1 -1
- ui/static/static/__next._full.txt +2 -2
- ui/static/static/__next._head.txt +1 -1
- ui/static/static/__next._index.txt +2 -2
- ui/static/static/__next._tree.txt +2 -2
- ui/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
- ui/static/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
- ui/static/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
- ui/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- ui/static/static/_not-found/__next._full.txt +2 -2
- ui/static/static/_not-found/__next._head.txt +1 -1
- ui/static/static/_not-found/__next._index.txt +2 -2
- ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/static/_not-found/__next._not-found.txt +1 -1
- ui/static/static/_not-found/__next._tree.txt +2 -2
- ui/static/static/_not-found/index.html +1 -1
- ui/static/static/_not-found/index.txt +2 -2
- ui/static/static/contracts/__next._full.txt +3 -3
- ui/static/static/contracts/__next._head.txt +1 -1
- ui/static/static/contracts/__next._index.txt +2 -2
- ui/static/static/contracts/__next._tree.txt +2 -2
- ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/static/contracts/__next.contracts.txt +1 -1
- ui/static/static/contracts/index.html +1 -1
- ui/static/static/contracts/index.txt +3 -3
- ui/static/static/documentation/__next._full.txt +3 -3
- ui/static/static/documentation/__next._head.txt +1 -1
- ui/static/static/documentation/__next._index.txt +2 -2
- ui/static/static/documentation/__next._tree.txt +2 -2
- ui/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- ui/static/static/documentation/__next.documentation.txt +1 -1
- ui/static/static/documentation/index.html +2 -2
- ui/static/static/documentation/index.txt +3 -3
- ui/static/static/index.html +1 -1
- ui/static/static/index.txt +2 -2
- ui/static/static/metadata/__next._full.txt +2 -2
- ui/static/static/metadata/__next._head.txt +1 -1
- ui/static/static/metadata/__next._index.txt +2 -2
- ui/static/static/metadata/__next._tree.txt +2 -2
- ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/static/metadata/__next.metadata.txt +1 -1
- ui/static/static/metadata/index.html +1 -1
- ui/static/static/metadata/index.txt +2 -2
- ui/static/static/quality/__next._full.txt +2 -2
- ui/static/static/quality/__next._head.txt +1 -1
- ui/static/static/quality/__next._index.txt +2 -2
- ui/static/static/quality/__next._tree.txt +2 -2
- ui/static/static/quality/__next.quality.__PAGE__.txt +1 -1
- ui/static/static/quality/__next.quality.txt +1 -1
- ui/static/static/quality/index.html +2 -2
- ui/static/static/quality/index.txt +2 -2
- ui/static/static/rules/__next._full.txt +2 -2
- ui/static/static/rules/__next._head.txt +1 -1
- ui/static/static/rules/__next._index.txt +2 -2
- ui/static/static/rules/__next._tree.txt +2 -2
- ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/static/rules/__next.rules.txt +1 -1
- ui/static/static/rules/index.html +1 -1
- ui/static/static/rules/index.txt +2 -2
- ui/static/static/schemas/__next._full.txt +2 -2
- ui/static/static/schemas/__next._head.txt +1 -1
- ui/static/static/schemas/__next._index.txt +2 -2
- ui/static/static/schemas/__next._tree.txt +2 -2
- ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/static/schemas/__next.schemas.txt +1 -1
- ui/static/static/schemas/index.html +1 -1
- ui/static/static/schemas/index.txt +2 -2
- ui/static/static/settings/__next._full.txt +2 -2
- ui/static/static/settings/__next._head.txt +1 -1
- ui/static/static/settings/__next._index.txt +2 -2
- ui/static/static/settings/__next._tree.txt +2 -2
- ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/static/settings/__next.settings.txt +1 -1
- ui/static/static/settings/index.html +1 -1
- ui/static/static/settings/index.txt +2 -2
- ui/static/static/static/.gitkeep +0 -0
- ui/static/static/static/404/index.html +1 -0
- ui/static/static/static/404.html +1 -0
- ui/static/static/static/__next.__PAGE__.txt +10 -0
- ui/static/static/static/__next._full.txt +30 -0
- ui/static/static/static/__next._head.txt +7 -0
- ui/static/static/static/__next._index.txt +9 -0
- ui/static/static/static/__next._tree.txt +2 -0
- ui/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- ui/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- ui/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- ui/static/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
- ui/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- ui/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
- ui/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- ui/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
- ui/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- ui/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- ui/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- ui/static/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
- ui/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- ui/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
- ui/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- ui/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- ui/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- ui/static/static/static/_not-found/__next._full.txt +17 -0
- ui/static/static/static/_not-found/__next._head.txt +7 -0
- ui/static/static/static/_not-found/__next._index.txt +9 -0
- ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- ui/static/static/static/_not-found/__next._not-found.txt +4 -0
- ui/static/static/static/_not-found/__next._tree.txt +2 -0
- ui/static/static/static/_not-found/index.html +1 -0
- ui/static/static/static/_not-found/index.txt +17 -0
- ui/static/static/static/contracts/__next._full.txt +21 -0
- ui/static/static/static/contracts/__next._head.txt +7 -0
- ui/static/static/static/contracts/__next._index.txt +9 -0
- ui/static/static/static/contracts/__next._tree.txt +2 -0
- ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- ui/static/static/static/contracts/__next.contracts.txt +4 -0
- ui/static/static/static/contracts/index.html +1 -0
- ui/static/static/static/contracts/index.txt +21 -0
- ui/static/static/static/documentation/__next._full.txt +21 -0
- ui/static/static/static/documentation/__next._head.txt +7 -0
- ui/static/static/static/documentation/__next._index.txt +9 -0
- ui/static/static/static/documentation/__next._tree.txt +2 -0
- ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- ui/static/static/static/documentation/__next.documentation.txt +4 -0
- ui/static/static/static/documentation/index.html +93 -0
- ui/static/static/static/documentation/index.txt +21 -0
- ui/static/static/static/index.html +1 -0
- ui/static/static/static/index.txt +30 -0
- ui/static/static/static/metadata/__next._full.txt +21 -0
- ui/static/static/static/metadata/__next._head.txt +7 -0
- ui/static/static/static/metadata/__next._index.txt +9 -0
- ui/static/static/static/metadata/__next._tree.txt +2 -0
- ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- ui/static/static/static/metadata/__next.metadata.txt +4 -0
- ui/static/static/static/metadata/index.html +1 -0
- ui/static/static/static/metadata/index.txt +21 -0
- ui/static/static/static/quality/__next._full.txt +21 -0
- ui/static/static/static/quality/__next._head.txt +7 -0
- ui/static/static/static/quality/__next._index.txt +9 -0
- ui/static/static/static/quality/__next._tree.txt +2 -0
- ui/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
- ui/static/static/static/quality/__next.quality.txt +4 -0
- ui/static/static/static/quality/index.html +2 -0
- ui/static/static/static/quality/index.txt +21 -0
- ui/static/static/static/rules/__next._full.txt +21 -0
- ui/static/static/static/rules/__next._head.txt +7 -0
- ui/static/static/static/rules/__next._index.txt +9 -0
- ui/static/static/static/rules/__next._tree.txt +2 -0
- ui/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
- ui/static/static/static/rules/__next.rules.txt +4 -0
- ui/static/static/static/rules/index.html +1 -0
- ui/static/static/static/rules/index.txt +21 -0
- ui/static/static/static/schemas/__next._full.txt +21 -0
- ui/static/static/static/schemas/__next._head.txt +7 -0
- ui/static/static/static/schemas/__next._index.txt +9 -0
- ui/static/static/static/schemas/__next._tree.txt +2 -0
- ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- ui/static/static/static/schemas/__next.schemas.txt +4 -0
- ui/static/static/static/schemas/index.html +1 -0
- ui/static/static/static/schemas/index.txt +21 -0
- ui/static/static/static/settings/__next._full.txt +21 -0
- ui/static/static/static/settings/__next._head.txt +7 -0
- ui/static/static/static/settings/__next._index.txt +9 -0
- ui/static/static/static/settings/__next._tree.txt +2 -0
- ui/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
- ui/static/static/static/settings/__next.settings.txt +4 -0
- ui/static/static/static/settings/index.html +1 -0
- ui/static/static/static/settings/index.txt +21 -0
- ui/static/static/static/validation/__next._full.txt +21 -0
- ui/static/static/static/validation/__next._head.txt +7 -0
- ui/static/static/static/validation/__next._index.txt +9 -0
- ui/static/static/static/validation/__next._tree.txt +2 -0
- ui/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
- ui/static/static/static/validation/__next.validation.txt +4 -0
- ui/static/static/static/validation/index.html +1 -0
- ui/static/static/static/validation/index.txt +21 -0
- ui/static/static/validation/__next._full.txt +2 -2
- ui/static/static/validation/__next._head.txt +1 -1
- ui/static/static/validation/__next._index.txt +2 -2
- ui/static/static/validation/__next._tree.txt +2 -2
- ui/static/static/validation/__next.validation.__PAGE__.txt +1 -1
- ui/static/static/validation/__next.validation.txt +1 -1
- ui/static/static/validation/index.html +1 -1
- ui/static/static/validation/index.txt +2 -2
- ui/static/validation/__next._full.txt +2 -2
- ui/static/validation/__next._head.txt +1 -1
- ui/static/validation/__next._index.txt +1 -1
- ui/static/validation/__next._tree.txt +1 -1
- ui/static/validation/__next.validation.__PAGE__.txt +2 -2
- ui/static/validation/__next.validation.txt +1 -1
- ui/static/validation/index.html +1 -1
- ui/static/validation/index.txt +2 -2
- pycharter/data/templates/template_coercion_rules.yaml +0 -15
- pycharter/data/templates/template_contract.yaml +0 -587
- pycharter/data/templates/template_metadata.yaml +0 -38
- pycharter/data/templates/template_schema.yaml +0 -22
- pycharter/data/templates/template_transform_advanced.yaml +0 -50
- pycharter/data/templates/template_transform_simple.yaml +0 -59
- pycharter/data/templates/template_validation_rules.yaml +0 -29
- pycharter/etl_generator/extraction.py +0 -916
- pycharter/etl_generator/factory.py +0 -174
- pycharter/etl_generator/orchestrator.py +0 -1650
- pycharter/integrations/__init__.py +0 -19
- pycharter/integrations/kafka.py +0 -178
- pycharter/integrations/streaming.py +0 -100
- pycharter-0.0.22.dist-info/RECORD +0 -358
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/licenses/LICENSE +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/top_level.txt +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
api/main.py
CHANGED
|
@@ -16,7 +16,18 @@ from fastapi.routing import APIRoute
|
|
|
16
16
|
from pycharter import __version__ as pycharter_version
|
|
17
17
|
|
|
18
18
|
# Import routers from v1
|
|
19
|
-
from api.routes.v1 import
|
|
19
|
+
from api.routes.v1 import (
|
|
20
|
+
contracts,
|
|
21
|
+
metadata,
|
|
22
|
+
quality,
|
|
23
|
+
schemas,
|
|
24
|
+
templates,
|
|
25
|
+
validation,
|
|
26
|
+
settings,
|
|
27
|
+
docs,
|
|
28
|
+
tracking,
|
|
29
|
+
evolution,
|
|
30
|
+
)
|
|
20
31
|
|
|
21
32
|
# Try to import validation_jobs router (requires worker component)
|
|
22
33
|
try:
|
|
@@ -128,6 +139,21 @@ def create_application() -> FastAPI:
|
|
|
128
139
|
prefix=f"/api/{API_VERSION}",
|
|
129
140
|
tags=["Settings"],
|
|
130
141
|
)
|
|
142
|
+
app.include_router(
|
|
143
|
+
docs.router,
|
|
144
|
+
prefix=f"/api/{API_VERSION}",
|
|
145
|
+
tags=["Documentation"],
|
|
146
|
+
)
|
|
147
|
+
app.include_router(
|
|
148
|
+
tracking.router,
|
|
149
|
+
prefix=f"/api/{API_VERSION}",
|
|
150
|
+
tags=["Quality Tracking"],
|
|
151
|
+
)
|
|
152
|
+
app.include_router(
|
|
153
|
+
evolution.router,
|
|
154
|
+
prefix=f"/api/{API_VERSION}",
|
|
155
|
+
tags=["Schema Evolution"],
|
|
156
|
+
)
|
|
131
157
|
|
|
132
158
|
# Include validation_jobs router if worker component is available
|
|
133
159
|
if VALIDATION_JOBS_AVAILABLE:
|
api/models/docs.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API models for documentation generation endpoints.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DocsFormat(str, Enum):
|
|
12
|
+
"""Supported documentation output formats."""
|
|
13
|
+
|
|
14
|
+
MARKDOWN = "markdown"
|
|
15
|
+
HTML = "html"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DocsRequest(BaseModel):
|
|
19
|
+
"""Request model for generating documentation from contract data."""
|
|
20
|
+
|
|
21
|
+
contract: dict = Field(..., description="Contract data to generate documentation for")
|
|
22
|
+
format: DocsFormat = Field(
|
|
23
|
+
default=DocsFormat.MARKDOWN, description="Output format for documentation"
|
|
24
|
+
)
|
|
25
|
+
include_schema: bool = Field(default=True, description="Include schema fields section")
|
|
26
|
+
include_coercions: bool = Field(
|
|
27
|
+
default=True, description="Include coercion rules section"
|
|
28
|
+
)
|
|
29
|
+
include_validations: bool = Field(
|
|
30
|
+
default=True, description="Include validation rules section"
|
|
31
|
+
)
|
|
32
|
+
include_metadata: bool = Field(
|
|
33
|
+
default=True, description="Include metadata/ownership section"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DocsResponse(BaseModel):
|
|
38
|
+
"""Response model for generated documentation."""
|
|
39
|
+
|
|
40
|
+
documentation: str = Field(..., description="Generated documentation content")
|
|
41
|
+
format: DocsFormat = Field(..., description="Format of the generated documentation")
|
|
42
|
+
schema_name: Optional[str] = Field(
|
|
43
|
+
default=None, description="Name of the schema documented"
|
|
44
|
+
)
|
|
45
|
+
version: Optional[str] = Field(
|
|
46
|
+
default=None, description="Version of the schema documented"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DocsSectionRequest(BaseModel):
|
|
51
|
+
"""Request model for generating a specific documentation section."""
|
|
52
|
+
|
|
53
|
+
contract: dict = Field(..., description="Contract data to generate documentation for")
|
|
54
|
+
section: str = Field(
|
|
55
|
+
...,
|
|
56
|
+
description="Section to generate: 'schema', 'coercions', 'validations', 'metadata'",
|
|
57
|
+
)
|
|
58
|
+
format: DocsFormat = Field(
|
|
59
|
+
default=DocsFormat.MARKDOWN, description="Output format for documentation"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class DocsSectionResponse(BaseModel):
|
|
64
|
+
"""Response model for a documentation section."""
|
|
65
|
+
|
|
66
|
+
section: str = Field(..., description="Name of the section generated")
|
|
67
|
+
content: str = Field(..., description="Generated section content")
|
|
68
|
+
format: DocsFormat = Field(..., description="Format of the generated content")
|
api/models/evolution.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API models for schema evolution endpoints.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CompatibilityModeEnum(str, Enum):
|
|
12
|
+
"""Compatibility checking modes."""
|
|
13
|
+
|
|
14
|
+
BACKWARD = "backward"
|
|
15
|
+
FORWARD = "forward"
|
|
16
|
+
FULL = "full"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ChangeTypeEnum(str, Enum):
|
|
20
|
+
"""Types of schema changes."""
|
|
21
|
+
|
|
22
|
+
FIELD_ADDED = "field_added"
|
|
23
|
+
FIELD_REMOVED = "field_removed"
|
|
24
|
+
FIELD_RENAMED = "field_renamed"
|
|
25
|
+
TYPE_CHANGED = "type_changed"
|
|
26
|
+
TYPE_WIDENED = "type_widened"
|
|
27
|
+
TYPE_NARROWED = "type_narrowed"
|
|
28
|
+
CONSTRAINT_ADDED = "constraint_added"
|
|
29
|
+
CONSTRAINT_REMOVED = "constraint_removed"
|
|
30
|
+
CONSTRAINT_MODIFIED = "constraint_modified"
|
|
31
|
+
REQUIRED_ADDED = "required_added"
|
|
32
|
+
REQUIRED_REMOVED = "required_removed"
|
|
33
|
+
ENUM_VALUE_ADDED = "enum_value_added"
|
|
34
|
+
ENUM_VALUE_REMOVED = "enum_value_removed"
|
|
35
|
+
DEFAULT_ADDED = "default_added"
|
|
36
|
+
DEFAULT_REMOVED = "default_removed"
|
|
37
|
+
DEFAULT_CHANGED = "default_changed"
|
|
38
|
+
DESCRIPTION_CHANGED = "description_changed"
|
|
39
|
+
TITLE_CHANGED = "title_changed"
|
|
40
|
+
FORMAT_CHANGED = "format_changed"
|
|
41
|
+
PATTERN_CHANGED = "pattern_changed"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SchemaChangeResponse(BaseModel):
|
|
45
|
+
"""Response model for a single schema change."""
|
|
46
|
+
|
|
47
|
+
path: str = Field(..., description="JSON path to the changed element")
|
|
48
|
+
change_type: ChangeTypeEnum = Field(..., description="Type of change")
|
|
49
|
+
old_value: Optional[Any] = Field(default=None, description="Value in old schema")
|
|
50
|
+
new_value: Optional[Any] = Field(default=None, description="Value in new schema")
|
|
51
|
+
breaking: bool = Field(..., description="Whether this is a breaking change")
|
|
52
|
+
message: str = Field(..., description="Human-readable description")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SchemaDiffResponse(BaseModel):
|
|
56
|
+
"""Response model for schema diff."""
|
|
57
|
+
|
|
58
|
+
changes: List[SchemaChangeResponse] = Field(..., description="All detected changes")
|
|
59
|
+
breaking_changes: List[SchemaChangeResponse] = Field(
|
|
60
|
+
..., description="Only breaking changes"
|
|
61
|
+
)
|
|
62
|
+
additions: List[SchemaChangeResponse] = Field(..., description="Added elements")
|
|
63
|
+
removals: List[SchemaChangeResponse] = Field(..., description="Removed elements")
|
|
64
|
+
modifications: List[SchemaChangeResponse] = Field(
|
|
65
|
+
..., description="Modified elements"
|
|
66
|
+
)
|
|
67
|
+
has_breaking_changes: bool = Field(
|
|
68
|
+
..., description="Whether any breaking changes exist"
|
|
69
|
+
)
|
|
70
|
+
total_changes: int = Field(..., description="Total number of changes")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class CompatibilityCheckRequest(BaseModel):
|
|
74
|
+
"""Request model for checking compatibility between two schemas."""
|
|
75
|
+
|
|
76
|
+
old_schema: Dict[str, Any] = Field(..., description="Original/existing schema")
|
|
77
|
+
new_schema: Dict[str, Any] = Field(..., description="New schema to check")
|
|
78
|
+
mode: CompatibilityModeEnum = Field(
|
|
79
|
+
default=CompatibilityModeEnum.BACKWARD,
|
|
80
|
+
description="Compatibility mode to check against",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class CompatibilityCheckResponse(BaseModel):
|
|
85
|
+
"""Response model for compatibility check."""
|
|
86
|
+
|
|
87
|
+
compatible: bool = Field(
|
|
88
|
+
..., description="Whether schemas are compatible in the specified mode"
|
|
89
|
+
)
|
|
90
|
+
mode: CompatibilityModeEnum = Field(..., description="Compatibility mode used")
|
|
91
|
+
diff: Optional[SchemaDiffResponse] = Field(
|
|
92
|
+
default=None, description="Detailed schema diff"
|
|
93
|
+
)
|
|
94
|
+
issues: List[str] = Field(
|
|
95
|
+
default_factory=list, description="Breaking change descriptions"
|
|
96
|
+
)
|
|
97
|
+
warnings: List[str] = Field(
|
|
98
|
+
default_factory=list, description="Non-breaking issues that may need attention"
|
|
99
|
+
)
|
|
100
|
+
breaking_change_count: int = Field(
|
|
101
|
+
..., description="Number of breaking changes detected"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class DiffRequest(BaseModel):
|
|
106
|
+
"""Request model for computing schema diff."""
|
|
107
|
+
|
|
108
|
+
old_schema: Dict[str, Any] = Field(..., description="Original schema")
|
|
109
|
+
new_schema: Dict[str, Any] = Field(..., description="New schema")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class StoredSchemaDiffRequest(BaseModel):
|
|
113
|
+
"""Request model for diffing stored schema versions."""
|
|
114
|
+
|
|
115
|
+
schema_name: str = Field(..., description="Name of the schema")
|
|
116
|
+
old_version: str = Field(..., description="Old version to compare")
|
|
117
|
+
new_version: str = Field(..., description="New version to compare")
|
api/models/tracking.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API models for quality tracking endpoints.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExportFormat(str, Enum):
|
|
13
|
+
"""Supported export formats."""
|
|
14
|
+
|
|
15
|
+
JSON = "json"
|
|
16
|
+
PROMETHEUS = "prometheus"
|
|
17
|
+
CSV = "csv"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ValidationMetricResponse(BaseModel):
|
|
21
|
+
"""Response model for a single validation metric."""
|
|
22
|
+
|
|
23
|
+
id: str = Field(..., description="Unique metric identifier")
|
|
24
|
+
schema_name: str = Field(..., description="Name of the schema validated")
|
|
25
|
+
version: str = Field(..., description="Version of the schema")
|
|
26
|
+
timestamp: datetime = Field(..., description="When the validation occurred")
|
|
27
|
+
record_count: int = Field(..., description="Number of records validated")
|
|
28
|
+
valid_count: int = Field(..., description="Number of valid records")
|
|
29
|
+
error_count: int = Field(..., description="Number of records with errors")
|
|
30
|
+
validity_rate: float = Field(..., description="Ratio of valid records (0.0 to 1.0)")
|
|
31
|
+
completeness: float = Field(..., description="Data completeness ratio (0.0 to 1.0)")
|
|
32
|
+
field_completeness: Dict[str, float] = Field(
|
|
33
|
+
default_factory=dict, description="Per-field completeness ratios"
|
|
34
|
+
)
|
|
35
|
+
duration_ms: float = Field(..., description="Validation duration in milliseconds")
|
|
36
|
+
errors_by_type: Dict[str, int] = Field(
|
|
37
|
+
default_factory=dict, description="Error counts by type"
|
|
38
|
+
)
|
|
39
|
+
metadata: Dict[str, Any] = Field(
|
|
40
|
+
default_factory=dict, description="Additional metadata"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MetricsSummaryResponse(BaseModel):
|
|
45
|
+
"""Response model for aggregated metrics summary."""
|
|
46
|
+
|
|
47
|
+
schema_name: str = Field(..., description="Name of the schema")
|
|
48
|
+
period_start: datetime = Field(..., description="Start of the summary period")
|
|
49
|
+
period_end: datetime = Field(..., description="End of the summary period")
|
|
50
|
+
total_validations: int = Field(..., description="Total validation runs in period")
|
|
51
|
+
total_records: int = Field(..., description="Total records validated")
|
|
52
|
+
total_valid: int = Field(..., description="Total valid records")
|
|
53
|
+
total_errors: int = Field(..., description="Total records with errors")
|
|
54
|
+
avg_validity_rate: float = Field(..., description="Average validity rate")
|
|
55
|
+
min_validity_rate: float = Field(..., description="Minimum validity rate")
|
|
56
|
+
max_validity_rate: float = Field(..., description="Maximum validity rate")
|
|
57
|
+
avg_completeness: float = Field(..., description="Average completeness")
|
|
58
|
+
avg_duration_ms: float = Field(..., description="Average validation duration")
|
|
59
|
+
overall_validity_rate: float = Field(..., description="Overall validity rate from totals")
|
|
60
|
+
top_error_types: Dict[str, int] = Field(
|
|
61
|
+
default_factory=dict, description="Most common error types"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MetricsQueryResponse(BaseModel):
|
|
66
|
+
"""Response model for metrics query."""
|
|
67
|
+
|
|
68
|
+
metrics: List[ValidationMetricResponse] = Field(
|
|
69
|
+
..., description="List of matching metrics"
|
|
70
|
+
)
|
|
71
|
+
total: int = Field(..., description="Total number of metrics matching filters")
|
|
72
|
+
limit: int = Field(..., description="Maximum results returned")
|
|
73
|
+
offset: int = Field(..., description="Pagination offset")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RecordMetricRequest(BaseModel):
|
|
77
|
+
"""Request model for recording a validation metric."""
|
|
78
|
+
|
|
79
|
+
schema_name: str = Field(..., description="Name of the schema validated")
|
|
80
|
+
version: str = Field(..., description="Version of the schema")
|
|
81
|
+
record_count: int = Field(default=1, description="Number of records validated")
|
|
82
|
+
valid_count: int = Field(default=1, description="Number of valid records")
|
|
83
|
+
error_count: int = Field(default=0, description="Number of records with errors")
|
|
84
|
+
validity_rate: Optional[float] = Field(
|
|
85
|
+
default=None, description="Validity rate (calculated if not provided)"
|
|
86
|
+
)
|
|
87
|
+
completeness: float = Field(default=1.0, description="Data completeness")
|
|
88
|
+
field_completeness: Dict[str, float] = Field(
|
|
89
|
+
default_factory=dict, description="Per-field completeness"
|
|
90
|
+
)
|
|
91
|
+
duration_ms: float = Field(default=0.0, description="Validation duration")
|
|
92
|
+
errors_by_type: Dict[str, int] = Field(
|
|
93
|
+
default_factory=dict, description="Error counts by type"
|
|
94
|
+
)
|
|
95
|
+
metadata: Dict[str, Any] = Field(
|
|
96
|
+
default_factory=dict, description="Additional metadata"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ExportResponse(BaseModel):
|
|
101
|
+
"""Response model for metrics export."""
|
|
102
|
+
|
|
103
|
+
format: ExportFormat = Field(..., description="Export format used")
|
|
104
|
+
data: str = Field(..., description="Exported data as string")
|
|
105
|
+
count: int = Field(..., description="Number of metrics exported")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SchemaListResponse(BaseModel):
|
|
109
|
+
"""Response model for list of schemas with metrics."""
|
|
110
|
+
|
|
111
|
+
schemas: List[str] = Field(..., description="List of schema names with recorded metrics")
|
api/models/validation.py
CHANGED
|
@@ -15,6 +15,7 @@ class ValidationRequest(BaseModel):
|
|
|
15
15
|
data: Dict[str, Any] = Field(..., description="Data to validate")
|
|
16
16
|
version: Optional[str] = Field(None, description="Schema version (default: latest)")
|
|
17
17
|
strict: bool = Field(False, description="If True, raise exceptions on validation errors")
|
|
18
|
+
include_quality: bool = Field(False, description="If True, include quality metrics in response")
|
|
18
19
|
|
|
19
20
|
class Config:
|
|
20
21
|
json_schema_extra = {
|
|
@@ -25,7 +26,8 @@ class ValidationRequest(BaseModel):
|
|
|
25
26
|
"age": 30
|
|
26
27
|
},
|
|
27
28
|
"version": "1.0.0",
|
|
28
|
-
"strict": False
|
|
29
|
+
"strict": False,
|
|
30
|
+
"include_quality": False
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -47,6 +49,29 @@ class ValidationErrorDetail(BaseModel):
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
class ValidationQualityMetrics(BaseModel):
|
|
53
|
+
"""Quality metrics for validated data."""
|
|
54
|
+
|
|
55
|
+
completeness: float = Field(..., description="Ratio of non-null fields (0.0 to 1.0)")
|
|
56
|
+
field_completeness: Dict[str, float] = Field(default_factory=dict, description="Per-field completeness ratios")
|
|
57
|
+
record_count: int = Field(0, description="Number of records validated")
|
|
58
|
+
valid_count: int = Field(0, description="Number of valid records")
|
|
59
|
+
error_count: int = Field(0, description="Number of records with errors")
|
|
60
|
+
validity_rate: float = Field(..., description="Ratio of valid records")
|
|
61
|
+
|
|
62
|
+
class Config:
|
|
63
|
+
json_schema_extra = {
|
|
64
|
+
"example": {
|
|
65
|
+
"completeness": 0.95,
|
|
66
|
+
"field_completeness": {"name": 1.0, "age": 0.9, "email": 0.85},
|
|
67
|
+
"record_count": 100,
|
|
68
|
+
"valid_count": 95,
|
|
69
|
+
"error_count": 5,
|
|
70
|
+
"validity_rate": 0.95
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
50
75
|
class ValidationResponse(BaseModel):
|
|
51
76
|
"""Response model for validation result."""
|
|
52
77
|
|
|
@@ -54,6 +79,7 @@ class ValidationResponse(BaseModel):
|
|
|
54
79
|
data: Optional[Dict[str, Any]] = Field(None, description="Validated data (if valid)")
|
|
55
80
|
errors: List[ValidationErrorDetail] = Field(default_factory=list, description="Validation errors (if invalid)")
|
|
56
81
|
error_count: int = Field(0, description="Number of validation errors")
|
|
82
|
+
quality: Optional[ValidationQualityMetrics] = Field(None, description="Quality metrics (if requested)")
|
|
57
83
|
|
|
58
84
|
class Config:
|
|
59
85
|
json_schema_extra = {
|
|
@@ -64,7 +90,8 @@ class ValidationResponse(BaseModel):
|
|
|
64
90
|
"age": 30
|
|
65
91
|
},
|
|
66
92
|
"errors": [],
|
|
67
|
-
"error_count": 0
|
|
93
|
+
"error_count": 0,
|
|
94
|
+
"quality": None
|
|
68
95
|
}
|
|
69
96
|
}
|
|
70
97
|
|
|
@@ -77,6 +104,7 @@ class ValidationBatchRequest(BaseModel):
|
|
|
77
104
|
data_list: List[Dict[str, Any]] = Field(..., description="List of data dictionaries to validate")
|
|
78
105
|
version: Optional[str] = Field(None, description="Schema version (default: latest)")
|
|
79
106
|
strict: bool = Field(False, description="If True, raise exceptions on validation errors")
|
|
107
|
+
include_quality: bool = Field(False, description="If True, include quality metrics in response")
|
|
80
108
|
|
|
81
109
|
class Config:
|
|
82
110
|
json_schema_extra = {
|
|
@@ -87,7 +115,8 @@ class ValidationBatchRequest(BaseModel):
|
|
|
87
115
|
{"name": "Bob", "age": 25}
|
|
88
116
|
],
|
|
89
117
|
"version": "1.0.0",
|
|
90
|
-
"strict": False
|
|
118
|
+
"strict": False,
|
|
119
|
+
"include_quality": False
|
|
91
120
|
}
|
|
92
121
|
}
|
|
93
122
|
|
|
@@ -99,6 +128,7 @@ class ValidationBatchResponse(BaseModel):
|
|
|
99
128
|
total_count: int = Field(..., description="Total number of items validated")
|
|
100
129
|
valid_count: int = Field(..., description="Number of valid items")
|
|
101
130
|
invalid_count: int = Field(..., description="Number of invalid items")
|
|
131
|
+
quality: Optional[ValidationQualityMetrics] = Field(None, description="Aggregate quality metrics (if requested)")
|
|
102
132
|
|
|
103
133
|
class Config:
|
|
104
134
|
json_schema_extra = {
|
|
@@ -108,7 +138,8 @@ class ValidationBatchResponse(BaseModel):
|
|
|
108
138
|
"is_valid": True,
|
|
109
139
|
"data": {"name": "Alice", "age": 30},
|
|
110
140
|
"errors": [],
|
|
111
|
-
"error_count": 0
|
|
141
|
+
"error_count": 0,
|
|
142
|
+
"quality": None
|
|
112
143
|
},
|
|
113
144
|
{
|
|
114
145
|
"is_valid": False,
|
|
@@ -120,12 +151,21 @@ class ValidationBatchResponse(BaseModel):
|
|
|
120
151
|
"input_value": -5
|
|
121
152
|
}
|
|
122
153
|
],
|
|
123
|
-
"error_count": 1
|
|
154
|
+
"error_count": 1,
|
|
155
|
+
"quality": None
|
|
124
156
|
}
|
|
125
157
|
],
|
|
126
158
|
"total_count": 2,
|
|
127
159
|
"valid_count": 1,
|
|
128
|
-
"invalid_count": 1
|
|
160
|
+
"invalid_count": 1,
|
|
161
|
+
"quality": {
|
|
162
|
+
"completeness": 0.95,
|
|
163
|
+
"field_completeness": {"name": 1.0, "age": 0.9},
|
|
164
|
+
"record_count": 2,
|
|
165
|
+
"valid_count": 1,
|
|
166
|
+
"error_count": 1,
|
|
167
|
+
"validity_rate": 0.5
|
|
168
|
+
}
|
|
129
169
|
}
|
|
130
170
|
}
|
|
131
171
|
|
api/routes/v1/__init__.py
CHANGED
|
@@ -6,7 +6,17 @@ All routers defined here are automatically included in the main application
|
|
|
6
6
|
with the `/api/v1` prefix.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from api.routes.v1 import
|
|
9
|
+
from api.routes.v1 import (
|
|
10
|
+
contracts,
|
|
11
|
+
metadata,
|
|
12
|
+
quality,
|
|
13
|
+
schemas,
|
|
14
|
+
validation,
|
|
15
|
+
settings,
|
|
16
|
+
docs,
|
|
17
|
+
tracking,
|
|
18
|
+
evolution,
|
|
19
|
+
)
|
|
10
20
|
|
|
11
21
|
# Export all routers for automatic inclusion in main.py
|
|
12
22
|
__all__ = [
|
|
@@ -16,5 +26,8 @@ __all__ = [
|
|
|
16
26
|
"schemas",
|
|
17
27
|
"validation",
|
|
18
28
|
"settings",
|
|
29
|
+
"docs",
|
|
30
|
+
"tracking",
|
|
31
|
+
"evolution",
|
|
19
32
|
]
|
|
20
33
|
|
api/routes/v1/docs.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API routes for documentation generation.
|
|
3
|
+
|
|
4
|
+
Provides endpoints to generate human-readable documentation
|
|
5
|
+
from data contracts.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
11
|
+
|
|
12
|
+
from api.dependencies.store import get_metadata_store
|
|
13
|
+
from api.models.docs import (
|
|
14
|
+
DocsFormat,
|
|
15
|
+
DocsRequest,
|
|
16
|
+
DocsResponse,
|
|
17
|
+
DocsSectionRequest,
|
|
18
|
+
DocsSectionResponse,
|
|
19
|
+
)
|
|
20
|
+
from pycharter.contract_parser import parse_contract
|
|
21
|
+
from pycharter.docs_generator import DocsGenerator, generate_docs
|
|
22
|
+
from pycharter.docs_generator.renderers import HTMLRenderer, MarkdownRenderer
|
|
23
|
+
from pycharter.metadata_store import MetadataStoreClient
|
|
24
|
+
|
|
25
|
+
router = APIRouter(prefix="/docs", tags=["Documentation"])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.post(
|
|
29
|
+
"/generate",
|
|
30
|
+
response_model=DocsResponse,
|
|
31
|
+
summary="Generate documentation from contract",
|
|
32
|
+
description="Generate human-readable documentation from contract data",
|
|
33
|
+
)
|
|
34
|
+
async def generate_documentation(request: DocsRequest) -> DocsResponse:
|
|
35
|
+
"""
|
|
36
|
+
Generate documentation from a contract.
|
|
37
|
+
|
|
38
|
+
Accepts contract data and generates formatted documentation
|
|
39
|
+
in the specified format (Markdown or HTML).
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
# Parse the contract
|
|
43
|
+
contract = parse_contract(request.contract, validate=False)
|
|
44
|
+
|
|
45
|
+
# Generate documentation
|
|
46
|
+
docs = generate_docs(
|
|
47
|
+
contract,
|
|
48
|
+
format=request.format.value,
|
|
49
|
+
include_schema=request.include_schema,
|
|
50
|
+
include_coercions=request.include_coercions,
|
|
51
|
+
include_validations=request.include_validations,
|
|
52
|
+
include_metadata=request.include_metadata,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Extract schema name and version
|
|
56
|
+
schema_name = contract.schema.get("title") or contract.metadata.get("title")
|
|
57
|
+
version = contract.versions.get("schema") or contract.schema.get("version")
|
|
58
|
+
|
|
59
|
+
return DocsResponse(
|
|
60
|
+
documentation=docs,
|
|
61
|
+
format=request.format,
|
|
62
|
+
schema_name=schema_name,
|
|
63
|
+
version=version,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
raise HTTPException(status_code=400, detail=f"Failed to generate documentation: {e}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@router.post(
|
|
71
|
+
"/section",
|
|
72
|
+
response_model=DocsSectionResponse,
|
|
73
|
+
summary="Generate a specific documentation section",
|
|
74
|
+
description="Generate a specific section of documentation from contract data",
|
|
75
|
+
)
|
|
76
|
+
async def generate_section(request: DocsSectionRequest) -> DocsSectionResponse:
|
|
77
|
+
"""
|
|
78
|
+
Generate a specific section of documentation.
|
|
79
|
+
|
|
80
|
+
Sections: 'schema', 'coercions', 'validations', 'metadata'
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
# Parse the contract
|
|
84
|
+
contract = parse_contract(request.contract, validate=False)
|
|
85
|
+
|
|
86
|
+
# Select renderer
|
|
87
|
+
if request.format == DocsFormat.HTML:
|
|
88
|
+
renderer = HTMLRenderer()
|
|
89
|
+
else:
|
|
90
|
+
renderer = MarkdownRenderer()
|
|
91
|
+
|
|
92
|
+
generator = DocsGenerator(renderer=renderer)
|
|
93
|
+
|
|
94
|
+
# Generate the requested section
|
|
95
|
+
section = request.section.lower()
|
|
96
|
+
if section == "schema":
|
|
97
|
+
content = generator.generate_schema_section(contract.schema)
|
|
98
|
+
elif section in ("coercions", "coercion_rules"):
|
|
99
|
+
content = generator.generate_coercion_section(contract.coercion_rules)
|
|
100
|
+
elif section in ("validations", "validation_rules"):
|
|
101
|
+
content = generator.generate_validation_section(contract.validation_rules)
|
|
102
|
+
elif section == "metadata":
|
|
103
|
+
content = generator.generate_metadata_section(contract)
|
|
104
|
+
else:
|
|
105
|
+
raise HTTPException(
|
|
106
|
+
status_code=400,
|
|
107
|
+
detail=f"Unknown section: {section}. Valid sections: schema, coercions, validations, metadata",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return DocsSectionResponse(
|
|
111
|
+
section=section,
|
|
112
|
+
content=content,
|
|
113
|
+
format=request.format,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
except HTTPException:
|
|
117
|
+
raise
|
|
118
|
+
except Exception as e:
|
|
119
|
+
raise HTTPException(status_code=400, detail=f"Failed to generate section: {e}")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@router.get(
|
|
123
|
+
"/{schema_name}",
|
|
124
|
+
response_model=DocsResponse,
|
|
125
|
+
summary="Generate documentation for a stored schema",
|
|
126
|
+
description="Generate documentation for a schema stored in the metadata store",
|
|
127
|
+
)
|
|
128
|
+
async def get_schema_docs(
|
|
129
|
+
schema_name: str,
|
|
130
|
+
version: Optional[str] = Query(default=None, description="Schema version (latest if not specified)"),
|
|
131
|
+
format: DocsFormat = Query(default=DocsFormat.MARKDOWN, description="Output format"),
|
|
132
|
+
store: MetadataStoreClient = Depends(get_metadata_store),
|
|
133
|
+
) -> DocsResponse:
|
|
134
|
+
"""
|
|
135
|
+
Generate documentation for a schema from the metadata store.
|
|
136
|
+
|
|
137
|
+
Retrieves the schema from the store and generates documentation.
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
# Get the complete schema from store
|
|
141
|
+
schema_data = store.get_complete_schema(schema_name, version=version)
|
|
142
|
+
|
|
143
|
+
if not schema_data:
|
|
144
|
+
raise HTTPException(
|
|
145
|
+
status_code=404, detail=f"Schema not found: {schema_name}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Build contract data from stored components
|
|
149
|
+
contract_data = {"schema": schema_data}
|
|
150
|
+
|
|
151
|
+
# Try to get additional components
|
|
152
|
+
try:
|
|
153
|
+
coercion_rules = store.get_coercion_rules(schema_name, version=version)
|
|
154
|
+
if coercion_rules:
|
|
155
|
+
contract_data["coercion_rules"] = coercion_rules
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
validation_rules = store.get_validation_rules(schema_name, version=version)
|
|
161
|
+
if validation_rules:
|
|
162
|
+
contract_data["validation_rules"] = validation_rules
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
metadata = store.get_metadata(schema_name, version=version)
|
|
168
|
+
if metadata:
|
|
169
|
+
contract_data["metadata"] = metadata
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
# Parse and generate docs
|
|
174
|
+
contract = parse_contract(contract_data, validate=False)
|
|
175
|
+
docs = generate_docs(contract, format=format.value)
|
|
176
|
+
|
|
177
|
+
return DocsResponse(
|
|
178
|
+
documentation=docs,
|
|
179
|
+
format=format,
|
|
180
|
+
schema_name=schema_name,
|
|
181
|
+
version=version or contract.versions.get("schema"),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
except HTTPException:
|
|
185
|
+
raise
|
|
186
|
+
except Exception as e:
|
|
187
|
+
raise HTTPException(status_code=500, detail=f"Failed to generate documentation: {e}")
|