tabalyst 0.6.0__tar.gz → 0.6.2__tar.gz
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.
- {tabalyst-0.6.0 → tabalyst-0.6.2}/MANIFEST.in +1 -0
- {tabalyst-0.6.0/src/tabalyst.egg-info → tabalyst-0.6.2}/PKG-INFO +69 -7
- {tabalyst-0.6.0 → tabalyst-0.6.2}/README.md +67 -6
- tabalyst-0.6.2/examples/README.md +144 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/pyproject.toml +3 -2
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/__init__.py +8 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/batch.py +11 -3
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/app.py +2 -0
- tabalyst-0.6.2/src/tabalyst/cli/generate.py +67 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/inspect.py +6 -9
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/report.py +26 -5
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/scan.py +8 -2
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/terminal.py +18 -14
- tabalyst-0.6.2/src/tabalyst/generate_anomalies.py +126 -0
- tabalyst-0.6.2/src/tabalyst/generate_csv.py +34 -0
- tabalyst-0.6.2/src/tabalyst/generate_definition.py +596 -0
- tabalyst-0.6.2/src/tabalyst/generate_examples/crm.yaml +70 -0
- tabalyst-0.6.2/src/tabalyst/generate_examples/insurance.yaml +71 -0
- tabalyst-0.6.2/src/tabalyst/generate_insurance.py +208 -0
- tabalyst-0.6.2/src/tabalyst/generate_references.py +221 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/SOURCES.md +27 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/addresses/street_directions_ca.csv +9 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/addresses/street_names_ca.csv +109 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/addresses/street_types_ca.csv +32 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/addresses/unit_types_ca.csv +12 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/contact/email_patterns.csv +12 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/contact/email_providers_ca.csv +10 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/contact/phone_area_codes_ca.csv +14 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/geography/cities_ca.csv +72 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/geography/postal_prefixes_ca.csv +72 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/geography/provinces_ca.csv +14 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/SOURCES.md +23 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/agencies.csv +21 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/branch_contract_weights.csv +13 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/claim_statuses.csv +5 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/contract_statuses.csv +5 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/contract_types.csv +5 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/payment_frequencies.csv +5 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/insurance/payment_methods.csv +5 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/people/family_names_ca.csv +136 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/people/first_names_en_ca.csv +81 -0
- tabalyst-0.6.2/src/tabalyst/generate_resources/people/first_names_fr_qc.csv +101 -0
- tabalyst-0.6.2/src/tabalyst/generate_service.py +367 -0
- tabalyst-0.6.2/src/tabalyst/generate_values.py +199 -0
- tabalyst-0.6.2/src/tabalyst/inspector/choices.py +114 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/models.py +2 -2
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/resolution.py +39 -4
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/models.py +2 -2
- tabalyst-0.6.2/src/tabalyst/option_notices.py +54 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/report_service.py +185 -4
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scan_service.py +17 -1
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/models.py +3 -3
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/static/theme.css +1 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/templates/column.html +2 -2
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/templates/report.html +2 -2
- {tabalyst-0.6.0 → tabalyst-0.6.2/src/tabalyst.egg-info}/PKG-INFO +69 -7
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst.egg-info/SOURCES.txt +41 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst.egg-info/requires.txt +1 -0
- tabalyst-0.6.2/tests/test_all_collections.py +312 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_cli.py +2 -2
- tabalyst-0.6.2/tests/test_generate.py +368 -0
- tabalyst-0.6.2/tests/test_generate_crm.py +51 -0
- tabalyst-0.6.2/tests/test_generate_insurance.py +73 -0
- tabalyst-0.6.2/tests/test_generate_lot4.py +122 -0
- tabalyst-0.6.2/tests/test_generate_references.py +116 -0
- tabalyst-0.6.2/tests/test_option_notices.py +89 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_index.py +5 -3
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_reporting.py +4 -3
- tabalyst-0.6.0/examples/README.md +0 -75
- {tabalyst-0.6.0 → tabalyst-0.6.2}/LICENSE +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/examples/config.json +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/examples/input/basic.csv +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/examples/input/insurance-customers.csv +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/setup.cfg +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/__main__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/_version.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cache_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/cache.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/cli/sample.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/config.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/errors.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/execution_log.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspect_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/excel_inspect/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/excel_inspect/build.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/excel_inspect/detect.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/excel_inspect/parameters.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/json_inspect/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/json_inspect/build.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/json_inspect/detect.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/json_inspect/parameters.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/inspector/persistence.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/progress.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/project_open_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/project_scan_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_bounded_values.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_cache.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_codec.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_generation.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_locks.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_publication.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_queries.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_query_budget.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_recovery.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_retention.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_schema.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_session.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_staging.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_sync.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/_validation.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/catalog.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/freshness.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/identity.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/index.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/location.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/models.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/projects/store.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/report_config.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/report_profile.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/reporting.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/sampling.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/sampling_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scan_reuse.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/api.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/config.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/amount.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/base.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/boolean.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/currency.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/email.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/enumeration.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/ip.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/names.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/number.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/pattern.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/percentage.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/phone.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/postal.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/quantity.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/registry.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/shape.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/temporal.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/url.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/detectors/uuid.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/diagnostics.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/engine.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/exposure.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/field.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/identity.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/measures.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/normalization.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/observations.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/paths.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/__init__.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/base.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/csv_reader.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/excel_common.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/excel_reader.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/excel_table.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/json_common.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/json_reader.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/readers/jsonl_reader.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/records.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/structure.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/technical.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/values.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/scanner/workers.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/shared_scan_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst/static/report.js +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst.egg-info/dependency_links.txt +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst.egg-info/entry_points.txt +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/src/tabalyst.egg-info/top_level.txt +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_license_headers.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_cache.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_database.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_generations.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_identity.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_location.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_open_service.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_queries.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_rescan.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_scan.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_sessions.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_project_store.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_public_api.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_sampling.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scan_command.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_amounts.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_batches.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_config.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_csv.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_email_url.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_exposure.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_paths.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_phone.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_postal.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_scanner_uuid_ip.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_shared_scan_cli.py +0 -0
- {tabalyst-0.6.0 → tabalyst-0.6.2}/tests/test_source_freshness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabalyst
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Open-source toolkit for understanding and working with unfamiliar data.
|
|
5
5
|
Author: Gregory Borelli
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -27,6 +27,7 @@ Requires-Dist: jinja2>=3.1
|
|
|
27
27
|
Requires-Dist: platformdirs>=4
|
|
28
28
|
Requires-Dist: pydantic>=2.11
|
|
29
29
|
Requires-Dist: python-calamine>=0.8
|
|
30
|
+
Requires-Dist: PyYAML>=6.0
|
|
30
31
|
Requires-Dist: typer>=0.12
|
|
31
32
|
Provides-Extra: dev
|
|
32
33
|
Requires-Dist: build>=1.2; extra == "dev"
|
|
@@ -48,7 +49,8 @@ Its first tool is **Tabalyst Report**. The current CSV implementation,
|
|
|
48
49
|
profile and a self-contained interactive HTML report.
|
|
49
50
|
**Tabalyst Scan** describes CSV, JSON, JSONL and Excel files in a scan document,
|
|
50
51
|
**Tabalyst Inspect** finds how to read a JSON, JSONL or Excel file, and
|
|
51
|
-
**Tabalyst Sample** creates smaller CSV files
|
|
52
|
+
**Tabalyst Sample** creates smaller CSV files by sampling, and **Tabalyst
|
|
53
|
+
Generate** creates synthetic CSV datasets.
|
|
52
54
|
|
|
53
55
|
Tabalyst is in beta. Its interfaces may still change while
|
|
54
56
|
the shared toolkit architecture is being established.
|
|
@@ -75,6 +77,7 @@ changes often: update it before each new test.
|
|
|
75
77
|
| One JSON file | `tabalyst report data.json` | `data.report.html` beside the source |
|
|
76
78
|
| One JSONL file | `tabalyst report events.jsonl` | `events.report.html` beside the source |
|
|
77
79
|
| One Excel workbook | `tabalyst report sales.xlsx` | `sales.report.html` beside the source |
|
|
80
|
+
| Every table of a workbook | `tabalyst report shop.xlsx --all-collections` | `shop.costs.html`, `shop.sales.html`, ... beside the source |
|
|
78
81
|
| From a scan document | `tabalyst report --scan data.scan.json` | `data.html` beside the scan |
|
|
79
82
|
|
|
80
83
|
The simplest command keeps the source filename:
|
|
@@ -143,9 +146,17 @@ tabalyst report sales.xlsx
|
|
|
143
146
|
|
|
144
147
|
Cells keep their Excel type, and dates are read as dates. A workbook with one
|
|
145
148
|
table needs nothing else. When several sheets or tables are equally plausible,
|
|
146
|
-
Tabalyst stops and
|
|
147
|
-
`
|
|
148
|
-
|
|
149
|
+
Tabalyst stops and prints one command per table to copy and run, such as
|
|
150
|
+
`tabalyst report shop.xlsx --collection Costs` (or `Sales.Orders` for a named
|
|
151
|
+
table), or see [Tabalyst Inspect](#tabalyst-inspect). To report every table at
|
|
152
|
+
once, use `--all-collections`:
|
|
153
|
+
|
|
154
|
+
```console
|
|
155
|
+
tabalyst report shop.xlsx --all-collections -d reports
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This writes `shop.costs.html` and `shop.costs.json` for each table, named after
|
|
159
|
+
the file and the sheet. Older `.xls` files are not read: save them as `.xlsx`.
|
|
149
160
|
|
|
150
161
|
### Multiple files
|
|
151
162
|
|
|
@@ -264,6 +275,33 @@ Sampling reads CSV records as a stream. Random and stratified sampling keep
|
|
|
264
275
|
only the requested sample, plus stratum counts, in memory. Existing outputs
|
|
265
276
|
require `--force`, and an input file is never overwritten.
|
|
266
277
|
|
|
278
|
+
## Tabalyst Generate
|
|
279
|
+
|
|
280
|
+
Generate synthetic CSV data from a versioned YAML definition. The packaged
|
|
281
|
+
`crm` example writes one contacts file; `insurance`
|
|
282
|
+
writes three branch files and a combined file:
|
|
283
|
+
|
|
284
|
+
```console
|
|
285
|
+
tabalyst generate --example crm -o crm-contacts.csv
|
|
286
|
+
tabalyst generate --example insurance --rows 3000 -d insurance-output/
|
|
287
|
+
tabalyst generate --example insurance --rows 3000 --anomaly-profile dirty_realistic -d insurance-dirty/
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
You can pass `examples/generate/input/crm.yaml` or
|
|
291
|
+
`examples/generate/input/insurance.yaml` instead of `--example`. A selected anomaly
|
|
292
|
+
profile writes an altered CSV, a cell change log and a summary beside the
|
|
293
|
+
untouched clean CSV. Insurance's profile changes only the combined file.
|
|
294
|
+
`--rows 3000` apportions 2,182, 273 and 545 rows to the three branches.
|
|
295
|
+
Existing outputs require `--force`.
|
|
296
|
+
|
|
297
|
+
Run `python examples/generate/generate_examples.py` to generate every YAML
|
|
298
|
+
from `examples/generate/input/` into ignored local output folders.
|
|
299
|
+
|
|
300
|
+
Insurance preserves the 33 source columns and the combined `filiale` column of
|
|
301
|
+
the existing public demo, but its generated rows are different. The branch date
|
|
302
|
+
and identifier formats are preserved; `filiale` is the last column in the new
|
|
303
|
+
combined CSV. The current public demo and report remain unchanged.
|
|
304
|
+
|
|
267
305
|
## Tabalyst Inspect
|
|
268
306
|
|
|
269
307
|
A JSON file can hold several arrays, and an Excel workbook several sheets.
|
|
@@ -417,7 +455,9 @@ scan = tabalyst.scan("orders.json")
|
|
|
417
455
|
scans = tabalyst.generate_scans(["data/*.json"], output_dir="scans")
|
|
418
456
|
|
|
419
457
|
inspection = tabalyst.inspect("orders.json")
|
|
420
|
-
inspections = tabalyst.generate_inspections(
|
|
458
|
+
inspections = tabalyst.generate_inspections(
|
|
459
|
+
["data/*.json", "logs/*.jsonl", "*.xlsx"]
|
|
460
|
+
)
|
|
421
461
|
reports = tabalyst.generate_reports(["scans/*.scan.json"], from_scan=True)
|
|
422
462
|
```
|
|
423
463
|
|
|
@@ -503,8 +543,30 @@ Additional documentation:
|
|
|
503
543
|
Please report defects and feature requests through the
|
|
504
544
|
[GitHub issue tracker](https://github.com/loribel-labs/tabalyst/issues).
|
|
505
545
|
|
|
546
|
+
## For AI assistants
|
|
547
|
+
|
|
548
|
+
To get help from an AI assistant, paste this prompt into a chat that can read
|
|
549
|
+
web pages:
|
|
550
|
+
|
|
551
|
+
```text
|
|
552
|
+
Read the complete Tabalyst documentation at
|
|
553
|
+
https://docs.tabalyst.com/llms-full.txt, then help me use Tabalyst well.
|
|
554
|
+
Ask me what my data looks like and what I want to learn from it, then suggest
|
|
555
|
+
the right commands and options. Base your answers only on that documentation.
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
If the assistant cannot open links, paste the content of the file instead.
|
|
559
|
+
|
|
560
|
+
The documentation site publishes plain-text versions of its pages:
|
|
561
|
+
[llms.txt](https://docs.tabalyst.com/llms.txt) lists every page with a
|
|
562
|
+
one-line description, and
|
|
563
|
+
[llms-full.txt](https://docs.tabalyst.com/llms-full.txt) holds the whole
|
|
564
|
+
documentation in one file. Every page is also available as Markdown by adding
|
|
565
|
+
`.md` to its URL, for example
|
|
566
|
+
[report.md](https://docs.tabalyst.com/report.md).
|
|
567
|
+
|
|
506
568
|
## License
|
|
507
569
|
|
|
508
570
|
Tabalyst is released under the [Mozilla Public License 2.0](LICENSE).
|
|
509
571
|
|
|
510
|
-
Created by Gregory Borelli
|
|
572
|
+
Created by Gregory Borelli. Open source project by Loribel Labs.
|
|
@@ -10,7 +10,8 @@ Its first tool is **Tabalyst Report**. The current CSV implementation,
|
|
|
10
10
|
profile and a self-contained interactive HTML report.
|
|
11
11
|
**Tabalyst Scan** describes CSV, JSON, JSONL and Excel files in a scan document,
|
|
12
12
|
**Tabalyst Inspect** finds how to read a JSON, JSONL or Excel file, and
|
|
13
|
-
**Tabalyst Sample** creates smaller CSV files
|
|
13
|
+
**Tabalyst Sample** creates smaller CSV files by sampling, and **Tabalyst
|
|
14
|
+
Generate** creates synthetic CSV datasets.
|
|
14
15
|
|
|
15
16
|
Tabalyst is in beta. Its interfaces may still change while
|
|
16
17
|
the shared toolkit architecture is being established.
|
|
@@ -37,6 +38,7 @@ changes often: update it before each new test.
|
|
|
37
38
|
| One JSON file | `tabalyst report data.json` | `data.report.html` beside the source |
|
|
38
39
|
| One JSONL file | `tabalyst report events.jsonl` | `events.report.html` beside the source |
|
|
39
40
|
| One Excel workbook | `tabalyst report sales.xlsx` | `sales.report.html` beside the source |
|
|
41
|
+
| Every table of a workbook | `tabalyst report shop.xlsx --all-collections` | `shop.costs.html`, `shop.sales.html`, ... beside the source |
|
|
40
42
|
| From a scan document | `tabalyst report --scan data.scan.json` | `data.html` beside the scan |
|
|
41
43
|
|
|
42
44
|
The simplest command keeps the source filename:
|
|
@@ -105,9 +107,17 @@ tabalyst report sales.xlsx
|
|
|
105
107
|
|
|
106
108
|
Cells keep their Excel type, and dates are read as dates. A workbook with one
|
|
107
109
|
table needs nothing else. When several sheets or tables are equally plausible,
|
|
108
|
-
Tabalyst stops and
|
|
109
|
-
`
|
|
110
|
-
|
|
110
|
+
Tabalyst stops and prints one command per table to copy and run, such as
|
|
111
|
+
`tabalyst report shop.xlsx --collection Costs` (or `Sales.Orders` for a named
|
|
112
|
+
table), or see [Tabalyst Inspect](#tabalyst-inspect). To report every table at
|
|
113
|
+
once, use `--all-collections`:
|
|
114
|
+
|
|
115
|
+
```console
|
|
116
|
+
tabalyst report shop.xlsx --all-collections -d reports
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This writes `shop.costs.html` and `shop.costs.json` for each table, named after
|
|
120
|
+
the file and the sheet. Older `.xls` files are not read: save them as `.xlsx`.
|
|
111
121
|
|
|
112
122
|
### Multiple files
|
|
113
123
|
|
|
@@ -226,6 +236,33 @@ Sampling reads CSV records as a stream. Random and stratified sampling keep
|
|
|
226
236
|
only the requested sample, plus stratum counts, in memory. Existing outputs
|
|
227
237
|
require `--force`, and an input file is never overwritten.
|
|
228
238
|
|
|
239
|
+
## Tabalyst Generate
|
|
240
|
+
|
|
241
|
+
Generate synthetic CSV data from a versioned YAML definition. The packaged
|
|
242
|
+
`crm` example writes one contacts file; `insurance`
|
|
243
|
+
writes three branch files and a combined file:
|
|
244
|
+
|
|
245
|
+
```console
|
|
246
|
+
tabalyst generate --example crm -o crm-contacts.csv
|
|
247
|
+
tabalyst generate --example insurance --rows 3000 -d insurance-output/
|
|
248
|
+
tabalyst generate --example insurance --rows 3000 --anomaly-profile dirty_realistic -d insurance-dirty/
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
You can pass `examples/generate/input/crm.yaml` or
|
|
252
|
+
`examples/generate/input/insurance.yaml` instead of `--example`. A selected anomaly
|
|
253
|
+
profile writes an altered CSV, a cell change log and a summary beside the
|
|
254
|
+
untouched clean CSV. Insurance's profile changes only the combined file.
|
|
255
|
+
`--rows 3000` apportions 2,182, 273 and 545 rows to the three branches.
|
|
256
|
+
Existing outputs require `--force`.
|
|
257
|
+
|
|
258
|
+
Run `python examples/generate/generate_examples.py` to generate every YAML
|
|
259
|
+
from `examples/generate/input/` into ignored local output folders.
|
|
260
|
+
|
|
261
|
+
Insurance preserves the 33 source columns and the combined `filiale` column of
|
|
262
|
+
the existing public demo, but its generated rows are different. The branch date
|
|
263
|
+
and identifier formats are preserved; `filiale` is the last column in the new
|
|
264
|
+
combined CSV. The current public demo and report remain unchanged.
|
|
265
|
+
|
|
229
266
|
## Tabalyst Inspect
|
|
230
267
|
|
|
231
268
|
A JSON file can hold several arrays, and an Excel workbook several sheets.
|
|
@@ -379,7 +416,9 @@ scan = tabalyst.scan("orders.json")
|
|
|
379
416
|
scans = tabalyst.generate_scans(["data/*.json"], output_dir="scans")
|
|
380
417
|
|
|
381
418
|
inspection = tabalyst.inspect("orders.json")
|
|
382
|
-
inspections = tabalyst.generate_inspections(
|
|
419
|
+
inspections = tabalyst.generate_inspections(
|
|
420
|
+
["data/*.json", "logs/*.jsonl", "*.xlsx"]
|
|
421
|
+
)
|
|
383
422
|
reports = tabalyst.generate_reports(["scans/*.scan.json"], from_scan=True)
|
|
384
423
|
```
|
|
385
424
|
|
|
@@ -465,8 +504,30 @@ Additional documentation:
|
|
|
465
504
|
Please report defects and feature requests through the
|
|
466
505
|
[GitHub issue tracker](https://github.com/loribel-labs/tabalyst/issues).
|
|
467
506
|
|
|
507
|
+
## For AI assistants
|
|
508
|
+
|
|
509
|
+
To get help from an AI assistant, paste this prompt into a chat that can read
|
|
510
|
+
web pages:
|
|
511
|
+
|
|
512
|
+
```text
|
|
513
|
+
Read the complete Tabalyst documentation at
|
|
514
|
+
https://docs.tabalyst.com/llms-full.txt, then help me use Tabalyst well.
|
|
515
|
+
Ask me what my data looks like and what I want to learn from it, then suggest
|
|
516
|
+
the right commands and options. Base your answers only on that documentation.
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
If the assistant cannot open links, paste the content of the file instead.
|
|
520
|
+
|
|
521
|
+
The documentation site publishes plain-text versions of its pages:
|
|
522
|
+
[llms.txt](https://docs.tabalyst.com/llms.txt) lists every page with a
|
|
523
|
+
one-line description, and
|
|
524
|
+
[llms-full.txt](https://docs.tabalyst.com/llms-full.txt) holds the whole
|
|
525
|
+
documentation in one file. Every page is also available as Markdown by adding
|
|
526
|
+
`.md` to its URL, for example
|
|
527
|
+
[report.md](https://docs.tabalyst.com/report.md).
|
|
528
|
+
|
|
468
529
|
## License
|
|
469
530
|
|
|
470
531
|
Tabalyst is released under the [Mozilla Public License 2.0](LICENSE).
|
|
471
532
|
|
|
472
|
-
Created by Gregory Borelli
|
|
533
|
+
Created by Gregory Borelli. Open source project by Loribel Labs.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Tabalyst examples
|
|
2
|
+
|
|
3
|
+
The examples are organized by input name so additional public datasets can be
|
|
4
|
+
added without mixing source data and generated artifacts.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
examples/
|
|
8
|
+
|-- config.json
|
|
9
|
+
|-- generate/
|
|
10
|
+
| |-- generate_examples.py
|
|
11
|
+
| |-- input/ (four YAML definitions)
|
|
12
|
+
| `-- output/ (generated locally; ignored by Git)
|
|
13
|
+
|-- input/
|
|
14
|
+
| |-- basic.csv
|
|
15
|
+
| |-- insurance-customers.csv
|
|
16
|
+
| |-- orders.json
|
|
17
|
+
| |-- sales.xlsx
|
|
18
|
+
| `-- web-events.jsonl
|
|
19
|
+
`-- output/
|
|
20
|
+
|-- basic/
|
|
21
|
+
| |-- report.html
|
|
22
|
+
| |-- report.json
|
|
23
|
+
| `-- executions.json
|
|
24
|
+
|-- insurance-customers/
|
|
25
|
+
| |-- report.html
|
|
26
|
+
| |-- report.json
|
|
27
|
+
| |-- report/ (34 standalone column pages with --details)
|
|
28
|
+
| `-- executions.json
|
|
29
|
+
|-- orders/
|
|
30
|
+
| |-- report.html
|
|
31
|
+
| |-- report.json
|
|
32
|
+
| `-- executions.json
|
|
33
|
+
|-- sales/
|
|
34
|
+
| |-- report.html
|
|
35
|
+
| |-- report.json
|
|
36
|
+
| `-- executions.json
|
|
37
|
+
`-- web-events/
|
|
38
|
+
|-- report.html
|
|
39
|
+
|-- report.json
|
|
40
|
+
`-- executions.json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`basic.csv` is a five-row smoke example. `insurance-customers.csv` contains 3,000
|
|
44
|
+
synthetic customer and contract records across 34 columns. Its people, contact
|
|
45
|
+
details, and contracts are fictional; email addresses use reserved `.example`
|
|
46
|
+
domains. `orders.json` is a synthetic shop export: 60 customers with nested
|
|
47
|
+
addresses, tags and orders. Its report has one dataset, `$.customers[]`, the only
|
|
48
|
+
collection of arrays of objects that Tabalyst Inspect finds in it.
|
|
49
|
+
`web-events.jsonl` is a synthetic web analytics log in JSON Lines: 300 events with
|
|
50
|
+
nested `user` and `device` objects, tags arrays, null values and an optional
|
|
51
|
+
field that appears late, plus three bad lines (a truncated record, a line that
|
|
52
|
+
is not JSON and a JSON array instead of an object). Its report is partial on
|
|
53
|
+
purpose: the three lines are excluded and counted, the 300 events are analyzed.
|
|
54
|
+
Its people and identifiers are fictional. `sales.xlsx` is a synthetic Excel
|
|
55
|
+
workbook: a sheet `Orders` of 120 fictional orders under two title lines, a
|
|
56
|
+
five-row `Regions` lookup sheet and a `Notes` sheet. Inspect selects `$.Orders`,
|
|
57
|
+
which has more than ten times the rows of `$.Regions`, so the report needs no
|
|
58
|
+
choice. The workbook is written by `make_sales_workbook.py`, with the same
|
|
59
|
+
bytes on every run; it needs `pip install xlsxwriter` and is only to run when the
|
|
60
|
+
input itself must be regenerated.
|
|
61
|
+
|
|
62
|
+
From the repository root, regenerate the outputs with:
|
|
63
|
+
|
|
64
|
+
```console
|
|
65
|
+
tabalyst report examples/input/basic.csv -o examples/output/basic/report.html --config examples/config.json --force
|
|
66
|
+
tabalyst report examples/input/insurance-customers.csv -o examples/output/insurance-customers/report.html --config examples/config.json --details --force
|
|
67
|
+
tabalyst report examples/input/orders.json -o examples/output/orders/report.html --config examples/config.json --force
|
|
68
|
+
tabalyst report examples/input/web-events.jsonl -o examples/output/web-events/report.html --config examples/config.json --force
|
|
69
|
+
tabalyst report examples/input/sales.xlsx -o examples/output/sales/report.html --config examples/config.json --force
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Every output folder is independent and contains its own HTML report, canonical
|
|
73
|
+
JSON profile and cumulative `executions.json` history. The insurance example
|
|
74
|
+
also has standalone HTML pages under `report/`. Delete an output folder
|
|
75
|
+
before regenerating when a fresh one-entry execution history is required.
|
|
76
|
+
|
|
77
|
+
The input datasets and generated output are public repository examples. The wheel
|
|
78
|
+
contains the runtime package, report resources, and packaged copies of the
|
|
79
|
+
`examples/generate/input/` YAML definitions. CRM and Insurance are local development
|
|
80
|
+
demos. Generate is under development
|
|
81
|
+
and is not advertised as a released feature. The CRM data is synthetic, with
|
|
82
|
+
reserved `.example` domains and fictional phone numbers. The configured 20,000
|
|
83
|
+
rows, generic account names and CRM audit story were approved for the example
|
|
84
|
+
on 2026-10-05. Generate remains local development work until a release.
|
|
85
|
+
|
|
86
|
+
To review the CRM export outside the checkout:
|
|
87
|
+
|
|
88
|
+
```console
|
|
89
|
+
tabalyst generate --example crm --rows 500 --anomaly-profile dirty_realistic -d OUTPUT_DIR
|
|
90
|
+
tabalyst scan OUTPUT_DIR/crm-contacts.dirty_realistic.csv -o OUTPUT_DIR/scan.json
|
|
91
|
+
tabalyst report OUTPUT_DIR/scan.json --scan -o OUTPUT_DIR/report.html
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Scan and Report expose missing values, fragmented categories and normalized
|
|
95
|
+
variants. The mutation summary counts requested, applied and skipped changes.
|
|
96
|
+
Neither tool currently diagnoses cross-column CRM rules such as stage versus
|
|
97
|
+
probability.
|
|
98
|
+
|
|
99
|
+
`examples/generate/input/common-references.yaml` is a lot 3R development fixture.
|
|
100
|
+
It generates coherent synthetic Canadian person, address and contact fields
|
|
101
|
+
from packaged references. Its prefixes and area codes are synthetic, email
|
|
102
|
+
domains end in `.example`, and phone line numbers use reserved `555-01xx`.
|
|
103
|
+
The Insurance example is now complete in `examples/generate/input/insurance.yaml`
|
|
104
|
+
and the packaged `--example insurance` copy. Its seven domain tables are in
|
|
105
|
+
`src/tabalyst/generate_resources/insurance/`; their provenance is in
|
|
106
|
+
`insurance/SOURCES.md`. To review 3,000 rows outside the checkout:
|
|
107
|
+
|
|
108
|
+
```console
|
|
109
|
+
tabalyst generate --example insurance --rows 3000 --anomaly-profile dirty_realistic -d OUTPUT_DIR
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This creates clean files for Principal, Ontario and Quebec, their clean union,
|
|
113
|
+
and three anomaly artifacts for that union. The 2,182/273/545 branch allocation
|
|
114
|
+
comes from the declared 200,000/25,000/50,000 weights. The new clean CSV has
|
|
115
|
+
the same 34 column names as the existing public demo, but places `filiale` last
|
|
116
|
+
and has different rows and identifiers. Do not replace
|
|
117
|
+
`examples/input/insurance-customers.csv` or its report without a separate
|
|
118
|
+
decision to change the published demo.
|
|
119
|
+
|
|
120
|
+
`examples/generate/input/cohorts-anomalies.yaml` is a lot 4 development fixture. It
|
|
121
|
+
shows two normal status conventions in the clean CSV and a separate `dirty`
|
|
122
|
+
profile that writes an altered CSV, cell-level change log and summary. Run it
|
|
123
|
+
with `tabalyst generate examples/generate/input/cohorts-anomalies.yaml --anomaly-profile dirty -d OUTPUT_DIR`.
|
|
124
|
+
Keep `OUTPUT_DIR` outside the repository for one-off local trials. The packaged
|
|
125
|
+
Insurance YAML is the same complete example as the visible file.
|
|
126
|
+
|
|
127
|
+
## Regenerate all Generate examples
|
|
128
|
+
|
|
129
|
+
Run the script from the repository root with an editable installation of
|
|
130
|
+
Tabalyst. It reads every YAML in `examples/generate/input/` and writes into
|
|
131
|
+
`examples/generate/output/<definition>/`. The output folder is ignored by Git.
|
|
132
|
+
By default it uses each YAML's declared row counts and generates every declared
|
|
133
|
+
anomaly profile along with its clean CSV. Insurance therefore writes 275,000
|
|
134
|
+
combined rows; allow enough disk space for the clean and altered copies.
|
|
135
|
+
|
|
136
|
+
```console
|
|
137
|
+
python examples/generate/generate_examples.py
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Existing outputs are protected; pass `--force` to replace generated files.
|
|
141
|
+
For a quick review run, `--rows 30` overrides each definition's row count,
|
|
142
|
+
and `--output-dir PATH` writes outside the checkout. `--clean-only` skips the
|
|
143
|
+
profile passes. A definition with several profiles gets one subfolder per
|
|
144
|
+
profile so its clean files cannot collide.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tabalyst"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.2"
|
|
8
8
|
description = "Open-source toolkit for understanding and working with unfamiliar data."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -40,6 +40,7 @@ dependencies = [
|
|
|
40
40
|
"platformdirs>=4",
|
|
41
41
|
"pydantic>=2.11",
|
|
42
42
|
"python-calamine>=0.8",
|
|
43
|
+
"PyYAML>=6.0",
|
|
43
44
|
"typer>=0.12",
|
|
44
45
|
]
|
|
45
46
|
|
|
@@ -59,7 +60,7 @@ tabalyst = "tabalyst.cli:main"
|
|
|
59
60
|
where = ["src"]
|
|
60
61
|
|
|
61
62
|
[tool.setuptools.package-data]
|
|
62
|
-
tabalyst = ["templates/*.html", "static/*.css", "static/*.js"]
|
|
63
|
+
tabalyst = ["templates/*.html", "static/*.css", "static/*.js", "generate_examples/*.yaml", "generate_resources/**/*.csv", "generate_resources/*.md"]
|
|
63
64
|
|
|
64
65
|
[project.urls]
|
|
65
66
|
Homepage = "https://github.com/loribel-labs/tabalyst"
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
from tabalyst._version import __version__
|
|
8
8
|
from tabalyst.errors import ConfigurationError, InputError, ReportError, TabalystError
|
|
9
|
+
from tabalyst.generate_service import GeneratePlan as GeneratePlan
|
|
10
|
+
from tabalyst.generate_service import GenerateResult as GenerateResult
|
|
11
|
+
from tabalyst.generate_service import build_generate_plan as build_generate_plan
|
|
12
|
+
from tabalyst.generate_service import generate_datasets as generate_datasets
|
|
9
13
|
from tabalyst.inspect_service import InspectResult as InspectResult
|
|
10
14
|
from tabalyst.inspect_service import generate_inspections as generate_inspections
|
|
11
15
|
from tabalyst.inspect_service import inspect as inspect
|
|
@@ -26,6 +30,8 @@ from tabalyst.service import analyze_scan as analyze_scan
|
|
|
26
30
|
|
|
27
31
|
__all__ = [
|
|
28
32
|
"ConfigurationError",
|
|
33
|
+
"GeneratePlan",
|
|
34
|
+
"GenerateResult",
|
|
29
35
|
"InputError",
|
|
30
36
|
"InspectResult",
|
|
31
37
|
"ReportError",
|
|
@@ -34,6 +40,8 @@ __all__ = [
|
|
|
34
40
|
"TabalystError",
|
|
35
41
|
"__version__",
|
|
36
42
|
"analyze",
|
|
43
|
+
"build_generate_plan",
|
|
44
|
+
"generate_datasets",
|
|
37
45
|
"generate_inspections",
|
|
38
46
|
"generate_reports",
|
|
39
47
|
"generate_samples",
|
|
@@ -90,6 +90,16 @@ def resolve_input_specs(input_specs: Sequence[str | Path]) -> list[Path]:
|
|
|
90
90
|
return sources
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
def output_directory(output_dir: str | Path | None) -> Path | None:
|
|
94
|
+
"""``--output-dir`` as a path, refused when it is an existing file."""
|
|
95
|
+
if output_dir is None:
|
|
96
|
+
return None
|
|
97
|
+
destination = Path(output_dir)
|
|
98
|
+
if destination.exists() and not destination.is_dir():
|
|
99
|
+
raise ReportError(f"Output directory path is a file: {destination}")
|
|
100
|
+
return destination
|
|
101
|
+
|
|
102
|
+
|
|
93
103
|
def plan_outputs(
|
|
94
104
|
input_specs: Sequence[str | Path],
|
|
95
105
|
*,
|
|
@@ -108,9 +118,7 @@ def plan_outputs(
|
|
|
108
118
|
if output is not None and len(sources) != 1:
|
|
109
119
|
raise ConfigurationError("--output can only be used with one input file.")
|
|
110
120
|
|
|
111
|
-
destination =
|
|
112
|
-
if destination is not None and destination.exists() and not destination.is_dir():
|
|
113
|
-
raise ReportError(f"Output directory path is a file: {destination}")
|
|
121
|
+
destination = output_directory(output_dir)
|
|
114
122
|
return [
|
|
115
123
|
(
|
|
116
124
|
source,
|
|
@@ -10,6 +10,7 @@ import typer
|
|
|
10
10
|
|
|
11
11
|
from tabalyst import __version__
|
|
12
12
|
from tabalyst.cli.cache import cache_app
|
|
13
|
+
from tabalyst.cli.generate import generate_command
|
|
13
14
|
from tabalyst.cli.inspect import inspect_command
|
|
14
15
|
from tabalyst.cli.report import report_command
|
|
15
16
|
from tabalyst.cli.sample import sample_command
|
|
@@ -45,6 +46,7 @@ def root(
|
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
app.command("inspect")(inspect_command)
|
|
49
|
+
app.command("generate")(generate_command)
|
|
48
50
|
app.command("report")(report_command)
|
|
49
51
|
app.command("sample")(sample_command)
|
|
50
52
|
app.command("scan")(scan_command)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
"""CLI adapter for Tabalyst Generate."""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from tabalyst.cli.terminal import error_exit_code
|
|
13
|
+
from tabalyst.errors import TabalystError
|
|
14
|
+
from tabalyst.generate_service import build_generate_plan, generate_datasets
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_command(
|
|
18
|
+
definition: Annotated[
|
|
19
|
+
Path | None,
|
|
20
|
+
typer.Argument(help="YAML Generate definition."),
|
|
21
|
+
] = None,
|
|
22
|
+
example: Annotated[
|
|
23
|
+
str | None,
|
|
24
|
+
typer.Option("--example", help="Packaged example: crm or insurance."),
|
|
25
|
+
] = None,
|
|
26
|
+
output: Annotated[
|
|
27
|
+
Path | None,
|
|
28
|
+
typer.Option("--output", "-o", help="One clean CSV file."),
|
|
29
|
+
] = None,
|
|
30
|
+
output_dir: Annotated[
|
|
31
|
+
Path | None,
|
|
32
|
+
typer.Option("--output-dir", "-d", help="Directory for all artifacts."),
|
|
33
|
+
] = None,
|
|
34
|
+
rows: Annotated[
|
|
35
|
+
int | None,
|
|
36
|
+
typer.Option("--rows", help="Number of generated rows."),
|
|
37
|
+
] = None,
|
|
38
|
+
seed: Annotated[
|
|
39
|
+
int | None,
|
|
40
|
+
typer.Option("--seed", help="Root random seed."),
|
|
41
|
+
] = None,
|
|
42
|
+
as_of_date: Annotated[
|
|
43
|
+
str | None,
|
|
44
|
+
typer.Option("--as-of-date", help="Reference date (YYYY-MM-DD)."),
|
|
45
|
+
] = None,
|
|
46
|
+
anomaly_profile: Annotated[
|
|
47
|
+
str | None,
|
|
48
|
+
typer.Option("--anomaly-profile", help="Selected anomaly profile."),
|
|
49
|
+
] = None,
|
|
50
|
+
force: Annotated[
|
|
51
|
+
bool,
|
|
52
|
+
typer.Option("--force", "-f", help="Replace existing artifacts."),
|
|
53
|
+
] = False,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Validate and generate datasets from a YAML definition."""
|
|
56
|
+
try:
|
|
57
|
+
plan = build_generate_plan(
|
|
58
|
+
definition, example=example, output=output, output_dir=output_dir,
|
|
59
|
+
rows=rows, seed=seed, as_of_date=as_of_date,
|
|
60
|
+
anomaly_profile=anomaly_profile, force=force,
|
|
61
|
+
)
|
|
62
|
+
result = generate_datasets(plan)
|
|
63
|
+
except TabalystError as exc:
|
|
64
|
+
typer.echo(f"Error: {exc}", err=True)
|
|
65
|
+
raise typer.Exit(error_exit_code(exc)) from exc
|
|
66
|
+
for path in result.paths:
|
|
67
|
+
typer.echo(f"Created: {path}", err=True)
|
|
@@ -17,6 +17,7 @@ from tabalyst.inspect_service import (
|
|
|
17
17
|
InspectResult,
|
|
18
18
|
generate_inspections,
|
|
19
19
|
)
|
|
20
|
+
from tabalyst.inspector.choices import choice_lines
|
|
20
21
|
|
|
21
22
|
_BASES = {
|
|
22
23
|
"root_array": "the root array",
|
|
@@ -50,16 +51,12 @@ def _choices(success: InspectResult) -> list[str]:
|
|
|
50
51
|
detection = success.document.detection
|
|
51
52
|
if detection.selection.basis != "ambiguous":
|
|
52
53
|
return []
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
source
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
f" tabalyst report {source} --collection '{candidate.path}'"
|
|
59
|
-
for candidate in detection.candidates
|
|
60
|
-
if candidate.eligible
|
|
54
|
+
return choice_lines(
|
|
55
|
+
"report",
|
|
56
|
+
success.source,
|
|
57
|
+
[candidate.path for candidate in detection.candidates if candidate.eligible],
|
|
58
|
+
noun="table" if success.document.inspect.kind == "excel" else "collection",
|
|
61
59
|
)
|
|
62
|
-
return lines
|
|
63
60
|
|
|
64
61
|
|
|
65
62
|
def _print_success(success: InspectResult, *, verbose: bool) -> None:
|
|
@@ -16,6 +16,7 @@ from tabalyst.cli.terminal import (
|
|
|
16
16
|
batch_exit_code,
|
|
17
17
|
collection_paths,
|
|
18
18
|
error_exit_code,
|
|
19
|
+
error_text,
|
|
19
20
|
)
|
|
20
21
|
from tabalyst.errors import TabalystError
|
|
21
22
|
from tabalyst.report_service import BatchReportResult, ReportSuccess, generate_reports
|
|
@@ -70,6 +71,8 @@ def _print_batch_result(
|
|
|
70
71
|
details.append(f"delimiter {source['delimiter']!r}")
|
|
71
72
|
typer.echo(" " + " | ".join(details), err=True)
|
|
72
73
|
|
|
74
|
+
for source, warning in batch.plan.warnings:
|
|
75
|
+
typer.echo(f"Warning [{source}]: {warning}", err=True)
|
|
73
76
|
for success in batch.successes:
|
|
74
77
|
for warning in success.warnings:
|
|
75
78
|
typer.echo(f"Warning [{success.job.source}]: {warning}", err=True)
|
|
@@ -81,7 +84,10 @@ def _print_batch_result(
|
|
|
81
84
|
err=True,
|
|
82
85
|
)
|
|
83
86
|
for failure in batch.failures:
|
|
84
|
-
typer.echo(
|
|
87
|
+
typer.echo(
|
|
88
|
+
f"Error [{failure.job.source}]: {error_text(failure.error, 'report')}",
|
|
89
|
+
err=True,
|
|
90
|
+
)
|
|
85
91
|
|
|
86
92
|
if len(batch.plan.jobs) > 1 and (not quiet or batch.failures):
|
|
87
93
|
typer.echo(
|
|
@@ -96,8 +102,8 @@ def report_command(
|
|
|
96
102
|
typer.Argument(
|
|
97
103
|
metavar="INPUT...",
|
|
98
104
|
help=(
|
|
99
|
-
"One or more CSV
|
|
100
|
-
"or non-recursive glob patterns."
|
|
105
|
+
"One or more CSV, JSON, JSONL or Excel (.xlsx, .xlsm) files, or "
|
|
106
|
+
"scan documents with --scan, or non-recursive glob patterns."
|
|
101
107
|
),
|
|
102
108
|
),
|
|
103
109
|
],
|
|
@@ -114,7 +120,10 @@ def report_command(
|
|
|
114
120
|
typer.Option(
|
|
115
121
|
"--output-dir",
|
|
116
122
|
"-d",
|
|
117
|
-
help=
|
|
123
|
+
help=(
|
|
124
|
+
"Directory for reports named after their source files; "
|
|
125
|
+
"required by --all-collections with several inputs."
|
|
126
|
+
),
|
|
118
127
|
),
|
|
119
128
|
] = None,
|
|
120
129
|
delimiter: Annotated[
|
|
@@ -133,6 +142,17 @@ def report_command(
|
|
|
133
142
|
list[str] | None,
|
|
134
143
|
typer.Option("--collection", help=COLLECTION_HELP),
|
|
135
144
|
] = None,
|
|
145
|
+
all_collections: Annotated[
|
|
146
|
+
bool,
|
|
147
|
+
typer.Option(
|
|
148
|
+
"--all-collections",
|
|
149
|
+
help=(
|
|
150
|
+
"Report every visible collection of a JSON file or Excel "
|
|
151
|
+
"workbook, as <name>.<collection>.html and .json; needs -d "
|
|
152
|
+
"with several inputs."
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
] = False,
|
|
136
156
|
from_scan: Annotated[
|
|
137
157
|
bool,
|
|
138
158
|
typer.Option(
|
|
@@ -202,9 +222,10 @@ def report_command(
|
|
|
202
222
|
on_progress=progress,
|
|
203
223
|
from_scan=from_scan,
|
|
204
224
|
workers=workers,
|
|
225
|
+
all_collections=all_collections,
|
|
205
226
|
)
|
|
206
227
|
except TabalystError as exc:
|
|
207
|
-
typer.echo(f"Error: {exc}", err=True)
|
|
228
|
+
typer.echo(f"Error: {error_text(exc, 'report')}", err=True)
|
|
208
229
|
raise typer.Exit(error_exit_code(exc)) from exc
|
|
209
230
|
|
|
210
231
|
_print_batch_result(batch, quiet=quiet, verbose=verbose)
|