pycharter 0.0.22__py3-none-any.whl → 0.0.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api/main.py +27 -1
- api/models/docs.py +68 -0
- api/models/evolution.py +117 -0
- api/models/tracking.py +111 -0
- api/models/validation.py +46 -6
- api/routes/v1/__init__.py +14 -1
- api/routes/v1/docs.py +187 -0
- api/routes/v1/evolution.py +337 -0
- api/routes/v1/templates.py +211 -27
- api/routes/v1/tracking.py +301 -0
- api/routes/v1/validation.py +68 -31
- pycharter/__init__.py +268 -58
- pycharter/data/templates/contract/template_coercion_rules.yaml +57 -0
- pycharter/data/templates/contract/template_contract.yaml +122 -0
- pycharter/data/templates/contract/template_metadata.yaml +68 -0
- pycharter/data/templates/contract/template_schema.yaml +100 -0
- pycharter/data/templates/contract/template_validation_rules.yaml +75 -0
- pycharter/data/templates/etl/README.md +224 -0
- pycharter/data/templates/etl/extract_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/extract_cloud_gcs.yaml +25 -0
- pycharter/data/templates/etl/extract_cloud_s3.yaml +30 -0
- pycharter/data/templates/etl/extract_database.yaml +34 -0
- pycharter/data/templates/etl/extract_database_ssh.yaml +40 -0
- pycharter/data/templates/etl/extract_file_csv.yaml +21 -0
- pycharter/data/templates/etl/extract_file_glob.yaml +25 -0
- pycharter/data/templates/etl/extract_file_json.yaml +24 -0
- pycharter/data/templates/etl/extract_file_parquet.yaml +20 -0
- pycharter/data/templates/etl/extract_http_paginated.yaml +79 -0
- pycharter/data/templates/etl/extract_http_path_params.yaml +38 -0
- pycharter/data/templates/etl/extract_http_simple.yaml +62 -0
- pycharter/data/templates/etl/load_cloud_azure.yaml +24 -0
- pycharter/data/templates/etl/load_cloud_gcs.yaml +22 -0
- pycharter/data/templates/etl/load_cloud_s3.yaml +27 -0
- pycharter/data/templates/etl/load_file.yaml +34 -0
- pycharter/data/templates/etl/load_insert.yaml +18 -0
- pycharter/data/templates/etl/load_postgresql.yaml +39 -0
- pycharter/data/templates/etl/load_sqlite.yaml +21 -0
- pycharter/data/templates/etl/load_truncate_and_load.yaml +20 -0
- pycharter/data/templates/etl/load_upsert.yaml +25 -0
- pycharter/data/templates/etl/load_with_dlq.yaml +34 -0
- pycharter/data/templates/etl/load_with_ssh_tunnel.yaml +35 -0
- pycharter/data/templates/etl/pipeline_http_to_db.yaml +75 -0
- pycharter/data/templates/etl/transform_combined.yaml +48 -0
- pycharter/data/templates/etl/transform_custom_function.yaml +58 -0
- pycharter/data/templates/etl/transform_jsonata.yaml +51 -0
- pycharter/data/templates/etl/transform_simple.yaml +59 -0
- pycharter/db/schemas/.ipynb_checkpoints/data_contract-checkpoint.py +160 -0
- pycharter/docs_generator/__init__.py +43 -0
- pycharter/docs_generator/generator.py +465 -0
- pycharter/docs_generator/renderers.py +247 -0
- pycharter/etl_generator/__init__.py +168 -80
- pycharter/etl_generator/builder.py +121 -0
- pycharter/etl_generator/config_loader.py +394 -0
- pycharter/etl_generator/config_validator.py +418 -0
- pycharter/etl_generator/context.py +132 -0
- pycharter/etl_generator/expression.py +499 -0
- pycharter/etl_generator/extractors/__init__.py +30 -0
- pycharter/etl_generator/extractors/base.py +70 -0
- pycharter/etl_generator/extractors/cloud_storage.py +530 -0
- pycharter/etl_generator/extractors/database.py +221 -0
- pycharter/etl_generator/extractors/factory.py +185 -0
- pycharter/etl_generator/extractors/file.py +475 -0
- pycharter/etl_generator/extractors/http.py +895 -0
- pycharter/etl_generator/extractors/streaming.py +57 -0
- pycharter/etl_generator/loaders/__init__.py +41 -0
- pycharter/etl_generator/loaders/base.py +35 -0
- pycharter/etl_generator/loaders/cloud.py +87 -0
- pycharter/etl_generator/loaders/cloud_storage_loader.py +275 -0
- pycharter/etl_generator/loaders/database.py +274 -0
- pycharter/etl_generator/loaders/factory.py +180 -0
- pycharter/etl_generator/loaders/file.py +72 -0
- pycharter/etl_generator/loaders/file_loader.py +130 -0
- pycharter/etl_generator/pipeline.py +743 -0
- pycharter/etl_generator/protocols.py +54 -0
- pycharter/etl_generator/result.py +63 -0
- pycharter/etl_generator/schemas/__init__.py +49 -0
- pycharter/etl_generator/transformers/__init__.py +49 -0
- pycharter/etl_generator/transformers/base.py +63 -0
- pycharter/etl_generator/transformers/config.py +45 -0
- pycharter/etl_generator/transformers/custom_function.py +101 -0
- pycharter/etl_generator/transformers/jsonata_transformer.py +56 -0
- pycharter/etl_generator/transformers/operations.py +218 -0
- pycharter/etl_generator/transformers/pipeline.py +54 -0
- pycharter/etl_generator/transformers/simple_operations.py +131 -0
- pycharter/quality/__init__.py +25 -0
- pycharter/quality/tracking/__init__.py +64 -0
- pycharter/quality/tracking/collector.py +318 -0
- pycharter/quality/tracking/exporters.py +238 -0
- pycharter/quality/tracking/models.py +194 -0
- pycharter/quality/tracking/store.py +385 -0
- pycharter/runtime_validator/__init__.py +20 -7
- pycharter/runtime_validator/builder.py +328 -0
- pycharter/runtime_validator/validator.py +311 -7
- pycharter/runtime_validator/validator_core.py +61 -0
- pycharter/schema_evolution/__init__.py +61 -0
- pycharter/schema_evolution/compatibility.py +270 -0
- pycharter/schema_evolution/diff.py +496 -0
- pycharter/schema_evolution/models.py +201 -0
- pycharter/shared/__init__.py +56 -0
- pycharter/shared/errors.py +296 -0
- pycharter/shared/protocols.py +234 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/METADATA +146 -26
- pycharter-0.0.24.dist-info/RECORD +543 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/WHEEL +1 -1
- ui/static/404/index.html +1 -1
- ui/static/404.html +1 -1
- ui/static/__next.__PAGE__.txt +1 -1
- ui/static/__next._full.txt +1 -1
- ui/static/__next._head.txt +1 -1
- ui/static/__next._index.txt +1 -1
- ui/static/__next._tree.txt +1 -1
- ui/static/_next/static/chunks/26dfc590f7714c03.js +1 -0
- ui/static/_next/static/chunks/34d289e6db2ef551.js +1 -0
- ui/static/_next/static/chunks/99508d9d5869cc27.js +1 -0
- ui/static/_next/static/chunks/b313c35a6ba76574.js +1 -0
- ui/static/_not-found/__next._full.txt +1 -1
- ui/static/_not-found/__next._head.txt +1 -1
- ui/static/_not-found/__next._index.txt +1 -1
- ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/_not-found/__next._not-found.txt +1 -1
- ui/static/_not-found/__next._tree.txt +1 -1
- ui/static/_not-found/index.html +1 -1
- ui/static/_not-found/index.txt +1 -1
- ui/static/contracts/__next._full.txt +2 -2
- ui/static/contracts/__next._head.txt +1 -1
- ui/static/contracts/__next._index.txt +1 -1
- ui/static/contracts/__next._tree.txt +1 -1
- ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/contracts/__next.contracts.txt +1 -1
- ui/static/contracts/index.html +1 -1
- ui/static/contracts/index.txt +2 -2
- ui/static/documentation/__next._full.txt +1 -1
- ui/static/documentation/__next._head.txt +1 -1
- ui/static/documentation/__next._index.txt +1 -1
- ui/static/documentation/__next._tree.txt +1 -1
- ui/static/documentation/__next.documentation.__PAGE__.txt +1 -1
- ui/static/documentation/__next.documentation.txt +1 -1
- ui/static/documentation/index.html +2 -2
- ui/static/documentation/index.txt +1 -1
- ui/static/index.html +1 -1
- ui/static/index.txt +1 -1
- ui/static/metadata/__next._full.txt +1 -1
- ui/static/metadata/__next._head.txt +1 -1
- ui/static/metadata/__next._index.txt +1 -1
- ui/static/metadata/__next._tree.txt +1 -1
- ui/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/metadata/__next.metadata.txt +1 -1
- ui/static/metadata/index.html +1 -1
- ui/static/metadata/index.txt +1 -1
- ui/static/quality/__next._full.txt +2 -2
- ui/static/quality/__next._head.txt +1 -1
- ui/static/quality/__next._index.txt +1 -1
- ui/static/quality/__next._tree.txt +1 -1
- ui/static/quality/__next.quality.__PAGE__.txt +2 -2
- ui/static/quality/__next.quality.txt +1 -1
- ui/static/quality/index.html +2 -2
- ui/static/quality/index.txt +2 -2
- ui/static/rules/__next._full.txt +1 -1
- ui/static/rules/__next._head.txt +1 -1
- ui/static/rules/__next._index.txt +1 -1
- ui/static/rules/__next._tree.txt +1 -1
- ui/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/rules/__next.rules.txt +1 -1
- ui/static/rules/index.html +1 -1
- ui/static/rules/index.txt +1 -1
- ui/static/schemas/__next._full.txt +1 -1
- ui/static/schemas/__next._head.txt +1 -1
- ui/static/schemas/__next._index.txt +1 -1
- ui/static/schemas/__next._tree.txt +1 -1
- ui/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/schemas/__next.schemas.txt +1 -1
- ui/static/schemas/index.html +1 -1
- ui/static/schemas/index.txt +1 -1
- ui/static/settings/__next._full.txt +1 -1
- ui/static/settings/__next._head.txt +1 -1
- ui/static/settings/__next._index.txt +1 -1
- ui/static/settings/__next._tree.txt +1 -1
- ui/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/settings/__next.settings.txt +1 -1
- ui/static/settings/index.html +1 -1
- ui/static/settings/index.txt +1 -1
- ui/static/static/404/index.html +1 -1
- ui/static/static/404.html +1 -1
- ui/static/static/__next.__PAGE__.txt +1 -1
- ui/static/static/__next._full.txt +2 -2
- ui/static/static/__next._head.txt +1 -1
- ui/static/static/__next._index.txt +2 -2
- ui/static/static/__next._tree.txt +2 -2
- ui/static/static/_next/static/chunks/13d4a0fbd74c1ee4.js +1 -0
- ui/static/static/_next/static/chunks/2edb43b48432ac04.js +441 -0
- ui/static/static/_next/static/chunks/d2363397e1b2bcab.css +1 -0
- ui/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
- ui/static/static/_not-found/__next._full.txt +2 -2
- ui/static/static/_not-found/__next._head.txt +1 -1
- ui/static/static/_not-found/__next._index.txt +2 -2
- ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
- ui/static/static/_not-found/__next._not-found.txt +1 -1
- ui/static/static/_not-found/__next._tree.txt +2 -2
- ui/static/static/_not-found/index.html +1 -1
- ui/static/static/_not-found/index.txt +2 -2
- ui/static/static/contracts/__next._full.txt +3 -3
- ui/static/static/contracts/__next._head.txt +1 -1
- ui/static/static/contracts/__next._index.txt +2 -2
- ui/static/static/contracts/__next._tree.txt +2 -2
- ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
- ui/static/static/contracts/__next.contracts.txt +1 -1
- ui/static/static/contracts/index.html +1 -1
- ui/static/static/contracts/index.txt +3 -3
- ui/static/static/documentation/__next._full.txt +3 -3
- ui/static/static/documentation/__next._head.txt +1 -1
- ui/static/static/documentation/__next._index.txt +2 -2
- ui/static/static/documentation/__next._tree.txt +2 -2
- ui/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
- ui/static/static/documentation/__next.documentation.txt +1 -1
- ui/static/static/documentation/index.html +2 -2
- ui/static/static/documentation/index.txt +3 -3
- ui/static/static/index.html +1 -1
- ui/static/static/index.txt +2 -2
- ui/static/static/metadata/__next._full.txt +2 -2
- ui/static/static/metadata/__next._head.txt +1 -1
- ui/static/static/metadata/__next._index.txt +2 -2
- ui/static/static/metadata/__next._tree.txt +2 -2
- ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
- ui/static/static/metadata/__next.metadata.txt +1 -1
- ui/static/static/metadata/index.html +1 -1
- ui/static/static/metadata/index.txt +2 -2
- ui/static/static/quality/__next._full.txt +2 -2
- ui/static/static/quality/__next._head.txt +1 -1
- ui/static/static/quality/__next._index.txt +2 -2
- ui/static/static/quality/__next._tree.txt +2 -2
- ui/static/static/quality/__next.quality.__PAGE__.txt +1 -1
- ui/static/static/quality/__next.quality.txt +1 -1
- ui/static/static/quality/index.html +2 -2
- ui/static/static/quality/index.txt +2 -2
- ui/static/static/rules/__next._full.txt +2 -2
- ui/static/static/rules/__next._head.txt +1 -1
- ui/static/static/rules/__next._index.txt +2 -2
- ui/static/static/rules/__next._tree.txt +2 -2
- ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
- ui/static/static/rules/__next.rules.txt +1 -1
- ui/static/static/rules/index.html +1 -1
- ui/static/static/rules/index.txt +2 -2
- ui/static/static/schemas/__next._full.txt +2 -2
- ui/static/static/schemas/__next._head.txt +1 -1
- ui/static/static/schemas/__next._index.txt +2 -2
- ui/static/static/schemas/__next._tree.txt +2 -2
- ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
- ui/static/static/schemas/__next.schemas.txt +1 -1
- ui/static/static/schemas/index.html +1 -1
- ui/static/static/schemas/index.txt +2 -2
- ui/static/static/settings/__next._full.txt +2 -2
- ui/static/static/settings/__next._head.txt +1 -1
- ui/static/static/settings/__next._index.txt +2 -2
- ui/static/static/settings/__next._tree.txt +2 -2
- ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
- ui/static/static/settings/__next.settings.txt +1 -1
- ui/static/static/settings/index.html +1 -1
- ui/static/static/settings/index.txt +2 -2
- ui/static/static/static/.gitkeep +0 -0
- ui/static/static/static/404/index.html +1 -0
- ui/static/static/static/404.html +1 -0
- ui/static/static/static/__next.__PAGE__.txt +10 -0
- ui/static/static/static/__next._full.txt +30 -0
- ui/static/static/static/__next._head.txt +7 -0
- ui/static/static/static/__next._index.txt +9 -0
- ui/static/static/static/__next._tree.txt +2 -0
- ui/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
- ui/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
- ui/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
- ui/static/static/static/_next/static/chunks/2ab439ce003cd691.js +1 -0
- ui/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
- ui/static/static/static/_next/static/chunks/49ca65abd26ae49e.js +1 -0
- ui/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
- ui/static/static/static/_next/static/chunks/9667e7a3d359eb39.js +1 -0
- ui/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
- ui/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- ui/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
- ui/static/static/static/_next/static/chunks/c69f6cba366bd988.js +1 -0
- ui/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
- ui/static/static/static/_next/static/chunks/f061a4be97bfc3b3.js +1 -0
- ui/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
- ui/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
- ui/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
- ui/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
- ui/static/static/static/_not-found/__next._full.txt +17 -0
- ui/static/static/static/_not-found/__next._head.txt +7 -0
- ui/static/static/static/_not-found/__next._index.txt +9 -0
- ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
- ui/static/static/static/_not-found/__next._not-found.txt +4 -0
- ui/static/static/static/_not-found/__next._tree.txt +2 -0
- ui/static/static/static/_not-found/index.html +1 -0
- ui/static/static/static/_not-found/index.txt +17 -0
- ui/static/static/static/contracts/__next._full.txt +21 -0
- ui/static/static/static/contracts/__next._head.txt +7 -0
- ui/static/static/static/contracts/__next._index.txt +9 -0
- ui/static/static/static/contracts/__next._tree.txt +2 -0
- ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
- ui/static/static/static/contracts/__next.contracts.txt +4 -0
- ui/static/static/static/contracts/index.html +1 -0
- ui/static/static/static/contracts/index.txt +21 -0
- ui/static/static/static/documentation/__next._full.txt +21 -0
- ui/static/static/static/documentation/__next._head.txt +7 -0
- ui/static/static/static/documentation/__next._index.txt +9 -0
- ui/static/static/static/documentation/__next._tree.txt +2 -0
- ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
- ui/static/static/static/documentation/__next.documentation.txt +4 -0
- ui/static/static/static/documentation/index.html +93 -0
- ui/static/static/static/documentation/index.txt +21 -0
- ui/static/static/static/index.html +1 -0
- ui/static/static/static/index.txt +30 -0
- ui/static/static/static/metadata/__next._full.txt +21 -0
- ui/static/static/static/metadata/__next._head.txt +7 -0
- ui/static/static/static/metadata/__next._index.txt +9 -0
- ui/static/static/static/metadata/__next._tree.txt +2 -0
- ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
- ui/static/static/static/metadata/__next.metadata.txt +4 -0
- ui/static/static/static/metadata/index.html +1 -0
- ui/static/static/static/metadata/index.txt +21 -0
- ui/static/static/static/quality/__next._full.txt +21 -0
- ui/static/static/static/quality/__next._head.txt +7 -0
- ui/static/static/static/quality/__next._index.txt +9 -0
- ui/static/static/static/quality/__next._tree.txt +2 -0
- ui/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
- ui/static/static/static/quality/__next.quality.txt +4 -0
- ui/static/static/static/quality/index.html +2 -0
- ui/static/static/static/quality/index.txt +21 -0
- ui/static/static/static/rules/__next._full.txt +21 -0
- ui/static/static/static/rules/__next._head.txt +7 -0
- ui/static/static/static/rules/__next._index.txt +9 -0
- ui/static/static/static/rules/__next._tree.txt +2 -0
- ui/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
- ui/static/static/static/rules/__next.rules.txt +4 -0
- ui/static/static/static/rules/index.html +1 -0
- ui/static/static/static/rules/index.txt +21 -0
- ui/static/static/static/schemas/__next._full.txt +21 -0
- ui/static/static/static/schemas/__next._head.txt +7 -0
- ui/static/static/static/schemas/__next._index.txt +9 -0
- ui/static/static/static/schemas/__next._tree.txt +2 -0
- ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
- ui/static/static/static/schemas/__next.schemas.txt +4 -0
- ui/static/static/static/schemas/index.html +1 -0
- ui/static/static/static/schemas/index.txt +21 -0
- ui/static/static/static/settings/__next._full.txt +21 -0
- ui/static/static/static/settings/__next._head.txt +7 -0
- ui/static/static/static/settings/__next._index.txt +9 -0
- ui/static/static/static/settings/__next._tree.txt +2 -0
- ui/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
- ui/static/static/static/settings/__next.settings.txt +4 -0
- ui/static/static/static/settings/index.html +1 -0
- ui/static/static/static/settings/index.txt +21 -0
- ui/static/static/static/validation/__next._full.txt +21 -0
- ui/static/static/static/validation/__next._head.txt +7 -0
- ui/static/static/static/validation/__next._index.txt +9 -0
- ui/static/static/static/validation/__next._tree.txt +2 -0
- ui/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
- ui/static/static/static/validation/__next.validation.txt +4 -0
- ui/static/static/static/validation/index.html +1 -0
- ui/static/static/static/validation/index.txt +21 -0
- ui/static/static/validation/__next._full.txt +2 -2
- ui/static/static/validation/__next._head.txt +1 -1
- ui/static/static/validation/__next._index.txt +2 -2
- ui/static/static/validation/__next._tree.txt +2 -2
- ui/static/static/validation/__next.validation.__PAGE__.txt +1 -1
- ui/static/static/validation/__next.validation.txt +1 -1
- ui/static/static/validation/index.html +1 -1
- ui/static/static/validation/index.txt +2 -2
- ui/static/validation/__next._full.txt +2 -2
- ui/static/validation/__next._head.txt +1 -1
- ui/static/validation/__next._index.txt +1 -1
- ui/static/validation/__next._tree.txt +1 -1
- ui/static/validation/__next.validation.__PAGE__.txt +2 -2
- ui/static/validation/__next.validation.txt +1 -1
- ui/static/validation/index.html +1 -1
- ui/static/validation/index.txt +2 -2
- pycharter/data/templates/template_coercion_rules.yaml +0 -15
- pycharter/data/templates/template_contract.yaml +0 -587
- pycharter/data/templates/template_metadata.yaml +0 -38
- pycharter/data/templates/template_schema.yaml +0 -22
- pycharter/data/templates/template_transform_advanced.yaml +0 -50
- pycharter/data/templates/template_transform_simple.yaml +0 -59
- pycharter/data/templates/template_validation_rules.yaml +0 -29
- pycharter/etl_generator/extraction.py +0 -916
- pycharter/etl_generator/factory.py +0 -174
- pycharter/etl_generator/orchestrator.py +0 -1650
- pycharter/integrations/__init__.py +0 -19
- pycharter/integrations/kafka.py +0 -178
- pycharter/integrations/streaming.py +0 -100
- pycharter-0.0.22.dist-info/RECORD +0 -358
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/entry_points.txt +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/licenses/LICENSE +0 -0
- {pycharter-0.0.22.dist-info → pycharter-0.0.24.dist-info}/top_level.txt +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
- /ui/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
- /ui/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
- /ui/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
- /ui/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
- /ui/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quality Tracking Exporters - Export metrics in various formats.
|
|
3
|
+
|
|
4
|
+
Provides functions to export validation metrics in different
|
|
5
|
+
formats for external consumption.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import List, Optional
|
|
11
|
+
|
|
12
|
+
from pycharter.quality.tracking.models import MetricsSummary, ValidationMetric
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def export_json(
|
|
16
|
+
metrics: List[ValidationMetric],
|
|
17
|
+
pretty: bool = True,
|
|
18
|
+
include_metadata: bool = True,
|
|
19
|
+
) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Export metrics as JSON.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
metrics: List of ValidationMetrics to export
|
|
25
|
+
pretty: If True, format with indentation
|
|
26
|
+
include_metadata: If True, include metric metadata
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
JSON string representation of metrics
|
|
30
|
+
"""
|
|
31
|
+
data = []
|
|
32
|
+
for m in metrics:
|
|
33
|
+
metric_dict = m.to_dict()
|
|
34
|
+
if not include_metadata:
|
|
35
|
+
metric_dict.pop("metadata", None)
|
|
36
|
+
data.append(metric_dict)
|
|
37
|
+
|
|
38
|
+
if pretty:
|
|
39
|
+
return json.dumps(data, indent=2, default=str)
|
|
40
|
+
return json.dumps(data, default=str)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def export_prometheus(
|
|
44
|
+
metrics: List[ValidationMetric],
|
|
45
|
+
summaries: Optional[List[MetricsSummary]] = None,
|
|
46
|
+
prefix: str = "pycharter",
|
|
47
|
+
) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Export metrics in Prometheus text format.
|
|
50
|
+
|
|
51
|
+
Generates Prometheus-compatible metrics that can be scraped
|
|
52
|
+
by a Prometheus server or used with pushgateway.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
metrics: List of ValidationMetrics to export
|
|
56
|
+
summaries: Optional list of MetricsSummaries to export
|
|
57
|
+
prefix: Metric name prefix
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Prometheus text format string
|
|
61
|
+
|
|
62
|
+
Example output:
|
|
63
|
+
# HELP pycharter_validation_total Total validation runs
|
|
64
|
+
# TYPE pycharter_validation_total counter
|
|
65
|
+
pycharter_validation_total{schema="users",version="1.0.0"} 100
|
|
66
|
+
"""
|
|
67
|
+
lines = []
|
|
68
|
+
timestamp_ms = int(datetime.utcnow().timestamp() * 1000)
|
|
69
|
+
|
|
70
|
+
# Helper to format labels
|
|
71
|
+
def format_labels(labels: dict) -> str:
|
|
72
|
+
parts = [f'{k}="{v}"' for k, v in labels.items()]
|
|
73
|
+
return "{" + ",".join(parts) + "}"
|
|
74
|
+
|
|
75
|
+
# Aggregate metrics by schema for summary metrics
|
|
76
|
+
schema_metrics: dict = {}
|
|
77
|
+
for m in metrics:
|
|
78
|
+
key = (m.schema_name, m.version)
|
|
79
|
+
if key not in schema_metrics:
|
|
80
|
+
schema_metrics[key] = {
|
|
81
|
+
"record_count": 0,
|
|
82
|
+
"valid_count": 0,
|
|
83
|
+
"error_count": 0,
|
|
84
|
+
"validity_rates": [],
|
|
85
|
+
"completeness_values": [],
|
|
86
|
+
"durations": [],
|
|
87
|
+
"latest": m,
|
|
88
|
+
}
|
|
89
|
+
data = schema_metrics[key]
|
|
90
|
+
data["record_count"] += m.record_count
|
|
91
|
+
data["valid_count"] += m.valid_count
|
|
92
|
+
data["error_count"] += m.error_count
|
|
93
|
+
data["validity_rates"].append(m.validity_rate)
|
|
94
|
+
data["completeness_values"].append(m.completeness)
|
|
95
|
+
data["durations"].append(m.duration_ms)
|
|
96
|
+
if m.timestamp > data["latest"].timestamp:
|
|
97
|
+
data["latest"] = m
|
|
98
|
+
|
|
99
|
+
# Total validations counter
|
|
100
|
+
lines.append(f"# HELP {prefix}_validations_total Total number of validation runs")
|
|
101
|
+
lines.append(f"# TYPE {prefix}_validations_total counter")
|
|
102
|
+
for (schema, version), data in schema_metrics.items():
|
|
103
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
104
|
+
lines.append(f"{prefix}_validations_total{labels} {len(data['validity_rates'])}")
|
|
105
|
+
|
|
106
|
+
lines.append("")
|
|
107
|
+
|
|
108
|
+
# Total records processed
|
|
109
|
+
lines.append(f"# HELP {prefix}_records_total Total number of records processed")
|
|
110
|
+
lines.append(f"# TYPE {prefix}_records_total counter")
|
|
111
|
+
for (schema, version), data in schema_metrics.items():
|
|
112
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
113
|
+
lines.append(f"{prefix}_records_total{labels} {data['record_count']}")
|
|
114
|
+
|
|
115
|
+
lines.append("")
|
|
116
|
+
|
|
117
|
+
# Valid records counter
|
|
118
|
+
lines.append(f"# HELP {prefix}_valid_records_total Total number of valid records")
|
|
119
|
+
lines.append(f"# TYPE {prefix}_valid_records_total counter")
|
|
120
|
+
for (schema, version), data in schema_metrics.items():
|
|
121
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
122
|
+
lines.append(f"{prefix}_valid_records_total{labels} {data['valid_count']}")
|
|
123
|
+
|
|
124
|
+
lines.append("")
|
|
125
|
+
|
|
126
|
+
# Error records counter
|
|
127
|
+
lines.append(f"# HELP {prefix}_error_records_total Total number of records with errors")
|
|
128
|
+
lines.append(f"# TYPE {prefix}_error_records_total counter")
|
|
129
|
+
for (schema, version), data in schema_metrics.items():
|
|
130
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
131
|
+
lines.append(f"{prefix}_error_records_total{labels} {data['error_count']}")
|
|
132
|
+
|
|
133
|
+
lines.append("")
|
|
134
|
+
|
|
135
|
+
# Validity rate gauge (latest value)
|
|
136
|
+
lines.append(f"# HELP {prefix}_validity_rate Current validity rate")
|
|
137
|
+
lines.append(f"# TYPE {prefix}_validity_rate gauge")
|
|
138
|
+
for (schema, version), data in schema_metrics.items():
|
|
139
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
140
|
+
latest_rate = data["latest"].validity_rate
|
|
141
|
+
lines.append(f"{prefix}_validity_rate{labels} {latest_rate:.6f}")
|
|
142
|
+
|
|
143
|
+
lines.append("")
|
|
144
|
+
|
|
145
|
+
# Completeness gauge (latest value)
|
|
146
|
+
lines.append(f"# HELP {prefix}_completeness Current data completeness")
|
|
147
|
+
lines.append(f"# TYPE {prefix}_completeness gauge")
|
|
148
|
+
for (schema, version), data in schema_metrics.items():
|
|
149
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
150
|
+
latest_completeness = data["latest"].completeness
|
|
151
|
+
lines.append(f"{prefix}_completeness{labels} {latest_completeness:.6f}")
|
|
152
|
+
|
|
153
|
+
lines.append("")
|
|
154
|
+
|
|
155
|
+
# Validation duration (latest value)
|
|
156
|
+
lines.append(f"# HELP {prefix}_validation_duration_ms Validation duration in milliseconds")
|
|
157
|
+
lines.append(f"# TYPE {prefix}_validation_duration_ms gauge")
|
|
158
|
+
for (schema, version), data in schema_metrics.items():
|
|
159
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
160
|
+
latest_duration = data["latest"].duration_ms
|
|
161
|
+
lines.append(f"{prefix}_validation_duration_ms{labels} {latest_duration:.3f}")
|
|
162
|
+
|
|
163
|
+
lines.append("")
|
|
164
|
+
|
|
165
|
+
# Average validity rate
|
|
166
|
+
lines.append(f"# HELP {prefix}_validity_rate_avg Average validity rate")
|
|
167
|
+
lines.append(f"# TYPE {prefix}_validity_rate_avg gauge")
|
|
168
|
+
for (schema, version), data in schema_metrics.items():
|
|
169
|
+
labels = format_labels({"schema": schema, "version": version})
|
|
170
|
+
avg_rate = sum(data["validity_rates"]) / len(data["validity_rates"])
|
|
171
|
+
lines.append(f"{prefix}_validity_rate_avg{labels} {avg_rate:.6f}")
|
|
172
|
+
|
|
173
|
+
# Include summary metrics if provided
|
|
174
|
+
if summaries:
|
|
175
|
+
lines.append("")
|
|
176
|
+
lines.append(f"# HELP {prefix}_summary_total_validations Total validations in summary period")
|
|
177
|
+
lines.append(f"# TYPE {prefix}_summary_total_validations gauge")
|
|
178
|
+
for s in summaries:
|
|
179
|
+
labels = format_labels({"schema": s.schema_name})
|
|
180
|
+
lines.append(f"{prefix}_summary_total_validations{labels} {s.total_validations}")
|
|
181
|
+
|
|
182
|
+
lines.append("")
|
|
183
|
+
lines.append(f"# HELP {prefix}_summary_avg_validity_rate Average validity rate in summary period")
|
|
184
|
+
lines.append(f"# TYPE {prefix}_summary_avg_validity_rate gauge")
|
|
185
|
+
for s in summaries:
|
|
186
|
+
labels = format_labels({"schema": s.schema_name})
|
|
187
|
+
lines.append(f"{prefix}_summary_avg_validity_rate{labels} {s.avg_validity_rate:.6f}")
|
|
188
|
+
|
|
189
|
+
return "\n".join(lines) + "\n"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def export_csv(
|
|
193
|
+
metrics: List[ValidationMetric],
|
|
194
|
+
include_header: bool = True,
|
|
195
|
+
) -> str:
|
|
196
|
+
"""
|
|
197
|
+
Export metrics as CSV.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
metrics: List of ValidationMetrics to export
|
|
201
|
+
include_header: If True, include header row
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
CSV string representation of metrics
|
|
205
|
+
"""
|
|
206
|
+
columns = [
|
|
207
|
+
"id",
|
|
208
|
+
"schema_name",
|
|
209
|
+
"version",
|
|
210
|
+
"timestamp",
|
|
211
|
+
"record_count",
|
|
212
|
+
"valid_count",
|
|
213
|
+
"error_count",
|
|
214
|
+
"validity_rate",
|
|
215
|
+
"completeness",
|
|
216
|
+
"duration_ms",
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
lines = []
|
|
220
|
+
if include_header:
|
|
221
|
+
lines.append(",".join(columns))
|
|
222
|
+
|
|
223
|
+
for m in metrics:
|
|
224
|
+
row = [
|
|
225
|
+
m.id,
|
|
226
|
+
m.schema_name,
|
|
227
|
+
m.version,
|
|
228
|
+
m.timestamp.isoformat(),
|
|
229
|
+
str(m.record_count),
|
|
230
|
+
str(m.valid_count),
|
|
231
|
+
str(m.error_count),
|
|
232
|
+
f"{m.validity_rate:.6f}",
|
|
233
|
+
f"{m.completeness:.6f}",
|
|
234
|
+
f"{m.duration_ms:.3f}",
|
|
235
|
+
]
|
|
236
|
+
lines.append(",".join(row))
|
|
237
|
+
|
|
238
|
+
return "\n".join(lines) + "\n"
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quality Tracking Models - Data models for metrics tracking.
|
|
3
|
+
|
|
4
|
+
Defines the core data structures for validation metrics
|
|
5
|
+
and aggregated summaries.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import uuid
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ValidationMetric:
|
|
16
|
+
"""
|
|
17
|
+
Single validation run metric.
|
|
18
|
+
|
|
19
|
+
Captures all relevant metrics from a single validation operation
|
|
20
|
+
for time-series tracking and analysis.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
id: Unique identifier for this metric
|
|
24
|
+
schema_name: Name of the schema validated against
|
|
25
|
+
version: Version of the schema
|
|
26
|
+
timestamp: When the validation occurred
|
|
27
|
+
record_count: Number of records validated
|
|
28
|
+
valid_count: Number of valid records
|
|
29
|
+
error_count: Number of records with errors
|
|
30
|
+
validity_rate: Ratio of valid records (0.0 to 1.0)
|
|
31
|
+
completeness: Overall data completeness (0.0 to 1.0)
|
|
32
|
+
field_completeness: Per-field completeness ratios
|
|
33
|
+
duration_ms: Validation duration in milliseconds
|
|
34
|
+
errors_by_type: Count of errors by error type
|
|
35
|
+
metadata: Additional custom metadata
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
schema_name: str
|
|
39
|
+
version: str
|
|
40
|
+
timestamp: datetime = field(default_factory=datetime.utcnow)
|
|
41
|
+
record_count: int = 0
|
|
42
|
+
valid_count: int = 0
|
|
43
|
+
error_count: int = 0
|
|
44
|
+
validity_rate: float = 1.0
|
|
45
|
+
completeness: float = 1.0
|
|
46
|
+
field_completeness: Dict[str, float] = field(default_factory=dict)
|
|
47
|
+
duration_ms: float = 0.0
|
|
48
|
+
errors_by_type: Dict[str, int] = field(default_factory=dict)
|
|
49
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
50
|
+
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
53
|
+
"""Convert to dictionary."""
|
|
54
|
+
return {
|
|
55
|
+
"id": self.id,
|
|
56
|
+
"schema_name": self.schema_name,
|
|
57
|
+
"version": self.version,
|
|
58
|
+
"timestamp": self.timestamp.isoformat(),
|
|
59
|
+
"record_count": self.record_count,
|
|
60
|
+
"valid_count": self.valid_count,
|
|
61
|
+
"error_count": self.error_count,
|
|
62
|
+
"validity_rate": self.validity_rate,
|
|
63
|
+
"completeness": self.completeness,
|
|
64
|
+
"field_completeness": self.field_completeness,
|
|
65
|
+
"duration_ms": self.duration_ms,
|
|
66
|
+
"errors_by_type": self.errors_by_type,
|
|
67
|
+
"metadata": self.metadata,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ValidationMetric":
|
|
72
|
+
"""Create from dictionary."""
|
|
73
|
+
timestamp = data.get("timestamp")
|
|
74
|
+
if isinstance(timestamp, str):
|
|
75
|
+
timestamp = datetime.fromisoformat(timestamp)
|
|
76
|
+
elif timestamp is None:
|
|
77
|
+
timestamp = datetime.utcnow()
|
|
78
|
+
|
|
79
|
+
return cls(
|
|
80
|
+
id=data.get("id", str(uuid.uuid4())),
|
|
81
|
+
schema_name=data["schema_name"],
|
|
82
|
+
version=data["version"],
|
|
83
|
+
timestamp=timestamp,
|
|
84
|
+
record_count=data.get("record_count", 0),
|
|
85
|
+
valid_count=data.get("valid_count", 0),
|
|
86
|
+
error_count=data.get("error_count", 0),
|
|
87
|
+
validity_rate=data.get("validity_rate", 1.0),
|
|
88
|
+
completeness=data.get("completeness", 1.0),
|
|
89
|
+
field_completeness=data.get("field_completeness", {}),
|
|
90
|
+
duration_ms=data.get("duration_ms", 0.0),
|
|
91
|
+
errors_by_type=data.get("errors_by_type", {}),
|
|
92
|
+
metadata=data.get("metadata", {}),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class MetricsSummary:
|
|
98
|
+
"""
|
|
99
|
+
Aggregated metrics summary for a schema over a time period.
|
|
100
|
+
|
|
101
|
+
Provides statistical aggregations of validation metrics
|
|
102
|
+
for trend analysis and monitoring.
|
|
103
|
+
|
|
104
|
+
Attributes:
|
|
105
|
+
schema_name: Name of the schema
|
|
106
|
+
period_start: Start of the summary period
|
|
107
|
+
period_end: End of the summary period
|
|
108
|
+
total_validations: Total number of validation runs
|
|
109
|
+
total_records: Total number of records validated
|
|
110
|
+
total_valid: Total number of valid records
|
|
111
|
+
total_errors: Total number of errors
|
|
112
|
+
avg_validity_rate: Average validity rate
|
|
113
|
+
min_validity_rate: Minimum validity rate
|
|
114
|
+
max_validity_rate: Maximum validity rate
|
|
115
|
+
avg_completeness: Average completeness
|
|
116
|
+
avg_duration_ms: Average validation duration
|
|
117
|
+
top_error_types: Most common error types with counts
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
schema_name: str
|
|
121
|
+
period_start: datetime
|
|
122
|
+
period_end: datetime
|
|
123
|
+
total_validations: int = 0
|
|
124
|
+
total_records: int = 0
|
|
125
|
+
total_valid: int = 0
|
|
126
|
+
total_errors: int = 0
|
|
127
|
+
avg_validity_rate: float = 0.0
|
|
128
|
+
min_validity_rate: float = 1.0
|
|
129
|
+
max_validity_rate: float = 0.0
|
|
130
|
+
avg_completeness: float = 0.0
|
|
131
|
+
avg_duration_ms: float = 0.0
|
|
132
|
+
top_error_types: Dict[str, int] = field(default_factory=dict)
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def overall_validity_rate(self) -> float:
|
|
136
|
+
"""Calculate overall validity rate from totals."""
|
|
137
|
+
if self.total_records == 0:
|
|
138
|
+
return 1.0
|
|
139
|
+
return self.total_valid / self.total_records
|
|
140
|
+
|
|
141
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
142
|
+
"""Convert to dictionary."""
|
|
143
|
+
return {
|
|
144
|
+
"schema_name": self.schema_name,
|
|
145
|
+
"period_start": self.period_start.isoformat(),
|
|
146
|
+
"period_end": self.period_end.isoformat(),
|
|
147
|
+
"total_validations": self.total_validations,
|
|
148
|
+
"total_records": self.total_records,
|
|
149
|
+
"total_valid": self.total_valid,
|
|
150
|
+
"total_errors": self.total_errors,
|
|
151
|
+
"avg_validity_rate": self.avg_validity_rate,
|
|
152
|
+
"min_validity_rate": self.min_validity_rate,
|
|
153
|
+
"max_validity_rate": self.max_validity_rate,
|
|
154
|
+
"avg_completeness": self.avg_completeness,
|
|
155
|
+
"avg_duration_ms": self.avg_duration_ms,
|
|
156
|
+
"overall_validity_rate": self.overall_validity_rate,
|
|
157
|
+
"top_error_types": self.top_error_types,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass
|
|
162
|
+
class MetricsFilter:
|
|
163
|
+
"""
|
|
164
|
+
Filter criteria for querying metrics.
|
|
165
|
+
|
|
166
|
+
Attributes:
|
|
167
|
+
schema_name: Filter by schema name
|
|
168
|
+
version: Filter by schema version
|
|
169
|
+
since: Filter metrics after this time
|
|
170
|
+
until: Filter metrics before this time
|
|
171
|
+
min_validity_rate: Filter by minimum validity rate
|
|
172
|
+
limit: Maximum number of results
|
|
173
|
+
offset: Offset for pagination
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
schema_name: Optional[str] = None
|
|
177
|
+
version: Optional[str] = None
|
|
178
|
+
since: Optional[datetime] = None
|
|
179
|
+
until: Optional[datetime] = None
|
|
180
|
+
min_validity_rate: Optional[float] = None
|
|
181
|
+
limit: int = 100
|
|
182
|
+
offset: int = 0
|
|
183
|
+
|
|
184
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
185
|
+
"""Convert to dictionary (for serialization)."""
|
|
186
|
+
return {
|
|
187
|
+
"schema_name": self.schema_name,
|
|
188
|
+
"version": self.version,
|
|
189
|
+
"since": self.since.isoformat() if self.since else None,
|
|
190
|
+
"until": self.until.isoformat() if self.until else None,
|
|
191
|
+
"min_validity_rate": self.min_validity_rate,
|
|
192
|
+
"limit": self.limit,
|
|
193
|
+
"offset": self.offset,
|
|
194
|
+
}
|