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.
Files changed (525) hide show
  1. {tabalyst-0.7.0/src/tabalyst.egg-info → tabalyst-0.7.1}/PKG-INFO +33 -2
  2. {tabalyst-0.7.0 → tabalyst-0.7.1}/README.md +32 -1
  3. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/README.md +5 -5
  4. {tabalyst-0.7.0 → tabalyst-0.7.1}/pyproject.toml +4 -2
  5. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/__init__.py +12 -0
  6. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/app.py +2 -0
  7. tabalyst-0.7.1/src/tabalyst/cli/dedup.py +410 -0
  8. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/terminal.py +8 -3
  9. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/config.py +29 -5
  10. tabalyst-0.7.1/src/tabalyst/csv_records.py +287 -0
  11. tabalyst-0.7.1/src/tabalyst/dedup/__init__.py +9 -0
  12. tabalyst-0.7.1/src/tabalyst/dedup/analysis.py +244 -0
  13. tabalyst-0.7.1/src/tabalyst/dedup/batch_report.py +252 -0
  14. tabalyst-0.7.1/src/tabalyst/dedup/cache.py +268 -0
  15. tabalyst-0.7.1/src/tabalyst/dedup/discovery.py +251 -0
  16. tabalyst-0.7.1/src/tabalyst/dedup/diskstore.py +291 -0
  17. tabalyst-0.7.1/src/tabalyst/dedup/failures.py +79 -0
  18. tabalyst-0.7.1/src/tabalyst/dedup/groups.py +260 -0
  19. tabalyst-0.7.1/src/tabalyst/dedup/identity.py +49 -0
  20. tabalyst-0.7.1/src/tabalyst/dedup/pipeline.py +209 -0
  21. tabalyst-0.7.1/src/tabalyst/dedup/publish.py +120 -0
  22. tabalyst-0.7.1/src/tabalyst/dedup/report.py +599 -0
  23. tabalyst-0.7.1/src/tabalyst/dedup/settings.py +113 -0
  24. tabalyst-0.7.1/src/tabalyst/dedup/store.py +124 -0
  25. tabalyst-0.7.1/src/tabalyst/dedup/tiered.py +121 -0
  26. tabalyst-0.7.1/src/tabalyst/dedup/writer.py +249 -0
  27. tabalyst-0.7.1/src/tabalyst/dedup_service.py +487 -0
  28. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_deep.py +4 -0
  29. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_relations.py +3 -0
  30. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_views.py +6 -0
  31. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/reporting.py +2 -4
  32. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/sampling.py +23 -81
  33. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/temporal.py +31 -1
  34. tabalyst-0.7.1/src/tabalyst/schemas/tabalyst.dedup.batch.schema.json +393 -0
  35. tabalyst-0.7.1/src/tabalyst/schemas/tabalyst.dedup.schema.json +714 -0
  36. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/static/theme.css +9 -0
  37. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/column.html +14 -16
  38. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/report.html +19 -19
  39. {tabalyst-0.7.0 → tabalyst-0.7.1/src/tabalyst.egg-info}/PKG-INFO +33 -2
  40. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/SOURCES.txt +22 -0
  41. tabalyst-0.7.1/tests/test_csv_records.py +218 -0
  42. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_public_api.py +27 -0
  43. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_sampling.py +60 -0
  44. {tabalyst-0.7.0 → tabalyst-0.7.1}/LICENSE +0 -0
  45. {tabalyst-0.7.0 → tabalyst-0.7.1}/MANIFEST.in +0 -0
  46. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/config.json +0 -0
  47. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/datasets/basic.csv +0 -0
  48. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/datasets/insurance-customers.csv +0 -0
  49. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/README.md +0 -0
  50. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/catalog.yaml +0 -0
  51. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/dataset.yaml +0 -0
  52. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/schemas/accounts.yaml +0 -0
  53. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/cohorts-anomalies/schemas/contacts.yaml +0 -0
  54. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/common-references/catalog.yaml +0 -0
  55. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/common-references/dataset.yaml +0 -0
  56. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/common-references/schemas/contacts.yaml +0 -0
  57. {tabalyst-0.7.0 → tabalyst-0.7.1}/examples/generate/generate_examples.py +0 -0
  58. {tabalyst-0.7.0 → tabalyst-0.7.1}/setup.cfg +0 -0
  59. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/__main__.py +0 -0
  60. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/_version.py +0 -0
  61. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/batch.py +0 -0
  62. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cache_service.py +0 -0
  63. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/__init__.py +0 -0
  64. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/cache.py +0 -0
  65. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/confirm.py +0 -0
  66. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/generate.py +0 -0
  67. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/inspect.py +0 -0
  68. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/report.py +0 -0
  69. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/sample.py +0 -0
  70. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/cli/scan.py +0 -0
  71. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/deep_service.py +0 -0
  72. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/deep_store.py +0 -0
  73. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/errors.py +0 -0
  74. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/execution_log.py +0 -0
  75. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_anomalies.py +0 -0
  76. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_columns_package.py +0 -0
  77. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_computer.py +0 -0
  78. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_computer_output.py +0 -0
  79. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_csv.py +0 -0
  80. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_definition.py +0 -0
  81. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/SOURCES.md +0 -0
  82. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/catalog.yaml +0 -0
  83. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/brand.csv +0 -0
  84. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/color.csv +0 -0
  85. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/computer_type.csv +0 -0
  86. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/computer_type_form_factor.csv +0 -0
  87. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/computer_type_product_segment.csv +0 -0
  88. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/cpu.csv +0 -0
  89. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/cpu_memory_weight.csv +0 -0
  90. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/cpu_operating_system.csv +0 -0
  91. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/display.csv +0 -0
  92. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/extended_specs.csv +0 -0
  93. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/form_factor.csv +0 -0
  94. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_color.csv +0 -0
  95. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_cpu.csv +0 -0
  96. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_display.csv +0 -0
  97. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_gpu.csv +0 -0
  98. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_memory.csv +0 -0
  99. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_operating_system.csv +0 -0
  100. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_profile.csv +0 -0
  101. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_profile_computer_type.csv +0 -0
  102. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_profile_segment.csv +0 -0
  103. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/generation_storage.csv +0 -0
  104. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu.csv +0 -0
  105. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu_display.csv +0 -0
  106. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu_memory_weight.csv +0 -0
  107. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/gpu_storage_weight.csv +0 -0
  108. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/memory.csv +0 -0
  109. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/model_form_factor.csv +0 -0
  110. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/model_generation.csv +0 -0
  111. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/operating_system.csv +0 -0
  112. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_category.csv +0 -0
  113. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_family.csv +0 -0
  114. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_family_computer_type.csv +0 -0
  115. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_family_product_segment.csv +0 -0
  116. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_model.csv +0 -0
  117. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment.csv +0 -0
  118. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_brand.csv +0 -0
  119. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_display_weight.csv +0 -0
  120. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_memory_weight.csv +0 -0
  121. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_segment_storage_weight.csv +0 -0
  122. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/product_subcategory.csv +0 -0
  123. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/profile_exclusions.csv +0 -0
  124. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/promotion_status.csv +0 -0
  125. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/promotion_type.csv +0 -0
  126. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/seller.csv +0 -0
  127. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/seller_type.csv +0 -0
  128. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/shipping_availability.csv +0 -0
  129. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/standard_specs.csv +0 -0
  130. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/storage.csv +0 -0
  131. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/data/warehouse.csv +0 -0
  132. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/dataset.yaml +0 -0
  133. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computer_type.yaml +0 -0
  134. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computer_type_form_factor.yaml +0 -0
  135. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computer_type_product_segment.yaml +0 -0
  136. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/computers.yaml +0 -0
  137. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/cpu.yaml +0 -0
  138. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/cpu_memory_weight.yaml +0 -0
  139. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/cpu_operating_system.yaml +0 -0
  140. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/display.yaml +0 -0
  141. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/extended_specs.yaml +0 -0
  142. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_color.yaml +0 -0
  143. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_cpu.yaml +0 -0
  144. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_display.yaml +0 -0
  145. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_gpu.yaml +0 -0
  146. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_memory.yaml +0 -0
  147. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_operating_system.yaml +0 -0
  148. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_profile.yaml +0 -0
  149. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_profile_computer_type.yaml +0 -0
  150. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_profile_segment.yaml +0 -0
  151. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/generation_storage.yaml +0 -0
  152. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu.yaml +0 -0
  153. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu_display.yaml +0 -0
  154. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu_memory_weight.yaml +0 -0
  155. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/gpu_storage_weight.yaml +0 -0
  156. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/localized.yaml +0 -0
  157. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/memory.yaml +0 -0
  158. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/model_form_factor.yaml +0 -0
  159. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/model_generation.yaml +0 -0
  160. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/operating_system.yaml +0 -0
  161. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_family.yaml +0 -0
  162. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_family_computer_type.yaml +0 -0
  163. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_family_product_segment.yaml +0 -0
  164. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_model.yaml +0 -0
  165. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment.yaml +0 -0
  166. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_brand.yaml +0 -0
  167. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_display_weight.yaml +0 -0
  168. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_memory_weight.yaml +0 -0
  169. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_segment_storage_weight.yaml +0 -0
  170. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/product_subcategory.yaml +0 -0
  171. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/profile_exclusions.yaml +0 -0
  172. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/seller.yaml +0 -0
  173. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/standard_specs.yaml +0 -0
  174. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/storage.yaml +0 -0
  175. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/computer/schemas/warehouse.yaml +0 -0
  176. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/catalog.yaml +0 -0
  177. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/dataset.yaml +0 -0
  178. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/schemas/accounts.yaml +0 -0
  179. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/crm/schemas/contacts.yaml +0 -0
  180. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/SOURCES.md +0 -0
  181. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/catalog.yaml +0 -0
  182. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/agencies.csv +0 -0
  183. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/branch_contract_weights.csv +0 -0
  184. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/claim_statuses.csv +0 -0
  185. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/contract_statuses.csv +0 -0
  186. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/contract_types.csv +0 -0
  187. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/payment_frequencies.csv +0 -0
  188. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/data/payment_methods.csv +0 -0
  189. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/dataset.yaml +0 -0
  190. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/agencies.yaml +0 -0
  191. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/branch_contract_weights.yaml +0 -0
  192. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/claim_statuses.yaml +0 -0
  193. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/contract_statuses.yaml +0 -0
  194. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/contract_types.yaml +0 -0
  195. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/customers.yaml +0 -0
  196. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/payment_frequencies.yaml +0 -0
  197. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_examples/insurance/schemas/payment_methods.yaml +0 -0
  198. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_insurance.py +0 -0
  199. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_package.py +0 -0
  200. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_references.py +0 -0
  201. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/SOURCES.md +0 -0
  202. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/street_directions_ca.csv +0 -0
  203. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/street_names_ca.csv +0 -0
  204. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/street_types_ca.csv +0 -0
  205. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/addresses/unit_types_ca.csv +0 -0
  206. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/contact/email_patterns.csv +0 -0
  207. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/contact/email_providers_ca.csv +0 -0
  208. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/contact/phone_area_codes_ca.csv +0 -0
  209. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/geography/cities_ca.csv +0 -0
  210. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/geography/postal_prefixes_ca.csv +0 -0
  211. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/geography/provinces_ca.csv +0 -0
  212. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/people/family_names_ca.csv +0 -0
  213. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/people/first_names_en_ca.csv +0 -0
  214. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_resources/people/first_names_fr_qc.csv +0 -0
  215. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_selection.py +0 -0
  216. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_service.py +0 -0
  217. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/generate_values.py +0 -0
  218. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspect_service.py +0 -0
  219. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/__init__.py +0 -0
  220. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/choices.py +0 -0
  221. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/__init__.py +0 -0
  222. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/build.py +0 -0
  223. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/detect.py +0 -0
  224. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/excel_inspect/parameters.py +0 -0
  225. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/__init__.py +0 -0
  226. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/build.py +0 -0
  227. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/detect.py +0 -0
  228. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/json_inspect/parameters.py +0 -0
  229. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/models.py +0 -0
  230. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/persistence.py +0 -0
  231. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/inspector/resolution.py +0 -0
  232. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/json_text.py +0 -0
  233. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/models.py +0 -0
  234. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/option_notices.py +0 -0
  235. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/plan_service.py +0 -0
  236. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/progress.py +0 -0
  237. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/project_open_service.py +0 -0
  238. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/project_scan_service.py +0 -0
  239. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/__init__.py +0 -0
  240. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_bounded_values.py +0 -0
  241. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_cache.py +0 -0
  242. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_codec.py +0 -0
  243. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_generation.py +0 -0
  244. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_locks.py +0 -0
  245. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_publication.py +0 -0
  246. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_queries.py +0 -0
  247. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_query_budget.py +0 -0
  248. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_recovery.py +0 -0
  249. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_retention.py +0 -0
  250. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_schema.py +0 -0
  251. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_session.py +0 -0
  252. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_staging.py +0 -0
  253. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_sync.py +0 -0
  254. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/_validation.py +0 -0
  255. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/catalog.py +0 -0
  256. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/freshness.py +0 -0
  257. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/identity.py +0 -0
  258. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/index.py +0 -0
  259. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/location.py +0 -0
  260. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/models.py +0 -0
  261. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/projects/store.py +0 -0
  262. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_chain.py +0 -0
  263. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_classification.py +0 -0
  264. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_column_page.py +0 -0
  265. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_config.py +0 -0
  266. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_details.py +0 -0
  267. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_finding_details.py +0 -0
  268. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_findings.py +0 -0
  269. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_insights.py +0 -0
  270. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_labels.py +0 -0
  271. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_measures.py +0 -0
  272. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_overview.py +0 -0
  273. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_plan.py +0 -0
  274. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_preview.py +0 -0
  275. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_profile.py +0 -0
  276. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_schema.py +0 -0
  277. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_sections.py +0 -0
  278. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_service.py +0 -0
  279. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_signals.py +0 -0
  280. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_summary.py +0 -0
  281. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/report_table.py +0 -0
  282. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/result_model.py +0 -0
  283. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/sample_page.py +0 -0
  284. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/sampling_service.py +0 -0
  285. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_document.py +0 -0
  286. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_page.py +0 -0
  287. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_reuse.py +0 -0
  288. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scan_service.py +0 -0
  289. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/__init__.py +0 -0
  290. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/api.py +0 -0
  291. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/bottomk.py +0 -0
  292. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/cases.py +0 -0
  293. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/charactermap.py +0 -0
  294. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/characters.py +0 -0
  295. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/__init__.py +0 -0
  296. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/candidates.py +0 -0
  297. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/catalog.py +0 -0
  298. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/identifiers.py +0 -0
  299. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/models.py +0 -0
  300. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/names.py +0 -0
  301. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/classify/rules.py +0 -0
  302. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/config.py +0 -0
  303. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/convention_rule.py +0 -0
  304. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/correlation_ratio.py +0 -0
  305. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/crosstab.py +0 -0
  306. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/__init__.py +0 -0
  307. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/amount.py +0 -0
  308. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/base.py +0 -0
  309. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/boolean.py +0 -0
  310. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/currency.py +0 -0
  311. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/email.py +0 -0
  312. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/enumeration.py +0 -0
  313. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/ip.py +0 -0
  314. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/keys.py +0 -0
  315. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/names.py +0 -0
  316. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/number.py +0 -0
  317. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/pattern.py +0 -0
  318. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/percentage.py +0 -0
  319. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/phone.py +0 -0
  320. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/postal.py +0 -0
  321. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/quantity.py +0 -0
  322. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/registry.py +0 -0
  323. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/shape.py +0 -0
  324. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/url.py +0 -0
  325. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/detectors/uuid.py +0 -0
  326. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/diagnostics.py +0 -0
  327. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/engine.py +0 -0
  328. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/exposure.py +0 -0
  329. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/features.py +0 -0
  330. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/field.py +0 -0
  331. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/identity.py +0 -0
  332. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/lengthsample.py +0 -0
  333. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/longtext.py +0 -0
  334. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/measures.py +0 -0
  335. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/memory.py +0 -0
  336. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/models.py +0 -0
  337. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/normalization.py +0 -0
  338. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/observations.py +0 -0
  339. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/paths.py +0 -0
  340. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/pattern_eligibility.py +0 -0
  341. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/patternfacets.py +0 -0
  342. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/patterninfer.py +0 -0
  343. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/patterntree.py +0 -0
  344. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/pearson.py +0 -0
  345. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/__init__.py +0 -0
  346. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzer.py +0 -0
  347. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/__init__.py +0 -0
  348. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/characters.py +0 -0
  349. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/common.py +0 -0
  350. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/crosstab.py +0 -0
  351. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/date_order.py +0 -0
  352. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/distinct.py +0 -0
  353. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/drift.py +0 -0
  354. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/duplicates.py +0 -0
  355. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/forms.py +0 -0
  356. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/frequencies.py +0 -0
  357. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/histogram.py +0 -0
  358. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/lengths.py +0 -0
  359. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/matrix.py +0 -0
  360. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/measures.py +0 -0
  361. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/parts.py +0 -0
  362. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/patterns.py +0 -0
  363. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/pearson.py +0 -0
  364. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/residuals.py +0 -0
  365. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/shape.py +0 -0
  366. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/stats.py +0 -0
  367. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/structure.py +0 -0
  368. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/temporal.py +0 -0
  369. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/analyzers/texts.py +0 -0
  370. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/categorystats.py +0 -0
  371. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/costmodel.py +0 -0
  372. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/crosstabs.py +0 -0
  373. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/facts.py +0 -0
  374. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/identifiers.py +0 -0
  375. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/models.py +0 -0
  376. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/orders.py +0 -0
  377. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/planner.py +0 -0
  378. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/reasons.py +0 -0
  379. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/registry.py +0 -0
  380. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/relations.py +0 -0
  381. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/render.py +0 -0
  382. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/samplerows.py +0 -0
  383. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/secondpass.py +0 -0
  384. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/stratification.py +0 -0
  385. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/planner/stratmodels.py +0 -0
  386. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/process.py +0 -0
  387. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/quality.py +0 -0
  388. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/rankcorr.py +0 -0
  389. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/__init__.py +0 -0
  390. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/base.py +0 -0
  391. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/csv_reader.py +0 -0
  392. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/excel_common.py +0 -0
  393. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/excel_reader.py +0 -0
  394. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/excel_table.py +0 -0
  395. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/factory.py +0 -0
  396. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/json_common.py +0 -0
  397. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/json_reader.py +0 -0
  398. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/readers/jsonl_reader.py +0 -0
  399. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/records.py +0 -0
  400. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/rowsample.py +0 -0
  401. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/schema.py +0 -0
  402. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/__init__.py +0 -0
  403. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/accumulators.py +0 -0
  404. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/bins.py +0 -0
  405. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/boundaries.py +0 -0
  406. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/characters.py +0 -0
  407. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/crosstab.py +0 -0
  408. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/document.py +0 -0
  409. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/drift.py +0 -0
  410. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/duplicates.py +0 -0
  411. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/exactset.py +0 -0
  412. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/executor.py +0 -0
  413. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/forms.py +0 -0
  414. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/frequencies.py +0 -0
  415. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/matrix.py +0 -0
  416. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/measures.py +0 -0
  417. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/models.py +0 -0
  418. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/orders.py +0 -0
  419. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/parallel.py +0 -0
  420. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/parts.py +0 -0
  421. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/patternreader.py +0 -0
  422. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/patterns.py +0 -0
  423. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/pearson.py +0 -0
  424. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/periods.py +0 -0
  425. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/ranks.py +0 -0
  426. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/replay.py +0 -0
  427. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/residuals.py +0 -0
  428. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/routing.py +0 -0
  429. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/schema.py +0 -0
  430. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/shape.py +0 -0
  431. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/stats.py +0 -0
  432. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/structure.py +0 -0
  433. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/secondpass/texts.py +0 -0
  434. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/__init__.py +0 -0
  435. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/bins.py +0 -0
  436. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/estimate.py +0 -0
  437. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/evaluate.py +0 -0
  438. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/execute.py +0 -0
  439. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/models.py +0 -0
  440. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/near.py +0 -0
  441. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/plan.py +0 -0
  442. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/reasons.py +0 -0
  443. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/render.py +0 -0
  444. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/result.py +0 -0
  445. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/select.py +0 -0
  446. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/targets.py +0 -0
  447. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/smart/weights.py +0 -0
  448. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/stratify.py +0 -0
  449. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/structure.py +0 -0
  450. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/technical.py +0 -0
  451. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/validation.py +0 -0
  452. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/values.py +0 -0
  453. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/valuesample.py +0 -0
  454. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/scanner/workers.py +0 -0
  455. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.report.schema.json +0 -0
  456. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.sample.schema.json +0 -0
  457. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.scan.deep.schema.json +0 -0
  458. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/schemas/tabalyst.scan.schema.json +0 -0
  459. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/service.py +0 -0
  460. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/shared_scan_service.py +0 -0
  461. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/smart_service.py +0 -0
  462. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/source_guard.py +0 -0
  463. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/static/report.js +0 -0
  464. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/summary_models.py +0 -0
  465. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/characters.html +0 -0
  466. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/insights.html +0 -0
  467. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/patterns.html +0 -0
  468. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst/templates/sample.html +0 -0
  469. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/dependency_links.txt +0 -0
  470. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/entry_points.txt +0 -0
  471. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/requires.txt +0 -0
  472. {tabalyst-0.7.0 → tabalyst-0.7.1}/src/tabalyst.egg-info/top_level.txt +0 -0
  473. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_all_collections.py +0 -0
  474. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_cli.py +0 -0
  475. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_examples_index.py +0 -0
  476. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_examples_layout.py +0 -0
  477. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate.py +0 -0
  478. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_anomalies.py +0 -0
  479. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_anomalies_regression.py +0 -0
  480. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_catalog.py +0 -0
  481. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_components.py +0 -0
  482. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_derivations.py +0 -0
  483. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_hierarchy.py +0 -0
  484. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_output.py +0 -0
  485. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_computer_service.py +0 -0
  486. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_crm.py +0 -0
  487. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_crm_package.py +0 -0
  488. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_insurance.py +0 -0
  489. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_insurance_stress.py +0 -0
  490. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_lot4.py +0 -0
  491. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_package.py +0 -0
  492. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_references.py +0 -0
  493. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_generate_selection.py +0 -0
  494. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_json_text.py +0 -0
  495. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_license_headers.py +0 -0
  496. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_make_datasets.py +0 -0
  497. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_option_notices.py +0 -0
  498. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_cache.py +0 -0
  499. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_database.py +0 -0
  500. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_generations.py +0 -0
  501. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_identity.py +0 -0
  502. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_index.py +0 -0
  503. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_location.py +0 -0
  504. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_open_service.py +0 -0
  505. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_queries.py +0 -0
  506. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_rescan.py +0 -0
  507. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_scan.py +0 -0
  508. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_scan_model.py +0 -0
  509. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_sessions.py +0 -0
  510. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_project_store.py +0 -0
  511. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_report_contrast.py +0 -0
  512. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_reporting.py +0 -0
  513. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scan_command.py +0 -0
  514. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_amounts.py +0 -0
  515. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_batches.py +0 -0
  516. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_config.py +0 -0
  517. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_csv.py +0 -0
  518. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_email_url.py +0 -0
  519. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_exposure.py +0 -0
  520. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_paths.py +0 -0
  521. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_phone.py +0 -0
  522. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_postal.py +0 -0
  523. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_scanner_uuid_ip.py +0 -0
  524. {tabalyst-0.7.0 → tabalyst-0.7.1}/tests/test_shared_scan_cli.py +0 -0
  525. {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.0
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. An Excel workbook is read by sheet into
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. An Excel workbook is read by sheet into
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-scan/ (scan, second pass, smart plan and sample; 100,000 rows)
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-scan/` | `scan` | no | `get-started/simple-clear-smart.md` |
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-scan/` shows the three steps of Tabalyst Scan on
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-scan/insurance-customers.scan.json --config examples/config.json --deep --yes --force
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-scan/insurance-customers.sample.csv --config examples/config.json --force
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.0"
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
- "shipped_sections: the test reads the sections the shipped report writes (some are hidden for now), not every section the suite writes",
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)