tabalyst 0.7.0__tar.gz → 0.7.1__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.7.0/src/tabalyst.egg-info → tabalyst-0.7.1}/PKG-INFO +33 -2
- {tabalyst-0.7.0 → tabalyst-0.7.1}/README.md +32 -1
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/README.md +5 -5
- {tabalyst-0.7.0 → tabalyst-0.7.1}/pyproject.toml +4 -2
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/__init__.py +12 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/app.py +2 -0
- tabalyst-0.7.1/src/tabalyst/cli/dedup.py +410 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/terminal.py +8 -3
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/config.py +29 -5
- tabalyst-0.7.1/src/tabalyst/csv_records.py +287 -0
- tabalyst-0.7.1/src/tabalyst/dedup/__init__.py +9 -0
- tabalyst-0.7.1/src/tabalyst/dedup/analysis.py +244 -0
- tabalyst-0.7.1/src/tabalyst/dedup/batch_report.py +252 -0
- tabalyst-0.7.1/src/tabalyst/dedup/cache.py +268 -0
- tabalyst-0.7.1/src/tabalyst/dedup/discovery.py +251 -0
- tabalyst-0.7.1/src/tabalyst/dedup/diskstore.py +291 -0
- tabalyst-0.7.1/src/tabalyst/dedup/failures.py +79 -0
- tabalyst-0.7.1/src/tabalyst/dedup/groups.py +260 -0
- tabalyst-0.7.1/src/tabalyst/dedup/identity.py +49 -0
- tabalyst-0.7.1/src/tabalyst/dedup/pipeline.py +209 -0
- tabalyst-0.7.1/src/tabalyst/dedup/publish.py +120 -0
- tabalyst-0.7.1/src/tabalyst/dedup/report.py +599 -0
- tabalyst-0.7.1/src/tabalyst/dedup/settings.py +113 -0
- tabalyst-0.7.1/src/tabalyst/dedup/store.py +124 -0
- tabalyst-0.7.1/src/tabalyst/dedup/tiered.py +121 -0
- tabalyst-0.7.1/src/tabalyst/dedup/writer.py +249 -0
- tabalyst-0.7.1/src/tabalyst/dedup_service.py +487 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_deep.py +4 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_relations.py +3 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_views.py +6 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/reporting.py +2 -4
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/sampling.py +23 -81
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/temporal.py +31 -1
- tabalyst-0.7.1/src/tabalyst/schemas/tabalyst.dedup.batch.schema.json +393 -0
- tabalyst-0.7.1/src/tabalyst/schemas/tabalyst.dedup.schema.json +714 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/static/theme.css +9 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/column.html +14 -16
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/report.html +19 -19
- {tabalyst-0.7.0 → tabalyst-0.7.1/src/tabalyst.egg-info}/PKG-INFO +33 -2
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/SOURCES.txt +22 -0
- tabalyst-0.7.1/tests/test_csv_records.py +218 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_public_api.py +27 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_sampling.py +60 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/LICENSE +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/MANIFEST.in +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/config.json +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/datasets/basic.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/datasets/insurance-customers.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/README.md +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/catalog.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/dataset.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/schemas/accounts.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/schemas/contacts.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/common-references/catalog.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/common-references/dataset.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/common-references/schemas/contacts.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/generate_examples.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/setup.cfg +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/__main__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/_version.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/batch.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cache_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/cache.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/confirm.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/generate.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/inspect.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/report.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/sample.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/scan.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/deep_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/deep_store.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/errors.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/execution_log.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_anomalies.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_columns_package.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_computer.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_computer_output.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_csv.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_definition.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/SOURCES.md +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/catalog.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/brand.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/color.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/computer_type.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/computer_type_form_factor.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/computer_type_product_segment.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/cpu.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/cpu_memory_weight.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/cpu_operating_system.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/display.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/extended_specs.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/form_factor.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_color.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_cpu.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_display.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_gpu.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_memory.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_operating_system.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_profile.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_profile_computer_type.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_profile_segment.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_storage.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu_display.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu_memory_weight.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu_storage_weight.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/memory.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/model_form_factor.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/model_generation.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/operating_system.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_category.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_family.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_family_computer_type.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_family_product_segment.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_model.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_brand.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_display_weight.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_memory_weight.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_storage_weight.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_subcategory.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/profile_exclusions.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/promotion_status.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/promotion_type.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/seller.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/seller_type.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/shipping_availability.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/standard_specs.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/storage.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/warehouse.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/dataset.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computer_type.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computer_type_form_factor.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computer_type_product_segment.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computers.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/cpu.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/cpu_memory_weight.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/cpu_operating_system.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/display.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/extended_specs.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_color.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_cpu.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_display.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_gpu.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_memory.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_operating_system.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_profile.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_profile_computer_type.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_profile_segment.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_storage.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu_display.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu_memory_weight.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu_storage_weight.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/localized.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/memory.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/model_form_factor.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/model_generation.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/operating_system.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_family.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_family_computer_type.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_family_product_segment.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_model.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_brand.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_display_weight.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_memory_weight.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_storage_weight.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_subcategory.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/profile_exclusions.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/seller.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/standard_specs.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/storage.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/warehouse.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/catalog.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/dataset.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/schemas/accounts.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/schemas/contacts.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/SOURCES.md +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/catalog.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/agencies.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/branch_contract_weights.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/claim_statuses.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/contract_statuses.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/contract_types.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/payment_frequencies.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/payment_methods.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/dataset.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/agencies.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/branch_contract_weights.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/claim_statuses.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/contract_statuses.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/contract_types.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/customers.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/payment_frequencies.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/payment_methods.yaml +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_insurance.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_package.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_references.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/SOURCES.md +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/street_directions_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/street_names_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/street_types_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/unit_types_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/contact/email_patterns.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/contact/email_providers_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/contact/phone_area_codes_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/geography/cities_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/geography/postal_prefixes_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/geography/provinces_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/people/family_names_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/people/first_names_en_ca.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/people/first_names_fr_qc.csv +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_selection.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_values.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspect_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/choices.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/build.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/detect.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/parameters.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/build.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/detect.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/parameters.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/persistence.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/resolution.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/json_text.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/option_notices.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/plan_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/progress.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/project_open_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/project_scan_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_bounded_values.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_cache.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_codec.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_generation.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_locks.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_publication.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_queries.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_query_budget.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_recovery.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_retention.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_schema.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_session.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_staging.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_sync.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_validation.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/catalog.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/freshness.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/identity.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/index.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/location.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/store.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_chain.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_classification.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_column_page.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_config.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_details.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_finding_details.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_findings.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_insights.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_labels.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_measures.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_overview.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_plan.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_preview.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_profile.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_schema.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_sections.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_signals.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_summary.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_table.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/result_model.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/sample_page.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/sampling_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_document.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_page.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_reuse.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/api.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/bottomk.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/cases.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/charactermap.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/characters.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/candidates.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/catalog.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/identifiers.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/names.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/rules.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/config.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/convention_rule.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/correlation_ratio.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/crosstab.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/amount.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/base.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/boolean.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/currency.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/email.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/enumeration.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/ip.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/keys.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/names.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/number.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/pattern.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/percentage.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/phone.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/postal.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/quantity.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/registry.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/shape.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/url.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/uuid.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/diagnostics.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/engine.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/exposure.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/features.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/field.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/identity.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/lengthsample.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/longtext.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/measures.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/memory.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/normalization.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/observations.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/paths.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/pattern_eligibility.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/patternfacets.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/patterninfer.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/patterntree.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/pearson.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzer.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/characters.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/common.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/crosstab.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/date_order.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/distinct.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/drift.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/duplicates.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/forms.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/frequencies.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/histogram.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/lengths.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/matrix.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/measures.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/parts.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/patterns.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/pearson.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/residuals.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/shape.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/stats.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/structure.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/temporal.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/texts.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/categorystats.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/costmodel.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/crosstabs.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/facts.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/identifiers.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/orders.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/planner.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/reasons.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/registry.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/relations.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/render.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/samplerows.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/secondpass.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/stratification.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/stratmodels.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/process.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/quality.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/rankcorr.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/base.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/csv_reader.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/excel_common.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/excel_reader.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/excel_table.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/factory.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/json_common.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/json_reader.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/jsonl_reader.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/records.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/rowsample.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/schema.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/accumulators.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/bins.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/boundaries.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/characters.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/crosstab.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/document.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/drift.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/duplicates.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/exactset.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/executor.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/forms.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/frequencies.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/matrix.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/measures.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/orders.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/parallel.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/parts.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/patternreader.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/patterns.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/pearson.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/periods.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/ranks.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/replay.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/residuals.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/routing.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/schema.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/shape.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/stats.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/structure.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/texts.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/__init__.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/bins.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/estimate.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/evaluate.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/execute.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/near.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/plan.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/reasons.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/render.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/result.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/select.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/targets.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/weights.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/stratify.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/structure.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/technical.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/validation.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/values.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/valuesample.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/workers.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.report.schema.json +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.sample.schema.json +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.scan.deep.schema.json +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.scan.schema.json +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/shared_scan_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/smart_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/source_guard.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/static/report.js +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/summary_models.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/characters.html +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/insights.html +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/patterns.html +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/sample.html +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/dependency_links.txt +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/entry_points.txt +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/requires.txt +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/top_level.txt +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_all_collections.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_cli.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_examples_index.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_examples_layout.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_anomalies.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_anomalies_regression.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_catalog.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_components.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_derivations.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_hierarchy.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_output.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_crm.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_crm_package.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_insurance.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_insurance_stress.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_lot4.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_package.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_references.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_selection.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_json_text.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_license_headers.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_make_datasets.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_option_notices.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_cache.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_database.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_generations.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_identity.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_index.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_location.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_open_service.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_queries.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_rescan.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_scan.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_scan_model.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_sessions.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_store.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_report_contrast.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_reporting.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scan_command.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_amounts.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_batches.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_config.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_csv.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_email_url.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_exposure.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_paths.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_phone.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_postal.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_uuid_ip.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_shared_scan_cli.py +0 -0
- {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_source_freshness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabalyst
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
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
|
|
@@ -82,6 +82,7 @@ Update before each new test.
|
|
|
82
82
|
| [**Tabalyst Scan**](https://docs.tabalyst.com/scan/) | Profile datasets and generate JSON and HTML scan summaries |
|
|
83
83
|
| [**Tabalyst Inspect**](https://docs.tabalyst.com/inspect/) | Discover and configure JSON collections and Excel tables |
|
|
84
84
|
| [**Tabalyst Sample**](https://docs.tabalyst.com/sample/) | Create random, stratified and smart CSV samples |
|
|
85
|
+
| [**Tabalyst Dedup**](https://docs.tabalyst.com/dedup/) | Find the exact duplicate rows of CSV files and write the unique and repeated rows |
|
|
85
86
|
| [**Tabalyst Generate**](https://docs.tabalyst.com/generate/) | Generate synthetic CSV datasets for testing and demos |
|
|
86
87
|
|
|
87
88
|
## Tabalyst Report
|
|
@@ -235,6 +236,34 @@ estimate the population from it. Sampling reads records as a stream.
|
|
|
235
236
|
|
|
236
237
|
Details: [Tabalyst Sample](https://docs.tabalyst.com/sample/).
|
|
237
238
|
|
|
239
|
+
## Tabalyst Dedup
|
|
240
|
+
|
|
241
|
+
Find the rows that occur more than once in a CSV file, without modifying it:
|
|
242
|
+
|
|
243
|
+
```console
|
|
244
|
+
tabalyst dedup customers.csv
|
|
245
|
+
tabalyst dedup "*.csv" -d deduped
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Two rows are the same only when every cell is identical (case, spaces and
|
|
249
|
+
leading zeros count). Dedup writes, beside the source or in `-d`,
|
|
250
|
+
`customers.unique.csv` (every distinct row once, at its first place),
|
|
251
|
+
`customers.duplicates.csv` (each repeated row once) and, when a row occurs three
|
|
252
|
+
times or more, `customers.duplicates_all.csv` (every occurrence of the repeated
|
|
253
|
+
rows), plus `customers.dedup.json`, a report whose counts always add up. A file
|
|
254
|
+
without duplicate gets its report only. A batch also writes
|
|
255
|
+
`dedup.summary.json`; a file that is already processed and unchanged is skipped.
|
|
256
|
+
|
|
257
|
+
A result is exact or the file fails with a reason: the rows are kept in memory up
|
|
258
|
+
to a budget (`--memory-budget-mb`, 512 by default) and then on disk, a file may
|
|
259
|
+
hold 10,000,000 records by default (`--max-rows`), and nothing is ever
|
|
260
|
+
approximated. Dedup reads CSV files only and asks no question.
|
|
261
|
+
|
|
262
|
+
Details: [Tabalyst Dedup](https://docs.tabalyst.com/dedup/),
|
|
263
|
+
[batches and already processed files](https://docs.tabalyst.com/dedup/batch/),
|
|
264
|
+
[exactness, memory and limits](https://docs.tabalyst.com/dedup/limits/) and the
|
|
265
|
+
[`dedup` command reference](https://docs.tabalyst.com/reference/cli/dedup/).
|
|
266
|
+
|
|
238
267
|
## Tabalyst Generate
|
|
239
268
|
|
|
240
269
|
Generate synthetic CSV data from a packaged example (`crm`, `insurance` or
|
|
@@ -264,6 +293,7 @@ import tabalyst
|
|
|
264
293
|
result = tabalyst.analyze("customers.csv", "customers.html")
|
|
265
294
|
batch = tabalyst.generate_reports(["*.csv"], output_dir="reports")
|
|
266
295
|
sample = tabalyst.sample_csv("customers.csv", method="random", rows=1000, seed=42)
|
|
296
|
+
dedup = tabalyst.dedup_csv("customers.csv")
|
|
267
297
|
scan = tabalyst.scan("orders.json")
|
|
268
298
|
inspection = tabalyst.inspect("orders.json")
|
|
269
299
|
```
|
|
@@ -297,7 +327,8 @@ Generated JSON and HTML may contain source values and should be shared
|
|
|
297
327
|
accordingly.
|
|
298
328
|
|
|
299
329
|
Reports, samples, and scans read CSV, JSON and JSONL files as a stream, with
|
|
300
|
-
memory bounded by configurable limits
|
|
330
|
+
memory bounded by configurable limits; Dedup keeps the rows it has seen in memory
|
|
331
|
+
up to a budget, then on disk. An Excel workbook is read by sheet into
|
|
301
332
|
memory, and a sheet above 1 GiB of XML is refused. See
|
|
302
333
|
[limitations](https://docs.tabalyst.com/about/limitations/).
|
|
303
334
|
|
|
@@ -42,6 +42,7 @@ Update before each new test.
|
|
|
42
42
|
| [**Tabalyst Scan**](https://docs.tabalyst.com/scan/) | Profile datasets and generate JSON and HTML scan summaries |
|
|
43
43
|
| [**Tabalyst Inspect**](https://docs.tabalyst.com/inspect/) | Discover and configure JSON collections and Excel tables |
|
|
44
44
|
| [**Tabalyst Sample**](https://docs.tabalyst.com/sample/) | Create random, stratified and smart CSV samples |
|
|
45
|
+
| [**Tabalyst Dedup**](https://docs.tabalyst.com/dedup/) | Find the exact duplicate rows of CSV files and write the unique and repeated rows |
|
|
45
46
|
| [**Tabalyst Generate**](https://docs.tabalyst.com/generate/) | Generate synthetic CSV datasets for testing and demos |
|
|
46
47
|
|
|
47
48
|
## Tabalyst Report
|
|
@@ -195,6 +196,34 @@ estimate the population from it. Sampling reads records as a stream.
|
|
|
195
196
|
|
|
196
197
|
Details: [Tabalyst Sample](https://docs.tabalyst.com/sample/).
|
|
197
198
|
|
|
199
|
+
## Tabalyst Dedup
|
|
200
|
+
|
|
201
|
+
Find the rows that occur more than once in a CSV file, without modifying it:
|
|
202
|
+
|
|
203
|
+
```console
|
|
204
|
+
tabalyst dedup customers.csv
|
|
205
|
+
tabalyst dedup "*.csv" -d deduped
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Two rows are the same only when every cell is identical (case, spaces and
|
|
209
|
+
leading zeros count). Dedup writes, beside the source or in `-d`,
|
|
210
|
+
`customers.unique.csv` (every distinct row once, at its first place),
|
|
211
|
+
`customers.duplicates.csv` (each repeated row once) and, when a row occurs three
|
|
212
|
+
times or more, `customers.duplicates_all.csv` (every occurrence of the repeated
|
|
213
|
+
rows), plus `customers.dedup.json`, a report whose counts always add up. A file
|
|
214
|
+
without duplicate gets its report only. A batch also writes
|
|
215
|
+
`dedup.summary.json`; a file that is already processed and unchanged is skipped.
|
|
216
|
+
|
|
217
|
+
A result is exact or the file fails with a reason: the rows are kept in memory up
|
|
218
|
+
to a budget (`--memory-budget-mb`, 512 by default) and then on disk, a file may
|
|
219
|
+
hold 10,000,000 records by default (`--max-rows`), and nothing is ever
|
|
220
|
+
approximated. Dedup reads CSV files only and asks no question.
|
|
221
|
+
|
|
222
|
+
Details: [Tabalyst Dedup](https://docs.tabalyst.com/dedup/),
|
|
223
|
+
[batches and already processed files](https://docs.tabalyst.com/dedup/batch/),
|
|
224
|
+
[exactness, memory and limits](https://docs.tabalyst.com/dedup/limits/) and the
|
|
225
|
+
[`dedup` command reference](https://docs.tabalyst.com/reference/cli/dedup/).
|
|
226
|
+
|
|
198
227
|
## Tabalyst Generate
|
|
199
228
|
|
|
200
229
|
Generate synthetic CSV data from a packaged example (`crm`, `insurance` or
|
|
@@ -224,6 +253,7 @@ import tabalyst
|
|
|
224
253
|
result = tabalyst.analyze("customers.csv", "customers.html")
|
|
225
254
|
batch = tabalyst.generate_reports(["*.csv"], output_dir="reports")
|
|
226
255
|
sample = tabalyst.sample_csv("customers.csv", method="random", rows=1000, seed=42)
|
|
256
|
+
dedup = tabalyst.dedup_csv("customers.csv")
|
|
227
257
|
scan = tabalyst.scan("orders.json")
|
|
228
258
|
inspection = tabalyst.inspect("orders.json")
|
|
229
259
|
```
|
|
@@ -257,7 +287,8 @@ Generated JSON and HTML may contain source values and should be shared
|
|
|
257
287
|
accordingly.
|
|
258
288
|
|
|
259
289
|
Reports, samples, and scans read CSV, JSON and JSONL files as a stream, with
|
|
260
|
-
memory bounded by configurable limits
|
|
290
|
+
memory bounded by configurable limits; Dedup keeps the rows it has seen in memory
|
|
291
|
+
up to a budget, then on disk. An Excel workbook is read by sheet into
|
|
261
292
|
memory, and a sheet above 1 GiB of XML is refused. See
|
|
262
293
|
[limitations](https://docs.tabalyst.com/about/limitations/).
|
|
263
294
|
|
|
@@ -16,7 +16,7 @@ examples/
|
|
|
16
16
|
|-- showcase/ (what the website and the documentation publish, nothing else)
|
|
17
17
|
| |-- basic/
|
|
18
18
|
| |-- insurance-customers/ (report.html, report.json, scan.json; 100,000 rows)
|
|
19
|
-
| |-- insurance-customers
|
|
19
|
+
| |-- insurance-customers.scan/ (scan, second pass, smart plan and sample; 100,000 rows)
|
|
20
20
|
| |-- orders/
|
|
21
21
|
| |-- sales/
|
|
22
22
|
| `-- web-events/
|
|
@@ -67,7 +67,7 @@ fails when it is stale; `regenerate.py` runs it), so it never drifts.
|
|
|
67
67
|
| --- | --- | --- | --- |
|
|
68
68
|
| `showcase/basic/` | `report` | no | `get-started/examples.md`, `reference/history.md` |
|
|
69
69
|
| `showcase/insurance-customers/` | `report` | yes | `get-started/examples.md` |
|
|
70
|
-
| `showcase/insurance-customers
|
|
70
|
+
| `showcase/insurance-customers.scan/` | `scan` | no | `get-started/simple-clear-smart.md` |
|
|
71
71
|
| `showcase/orders/` | `report` | yes | `get-started/examples.md` |
|
|
72
72
|
| `showcase/sales/` | `report` | yes | `get-started/examples.md` |
|
|
73
73
|
| `showcase/web-events/` | `report` | yes | `get-started/examples.md` |
|
|
@@ -96,17 +96,17 @@ tabalyst report examples/datasets/web-events.jsonl -o examples/showcase/web-even
|
|
|
96
96
|
tabalyst report examples/datasets/sales.xlsx -o examples/showcase/sales/report.html
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
The folder `insurance-customers
|
|
99
|
+
The folder `insurance-customers.scan/` shows the three steps of Tabalyst Scan on
|
|
100
100
|
`insurance-customers-100k.csv`, the generated base of 100,000 rows (see "Simple, Clear and Smart" in the documentation):
|
|
101
101
|
the scan (Simple, with its web page, Clear), the second pass and the plan of a
|
|
102
102
|
smart sample (Smart), and the smart sample with its report and page. Its four steps
|
|
103
103
|
are in its `demo.json`:
|
|
104
104
|
|
|
105
105
|
```console
|
|
106
|
-
tabalyst scan {datasets}/insurance-customers-100k.csv -o examples/showcase/insurance-customers
|
|
106
|
+
tabalyst scan {datasets}/insurance-customers-100k.csv -o examples/showcase/insurance-customers.scan/insurance-customers.scan.json --config examples/config.json --deep --yes --force
|
|
107
107
|
tabalyst scan {datasets}/insurance-customers-100k.csv --config examples/config.json --deep --yes --force --no-html
|
|
108
108
|
tabalyst scan {datasets}/insurance-customers-100k.csv --plan --smart --sample-size 3000 --config examples/config.json
|
|
109
|
-
tabalyst sample {datasets}/insurance-customers-100k.csv --smart --rows 3000 --seed 42 -o examples/showcase/insurance-customers
|
|
109
|
+
tabalyst sample {datasets}/insurance-customers-100k.csv --smart --rows 3000 --seed 42 -o examples/showcase/insurance-customers.scan/insurance-customers.sample.csv --config examples/config.json --force
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
`make_datasets.py` generates the base (45 seconds, 27 MB, SHA-256 checked) and leaves it outside the
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tabalyst"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.1"
|
|
8
8
|
description = "Open-source toolkit for understanding and working with unfamiliar data."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -77,7 +77,9 @@ markers = [
|
|
|
77
77
|
"scan_json_lot(name): Scan and JSON work lot (plan 2026-10-06) that provides the tested behavior; lot 0 tests describe current behavior",
|
|
78
78
|
"report_lot(name): Report overview lot (plan 2026-10-16) that provides the tested behavior; skipped until the lot is enabled in tests/scan/report_gate.py",
|
|
79
79
|
"inspect_lot(name): Tabalyst Inspect implementation lot that provides the tested behavior",
|
|
80
|
-
"
|
|
80
|
+
"dedup_lot(name): Tabalyst Dedup implementation lot (plan 2026-10-22) that provides the tested behavior; skipped until the lot is enabled in tests/dedup/conftest.py",
|
|
81
|
+
"dedup_t(*ids): requirement tests (T01 to T30 of the Dedup specification) that the test covers",
|
|
82
|
+
"dedup_heavy: Dedup large-scale test, never run by default; select it with -m dedup_heavy",
|
|
81
83
|
"shipped_exposure_default: the test reads the shipped default of scan.exposure.sensitive_values (show), not the masking default the suite runs with",
|
|
82
84
|
]
|
|
83
85
|
|
|
@@ -16,6 +16,10 @@ from tabalyst.errors import ConfigurationError, InputError, ReportError, Tabalys
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from tabalyst.config import DeepSettings as DeepSettings
|
|
18
18
|
from tabalyst.config import PearsonSettings as PearsonSettings
|
|
19
|
+
from tabalyst.dedup_service import BatchDedupResult as BatchDedupResult
|
|
20
|
+
from tabalyst.dedup_service import DedupResult as DedupResult
|
|
21
|
+
from tabalyst.dedup_service import dedup_csv as dedup_csv
|
|
22
|
+
from tabalyst.dedup_service import generate_dedups as generate_dedups
|
|
19
23
|
from tabalyst.deep_service import execute_deep as execute_deep
|
|
20
24
|
from tabalyst.deep_service import generate_deep as generate_deep
|
|
21
25
|
from tabalyst.deep_service import prepare_deep as prepare_deep
|
|
@@ -76,6 +80,10 @@ _LAZY: dict[str, str] = {
|
|
|
76
80
|
"generate_deep": "tabalyst.deep_service",
|
|
77
81
|
"prepare_deep": "tabalyst.deep_service",
|
|
78
82
|
"ConfirmationRequired": "tabalyst.deep_store",
|
|
83
|
+
"BatchDedupResult": "tabalyst.dedup_service",
|
|
84
|
+
"DedupResult": "tabalyst.dedup_service",
|
|
85
|
+
"dedup_csv": "tabalyst.dedup_service",
|
|
86
|
+
"generate_dedups": "tabalyst.dedup_service",
|
|
79
87
|
"ComputerPlan": "tabalyst.generate_service",
|
|
80
88
|
"GeneratePlan": "tabalyst.generate_service",
|
|
81
89
|
"GenerateResult": "tabalyst.generate_service",
|
|
@@ -140,9 +148,11 @@ def __dir__() -> list[str]:
|
|
|
140
148
|
__all__ = [
|
|
141
149
|
"AnalysisPlan",
|
|
142
150
|
"AnalysisRequest",
|
|
151
|
+
"BatchDedupResult",
|
|
143
152
|
"ComputerPlan",
|
|
144
153
|
"ConfigurationError",
|
|
145
154
|
"ConfirmationRequired",
|
|
155
|
+
"DedupResult",
|
|
146
156
|
"DeepResult",
|
|
147
157
|
"DeepSettings",
|
|
148
158
|
"GeneratePlan",
|
|
@@ -166,9 +176,11 @@ __all__ = [
|
|
|
166
176
|
"__version__",
|
|
167
177
|
"analyze",
|
|
168
178
|
"build_generate_plan",
|
|
179
|
+
"dedup_csv",
|
|
169
180
|
"execute_deep",
|
|
170
181
|
"execute_smart_samples",
|
|
171
182
|
"generate_datasets",
|
|
183
|
+
"generate_dedups",
|
|
172
184
|
"generate_deep",
|
|
173
185
|
"generate_inspections",
|
|
174
186
|
"generate_plans",
|
|
@@ -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.dedup import dedup_command
|
|
13
14
|
from tabalyst.cli.generate import generate_command
|
|
14
15
|
from tabalyst.cli.inspect import inspect_command
|
|
15
16
|
from tabalyst.cli.report import report_command
|
|
@@ -45,6 +46,7 @@ def root(
|
|
|
45
46
|
"""Understand, explore, clean, and validate structured data."""
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
app.command("dedup")(dedup_command)
|
|
48
50
|
app.command("inspect")(inspect_command)
|
|
49
51
|
app.command("generate")(generate_command)
|
|
50
52
|
app.command("report")(report_command)
|
|
@@ -0,0 +1,410 @@
|
|
|
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 Dedup: options, progress, the summaries of design dedup 13.2 and the exit codes."""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Annotated
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
|
|
14
|
+
from tabalyst.cli.terminal import ProgressPrinter, batch_exit_code, error_exit_code
|
|
15
|
+
from tabalyst.dedup.report import CompleteReport
|
|
16
|
+
from tabalyst.dedup.settings import DedupSettings, resolve_dedup_settings
|
|
17
|
+
from tabalyst.dedup_service import BatchDedupResult, DedupResult, execute_dedups
|
|
18
|
+
from tabalyst.errors import TabalystError
|
|
19
|
+
from tabalyst.progress import ProgressEvent, ProgressPhase
|
|
20
|
+
|
|
21
|
+
RULE = "-" * 63
|
|
22
|
+
LABEL_WIDTH = 22
|
|
23
|
+
BLOCK_LABEL_WIDTH = 14
|
|
24
|
+
MIB = 1 << 20
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _is_terminal() -> bool:
|
|
28
|
+
"""Whether the progress line can be rewritten: only a terminal shows it."""
|
|
29
|
+
return sys.stderr.isatty()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _DedupProgress(ProgressPrinter):
|
|
33
|
+
"""The progress line of Dedup, which also tells how a source ended."""
|
|
34
|
+
|
|
35
|
+
def _label(self, event: ProgressEvent) -> str:
|
|
36
|
+
label = super()._label(event)
|
|
37
|
+
if event.detail and event.phase in {ProgressPhase.COMPLETE, ProgressPhase.FAILED}:
|
|
38
|
+
label = f"{label} ({event.detail})"
|
|
39
|
+
return label
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _display(path: Path) -> str:
|
|
43
|
+
"""A path relative to the working folder when it is inside it, absolute otherwise."""
|
|
44
|
+
resolved = path.resolve()
|
|
45
|
+
try:
|
|
46
|
+
return str(resolved.relative_to(Path.cwd().resolve()))
|
|
47
|
+
except ValueError:
|
|
48
|
+
return str(resolved)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _mib(size: int) -> str:
|
|
52
|
+
return f"{size / MIB:,.1f} MiB"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _labels(sources: list[Path]) -> list[str]:
|
|
56
|
+
"""The name of each source, with its folder when two sources share a name."""
|
|
57
|
+
names = [path.name for path in sources]
|
|
58
|
+
return [
|
|
59
|
+
name if names.count(name) == 1 else _display(path)
|
|
60
|
+
for name, path in zip(names, sources, strict=True)
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ------------------------------------------------------------------- table
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _counts(result: DedupResult) -> tuple[str, str, str]:
|
|
68
|
+
"""Rows, extra copies and outputs of a source as the table prints them."""
|
|
69
|
+
if result.status == "FAILED":
|
|
70
|
+
return "-", "-", "-"
|
|
71
|
+
report = result.report
|
|
72
|
+
rows = extra = "-"
|
|
73
|
+
if isinstance(report, CompleteReport):
|
|
74
|
+
rows = f"{report.records.total:,}"
|
|
75
|
+
extra = f"{report.records.extra_copies:,}"
|
|
76
|
+
outputs = "0 new" if result.status == "SKIPPED" else str(len(result.files))
|
|
77
|
+
return rows, extra, outputs
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _table(results: tuple[DedupResult, ...]) -> list[str]:
|
|
81
|
+
labels = _labels([item.source for item in results])
|
|
82
|
+
counts = [_counts(item) for item in results]
|
|
83
|
+
file_width = max([16, *(len(label) for label in labels)])
|
|
84
|
+
rows_width = max([7, *(len(row[0]) for row in counts)])
|
|
85
|
+
extra_width = max([12, *(len(row[1]) for row in counts)])
|
|
86
|
+
width = max(len(RULE), file_width + 2 + 8 + 2 + rows_width + 4 + extra_width + 2 + 7)
|
|
87
|
+
rule = "-" * width
|
|
88
|
+
lines = [
|
|
89
|
+
rule,
|
|
90
|
+
(
|
|
91
|
+
f"{'File':<{file_width}} {'Status':<8} {'Rows':<{rows_width}} "
|
|
92
|
+
f"{'Extra copies':<{extra_width}} Outputs"
|
|
93
|
+
),
|
|
94
|
+
rule,
|
|
95
|
+
]
|
|
96
|
+
for label, item, (rows, extra, outputs) in zip(labels, results, counts, strict=True):
|
|
97
|
+
lines.append(
|
|
98
|
+
f"{label:<{file_width}} {item.status:<8} {rows:>{rows_width}} "
|
|
99
|
+
f"{extra:<{extra_width}} {outputs}".rstrip()
|
|
100
|
+
)
|
|
101
|
+
lines.append(rule)
|
|
102
|
+
return lines
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _field(label: str, value: object, width: int = LABEL_WIDTH) -> str:
|
|
106
|
+
return f"{label:<{width}}{value}"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _batch_result(batch: BatchDedupResult) -> str:
|
|
110
|
+
if not batch.results or not batch.failures:
|
|
111
|
+
return "COMPLETE"
|
|
112
|
+
return "FAILED" if len(batch.failures) == len(batch.results) else "PARTIAL"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _batch_lines(batch: BatchDedupResult, *, verbose: bool) -> list[str]:
|
|
116
|
+
results = batch.results
|
|
117
|
+
done = [item for item in results if item.status == "DONE"]
|
|
118
|
+
skipped = [item for item in results if item.status == "SKIPPED"]
|
|
119
|
+
reports = [item for item in results if item.report_path is not None]
|
|
120
|
+
updated = [item for item in reports if item.status != "SKIPPED"]
|
|
121
|
+
lines = ["Tabalyst Dedup"]
|
|
122
|
+
if results:
|
|
123
|
+
lines += _table(results)
|
|
124
|
+
else:
|
|
125
|
+
lines += [RULE, "No file to process.", RULE]
|
|
126
|
+
lines.append(_field("Eligible files:", len(results)))
|
|
127
|
+
if batch.excluded:
|
|
128
|
+
lines.append(_field("Excluded:", len(batch.excluded)))
|
|
129
|
+
lines += [
|
|
130
|
+
_field("Processed:", len(done)),
|
|
131
|
+
_field("Already processed:", len(skipped)),
|
|
132
|
+
_field("Failed:", len(batch.failures)),
|
|
133
|
+
_field("New CSV files:", sum(len(item.files) for item in results)),
|
|
134
|
+
_field("JSON reports:", f"{len(reports)} available ({len(updated)} updated)"),
|
|
135
|
+
_field(
|
|
136
|
+
"Batch report:",
|
|
137
|
+
_display(batch.batch_report)
|
|
138
|
+
if batch.batch_report is not None
|
|
139
|
+
else ("not written" if batch.batch_report_error is not None else "none"),
|
|
140
|
+
),
|
|
141
|
+
_field("Result:", _batch_result(batch)),
|
|
142
|
+
]
|
|
143
|
+
if verbose:
|
|
144
|
+
lines += ["", "Details"]
|
|
145
|
+
for item, label in zip(
|
|
146
|
+
results, _labels([item.source for item in results]), strict=True
|
|
147
|
+
):
|
|
148
|
+
lines.append(f" {label}: {_detail(item)}")
|
|
149
|
+
for path, reason in batch.excluded:
|
|
150
|
+
lines.append(f" {path.name}: excluded ({reason})")
|
|
151
|
+
lines.append(f"Duration: {batch.duration_seconds:.2f}s")
|
|
152
|
+
return lines
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _detail(result: DedupResult) -> str:
|
|
156
|
+
"""What --verbose adds for one source: how it ended and, if run, the measures."""
|
|
157
|
+
if result.status == "FAILED":
|
|
158
|
+
return f"failed ({result.reason})"
|
|
159
|
+
if result.status == "SKIPPED":
|
|
160
|
+
return f"already processed, verified by {result.verification}"
|
|
161
|
+
report = result.report
|
|
162
|
+
if not isinstance(report, CompleteReport):
|
|
163
|
+
return "processed"
|
|
164
|
+
run = report.run
|
|
165
|
+
parts = [f"storage {run.storage_mode}", f"{run.passes} pass{'es' if run.passes > 1 else ''}"]
|
|
166
|
+
parts.append(f"first pass {run.first_pass_seconds:.2f}s")
|
|
167
|
+
if run.second_pass_seconds is not None:
|
|
168
|
+
parts.append(f"second pass {run.second_pass_seconds:.2f}s")
|
|
169
|
+
if run.memory_peak_bytes is not None:
|
|
170
|
+
parts.append(f"memory peak {_mib(run.memory_peak_bytes)}")
|
|
171
|
+
return ", ".join(parts)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# ------------------------------------------------------------ one source
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _block_field(label: str, value: object) -> str:
|
|
178
|
+
return _field(label, value, BLOCK_LABEL_WIDTH)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _single_lines(result: DedupResult, *, verbose: bool) -> list[str]:
|
|
182
|
+
lines = ["Tabalyst Dedup", RULE, _block_field("File:", result.source.name)]
|
|
183
|
+
report = result.report
|
|
184
|
+
failed = result.status == "FAILED"
|
|
185
|
+
if isinstance(report, CompleteReport):
|
|
186
|
+
counts = report.records
|
|
187
|
+
lines += [
|
|
188
|
+
_block_field("Source rows:", f"{counts.total:,}"),
|
|
189
|
+
_block_field("Distinct:", f"{counts.distinct:,}"),
|
|
190
|
+
_block_field("Groups:", f"{counts.duplicate_groups:,}"),
|
|
191
|
+
_block_field("Extra copies:", f"{counts.extra_copies:,}"),
|
|
192
|
+
]
|
|
193
|
+
if verbose and result.status == "DONE":
|
|
194
|
+
run = report.run
|
|
195
|
+
lines += [
|
|
196
|
+
_block_field("Storage:", run.storage_mode),
|
|
197
|
+
_block_field("Passes:", run.passes),
|
|
198
|
+
_block_field("First pass:", f"{run.first_pass_seconds:.2f}s"),
|
|
199
|
+
]
|
|
200
|
+
if run.second_pass_seconds is not None:
|
|
201
|
+
lines.append(_block_field("Second pass:", f"{run.second_pass_seconds:.2f}s"))
|
|
202
|
+
if run.memory_peak_bytes is not None:
|
|
203
|
+
lines.append(_block_field("Memory peak:", _mib(run.memory_peak_bytes)))
|
|
204
|
+
lines.append(_block_field("Duration:", f"{run.duration_seconds:.2f}s"))
|
|
205
|
+
lines.append("")
|
|
206
|
+
if result.status == "SKIPPED":
|
|
207
|
+
lines.append(f"Already processed, verified by {result.verification}.")
|
|
208
|
+
elif counts.extra_copies == 0:
|
|
209
|
+
lines.append("No duplicate records found.")
|
|
210
|
+
else:
|
|
211
|
+
lines.append("Duplicate records found.")
|
|
212
|
+
if result.files:
|
|
213
|
+
lines.append("CSV files generated:")
|
|
214
|
+
lines += [f" {_display(path)}" for path in result.files]
|
|
215
|
+
else:
|
|
216
|
+
lines.append("No CSV files generated.")
|
|
217
|
+
else:
|
|
218
|
+
lines += ["", f"Failed: {result.reason}. No CSV files generated."]
|
|
219
|
+
if result.report_path is not None:
|
|
220
|
+
lines.append(f"Report: {_display(result.report_path)}")
|
|
221
|
+
lines.append(f"Status: {'FAILED' if failed else 'COMPLETE'}")
|
|
222
|
+
return lines
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ----------------------------------------------------------------- command
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _error_lines(batch: BatchDedupResult) -> list[str]:
|
|
229
|
+
"""One line per failed source: its reason and its message, never a cell."""
|
|
230
|
+
return [
|
|
231
|
+
f"Error [{item.source.name}]: {item.error} ({item.reason})"
|
|
232
|
+
for item in batch.failures
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def exit_code(batch: BatchDedupResult) -> int:
|
|
237
|
+
"""Exit code of design dedup 13.4 for a batch that ran.
|
|
238
|
+
|
|
239
|
+
The class of each error decides: 4 when every failure is an input error, 1 as soon as
|
|
240
|
+
one is not. A source refused just before its run (``output_refused``) keeps the class
|
|
241
|
+
of its refusal: a collision with a source is 4, a foreign file is 1, as at the plan.
|
|
242
|
+
A batch report that cannot be written is 1, whatever the sources did.
|
|
243
|
+
"""
|
|
244
|
+
errors: list[TabalystError] = [
|
|
245
|
+
item.error for item in batch.failures if item.error is not None
|
|
246
|
+
]
|
|
247
|
+
if batch.batch_report_error is not None:
|
|
248
|
+
errors.append(batch.batch_report_error)
|
|
249
|
+
return batch_exit_code(errors) if errors else 0
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def dedup_command(
|
|
253
|
+
inputs: Annotated[
|
|
254
|
+
list[str],
|
|
255
|
+
typer.Argument(
|
|
256
|
+
metavar="INPUT...",
|
|
257
|
+
help="One or more CSV files or non-recursive glob patterns. Quote a "
|
|
258
|
+
"pattern so that Tabalyst expands it.",
|
|
259
|
+
),
|
|
260
|
+
],
|
|
261
|
+
output_dir: Annotated[
|
|
262
|
+
Path | None,
|
|
263
|
+
typer.Option(
|
|
264
|
+
"--output-dir",
|
|
265
|
+
"-d",
|
|
266
|
+
help="Directory for the outputs and the batch report. Default: beside "
|
|
267
|
+
"each source.",
|
|
268
|
+
),
|
|
269
|
+
] = None,
|
|
270
|
+
config: Annotated[
|
|
271
|
+
Path | None,
|
|
272
|
+
typer.Option("--config", "-c", help="JSON configuration file."),
|
|
273
|
+
] = None,
|
|
274
|
+
max_rows: Annotated[
|
|
275
|
+
int | None,
|
|
276
|
+
typer.Option(
|
|
277
|
+
"--max-rows",
|
|
278
|
+
help="Most data records a source may hold; more fails that source "
|
|
279
|
+
"(default 10,000,000).",
|
|
280
|
+
),
|
|
281
|
+
] = None,
|
|
282
|
+
delimiter: Annotated[
|
|
283
|
+
str | None,
|
|
284
|
+
typer.Option("--delimiter", help="One-character CSV delimiter."),
|
|
285
|
+
] = None,
|
|
286
|
+
encoding: Annotated[
|
|
287
|
+
str | None,
|
|
288
|
+
typer.Option("--encoding", help="CSV text encoding."),
|
|
289
|
+
] = None,
|
|
290
|
+
memory_budget_mb: Annotated[
|
|
291
|
+
int | None,
|
|
292
|
+
typer.Option(
|
|
293
|
+
"--memory-budget-mb",
|
|
294
|
+
help="Memory the rows may take before the run continues on disk "
|
|
295
|
+
"(default 512).",
|
|
296
|
+
),
|
|
297
|
+
] = None,
|
|
298
|
+
temp_dir: Annotated[
|
|
299
|
+
Path | None,
|
|
300
|
+
typer.Option(
|
|
301
|
+
"--temp-dir",
|
|
302
|
+
help="Folder for the temporary store used when the budget is reached.",
|
|
303
|
+
),
|
|
304
|
+
] = None,
|
|
305
|
+
force: Annotated[
|
|
306
|
+
bool,
|
|
307
|
+
typer.Option(
|
|
308
|
+
"--force",
|
|
309
|
+
"-f",
|
|
310
|
+
help="Reprocess sources that are already processed and replace output "
|
|
311
|
+
"files that Dedup did not write. Never replaces a source.",
|
|
312
|
+
),
|
|
313
|
+
] = False,
|
|
314
|
+
verify_content: Annotated[
|
|
315
|
+
bool,
|
|
316
|
+
typer.Option(
|
|
317
|
+
"--verify-content",
|
|
318
|
+
help="Decide that a source is already processed from its SHA-256 and "
|
|
319
|
+
"that of its outputs, not from size and modification time. Cannot be "
|
|
320
|
+
"combined with --force.",
|
|
321
|
+
),
|
|
322
|
+
] = False,
|
|
323
|
+
quiet: Annotated[
|
|
324
|
+
bool,
|
|
325
|
+
typer.Option(
|
|
326
|
+
"--quiet",
|
|
327
|
+
help="Print only the final status, and the errors. Hides the progress.",
|
|
328
|
+
),
|
|
329
|
+
] = False,
|
|
330
|
+
verbose: Annotated[
|
|
331
|
+
bool,
|
|
332
|
+
typer.Option(
|
|
333
|
+
"--verbose",
|
|
334
|
+
help="Add the storage mode, the passes, the durations and the memory "
|
|
335
|
+
"peak, and list the excluded files. Measuring the memory peak "
|
|
336
|
+
"makes the first pass two to four times slower and doubles "
|
|
337
|
+
"the memory used.",
|
|
338
|
+
),
|
|
339
|
+
] = False,
|
|
340
|
+
no_progress: Annotated[
|
|
341
|
+
bool,
|
|
342
|
+
typer.Option(
|
|
343
|
+
"--no-progress",
|
|
344
|
+
help="Hide the progress line. It is also hidden outside a terminal.",
|
|
345
|
+
),
|
|
346
|
+
] = False,
|
|
347
|
+
) -> None:
|
|
348
|
+
"""Remove exact duplicate rows from CSV files without modifying them.
|
|
349
|
+
|
|
350
|
+
For each source, writes <name>.unique.csv (every distinct row once, at its first place), <name>.duplicates.csv (each repeated row once) and, when a row occurs three times or more, <name>.duplicates_all.csv (every occurrence of the repeated rows), plus a JSON report <name>.dedup.json. A source without duplicate gets its report only. A pattern or several inputs also write dedup.summary.json. Two rows are the same only when all their cells are identical: case, spaces and leading zeros count.
|
|
351
|
+
|
|
352
|
+
A source that is already processed and unchanged is skipped. A source that fails does not stop the others. Nothing is ever asked: outside a terminal the command behaves the same.
|
|
353
|
+
|
|
354
|
+
Exit codes: 0 nothing failed; 1 a failure that is not an input error, or an unwritable batch report; 2 invalid option or configuration; 4 a missing source or only input errors (invalid CSV, row limit, changed source).
|
|
355
|
+
"""
|
|
356
|
+
if quiet and no_progress:
|
|
357
|
+
raise typer.BadParameter(
|
|
358
|
+
"--no-progress is redundant with --quiet, which already hides the progress."
|
|
359
|
+
)
|
|
360
|
+
try:
|
|
361
|
+
settings = resolve_dedup_settings(
|
|
362
|
+
config,
|
|
363
|
+
delimiter=delimiter,
|
|
364
|
+
encoding=encoding,
|
|
365
|
+
max_rows=max_rows,
|
|
366
|
+
memory_budget_mb=memory_budget_mb,
|
|
367
|
+
temp_dir=temp_dir,
|
|
368
|
+
output_dir=output_dir,
|
|
369
|
+
force=force,
|
|
370
|
+
verify_content=verify_content,
|
|
371
|
+
quiet=quiet,
|
|
372
|
+
verbose=verbose,
|
|
373
|
+
)
|
|
374
|
+
batch = _run(inputs, settings, no_progress=no_progress)
|
|
375
|
+
except TabalystError as exc:
|
|
376
|
+
typer.echo(f"Error: {exc}", err=True)
|
|
377
|
+
raise typer.Exit(error_exit_code(exc)) from exc
|
|
378
|
+
|
|
379
|
+
for line in _error_lines(batch):
|
|
380
|
+
typer.echo(line, err=True)
|
|
381
|
+
if batch.batch_report_error is not None:
|
|
382
|
+
typer.echo(f"Error: {batch.batch_report_error}", err=True)
|
|
383
|
+
_print_summary(batch, settings)
|
|
384
|
+
code = exit_code(batch)
|
|
385
|
+
if code:
|
|
386
|
+
raise typer.Exit(code)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _run(
|
|
390
|
+
inputs: list[str], settings: DedupSettings, *, no_progress: bool
|
|
391
|
+
) -> BatchDedupResult:
|
|
392
|
+
show = not (settings.quiet or no_progress) and _is_terminal()
|
|
393
|
+
return execute_dedups(
|
|
394
|
+
[os.fspath(item) for item in inputs],
|
|
395
|
+
settings,
|
|
396
|
+
on_progress=_DedupProgress(enabled=show) if show else None,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _print_summary(batch: BatchDedupResult, settings: DedupSettings) -> None:
|
|
401
|
+
single = not batch.plan.writes_batch_report and len(batch.results) == 1
|
|
402
|
+
if single:
|
|
403
|
+
result = batch.results[0]
|
|
404
|
+
lines = _single_lines(result, verbose=settings.verbose)
|
|
405
|
+
status = lines[-1]
|
|
406
|
+
else:
|
|
407
|
+
lines = _batch_lines(batch, verbose=settings.verbose)
|
|
408
|
+
status = lines[next(i for i, line in enumerate(lines) if line.startswith("Result:"))]
|
|
409
|
+
for line in [status] if settings.quiet else lines:
|
|
410
|
+
typer.echo(line)
|