tabalyst 0.4.3__tar.gz → 0.5.0__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.4.3/src/tabalyst.egg-info → tabalyst-0.5.0}/PKG-INFO +94 -27
- {tabalyst-0.4.3 → tabalyst-0.5.0}/README.md +93 -26
- {tabalyst-0.4.3 → tabalyst-0.5.0}/examples/README.md +20 -6
- {tabalyst-0.4.3 → tabalyst-0.5.0}/pyproject.toml +2 -1
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/__init__.py +6 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/batch.py +24 -2
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/app.py +9 -2
- tabalyst-0.5.0/src/tabalyst/cli/inspect.py +161 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/report.py +8 -0
- tabalyst-0.5.0/src/tabalyst/inspect_service.py +209 -0
- tabalyst-0.5.0/src/tabalyst/inspector/__init__.py +5 -0
- tabalyst-0.5.0/src/tabalyst/inspector/json_inspect/__init__.py +10 -0
- tabalyst-0.5.0/src/tabalyst/inspector/json_inspect/build.py +314 -0
- tabalyst-0.5.0/src/tabalyst/inspector/json_inspect/detect.py +352 -0
- tabalyst-0.5.0/src/tabalyst/inspector/json_inspect/parameters.py +16 -0
- tabalyst-0.5.0/src/tabalyst/inspector/models.py +283 -0
- tabalyst-0.5.0/src/tabalyst/inspector/persistence.py +349 -0
- tabalyst-0.5.0/src/tabalyst/inspector/resolution.py +386 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/models.py +44 -6
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/progress.py +30 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/project_open_service.py +6 -2
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_session.py +27 -14
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_staging.py +11 -4
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/freshness.py +16 -3
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/location.py +15 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/report_profile.py +55 -5
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/report_service.py +9 -3
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/reporting.py +53 -2
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scan_reuse.py +41 -12
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scan_service.py +36 -22
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/api.py +15 -30
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/config.py +99 -4
- tabalyst-0.5.0/src/tabalyst/scanner/identity.py +56 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/models.py +2 -2
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/paths.py +24 -13
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/readers/base.py +1 -1
- tabalyst-0.5.0/src/tabalyst/scanner/readers/json_common.py +37 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/readers/json_reader.py +65 -65
- tabalyst-0.5.0/src/tabalyst/scanner/readers/jsonl_reader.py +431 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/structure.py +1 -1
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/service.py +128 -22
- tabalyst-0.5.0/src/tabalyst/shared_scan_service.py +91 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/static/theme.css +17 -0
- tabalyst-0.5.0/src/tabalyst/templates/column.html +66 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/templates/report.html +4 -4
- {tabalyst-0.4.3 → tabalyst-0.5.0/src/tabalyst.egg-info}/PKG-INFO +94 -27
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst.egg-info/SOURCES.txt +14 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_cli.py +2 -2
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_database.py +9 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_open_service.py +4 -1
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_queries.py +2 -1
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_rescan.py +8 -3
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_sessions.py +5 -3
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_public_api.py +22 -0
- tabalyst-0.5.0/tests/test_reporting.py +296 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scan_command.py +6 -13
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_config.py +6 -1
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_shared_scan_cli.py +33 -24
- tabalyst-0.4.3/src/tabalyst/shared_scan_service.py +0 -149
- tabalyst-0.4.3/tests/test_reporting.py +0 -137
- {tabalyst-0.4.3 → tabalyst-0.5.0}/LICENSE +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/MANIFEST.in +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/examples/config.json +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/examples/input/basic.csv +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/examples/input/insurance-customers.csv +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/setup.cfg +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/__main__.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/_version.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cache_service.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/__init__.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/cache.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/sample.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/scan.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/cli/terminal.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/config.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/errors.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/execution_log.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/project_scan_service.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/__init__.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_bounded_values.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_cache.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_codec.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_generation.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_locks.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_publication.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_queries.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_query_budget.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_recovery.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_retention.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_schema.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_sync.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/_validation.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/catalog.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/identity.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/index.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/models.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/projects/store.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/report_config.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/sampling.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/sampling_service.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/__init__.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/__init__.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/amount.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/base.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/boolean.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/currency.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/email.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/enumeration.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/ip.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/names.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/number.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/pattern.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/percentage.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/phone.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/postal.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/quantity.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/registry.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/shape.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/temporal.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/url.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/detectors/uuid.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/diagnostics.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/engine.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/exposure.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/field.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/measures.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/normalization.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/observations.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/readers/__init__.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/readers/csv_reader.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/records.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/technical.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/values.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/scanner/workers.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst/static/report.js +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst.egg-info/dependency_links.txt +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst.egg-info/entry_points.txt +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst.egg-info/requires.txt +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/src/tabalyst.egg-info/top_level.txt +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_cache.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_generations.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_identity.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_index.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_location.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_scan.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_project_store.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_sampling.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_amounts.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_batches.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_csv.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_email_url.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_exposure.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_paths.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_phone.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_postal.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_scanner_uuid_ip.py +0 -0
- {tabalyst-0.4.3 → tabalyst-0.5.0}/tests/test_source_freshness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabalyst
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Open-source toolkit for understanding and working with unfamiliar data.
|
|
5
5
|
Author: Gregory Borelli
|
|
6
6
|
License-Expression: MIT
|
|
@@ -44,7 +44,8 @@ with structured data.
|
|
|
44
44
|
Its first tool is **Tabalyst Report**. The current CSV implementation,
|
|
45
45
|
**Tabalyst CSV Report**, analyzes a CSV file and produces both a structured JSON
|
|
46
46
|
profile and a self-contained interactive HTML report.
|
|
47
|
-
**Tabalyst Scan** describes CSV and
|
|
47
|
+
**Tabalyst Scan** describes CSV, JSON and JSONL files in a scan document,
|
|
48
|
+
**Tabalyst Inspect** finds how to read a JSON or JSONL file, and
|
|
48
49
|
**Tabalyst Sample** creates smaller CSV files.
|
|
49
50
|
|
|
50
51
|
Tabalyst is in beta. Its interfaces may still change while
|
|
@@ -70,6 +71,7 @@ changes often: update it before each new test.
|
|
|
70
71
|
| Several files, automatic names | `tabalyst report *.csv` | Beside each source |
|
|
71
72
|
| Several files, one directory | `tabalyst report *.csv -d reports/` | The directory `reports/` |
|
|
72
73
|
| One JSON file | `tabalyst report data.json` | `data.report.html` beside the source |
|
|
74
|
+
| One JSONL file | `tabalyst report events.jsonl` | `events.report.html` beside the source |
|
|
73
75
|
| From a scan document | `tabalyst report --scan data.scan.json` | `data.html` beside the scan |
|
|
74
76
|
|
|
75
77
|
The simplest command keeps the source filename:
|
|
@@ -87,6 +89,20 @@ customers.json
|
|
|
87
89
|
executions.json
|
|
88
90
|
```
|
|
89
91
|
|
|
92
|
+
Add `--details` to generate one self-contained HTML page per column:
|
|
93
|
+
|
|
94
|
+
```console
|
|
95
|
+
tabalyst report customers.csv --details
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The **Columns** table then links to those pages. Each page has its own sidebar
|
|
99
|
+
and shows values, counts, detected formats and other analysis directly. The
|
|
100
|
+
subfolder takes the HTML filename without `.html`; for `-o report.html`, pages
|
|
101
|
+
are in `report/`. Names use `col-01-` and a shortened slug of the column name,
|
|
102
|
+
in source order. Details are off by default. `--no-details` states that choice
|
|
103
|
+
explicitly; with `--force`, it removes prior generated column pages while
|
|
104
|
+
preserving other files in the folder.
|
|
105
|
+
|
|
90
106
|
Use `-o` to choose a different HTML filename for one source:
|
|
91
107
|
|
|
92
108
|
```console
|
|
@@ -95,16 +111,23 @@ tabalyst report customers.csv -o customer-analysis.html
|
|
|
95
111
|
|
|
96
112
|
### JSON files
|
|
97
113
|
|
|
98
|
-
A JSON file gets a report too, named `<stem>.report.html` so its
|
|
99
|
-
`<stem>.report.json` never replaces the source.
|
|
100
|
-
such as the `customers` array of
|
|
101
|
-
|
|
102
|
-
`address.city`:
|
|
114
|
+
A JSON or JSONL file gets a report too, named `<stem>.report.html` so its
|
|
115
|
+
profile `<stem>.report.json` never replaces the source. The report analyzes one
|
|
116
|
+
collection of records, such as the `customers` array of
|
|
117
|
+
`{"customers": [...]}`, chosen by Tabalyst Inspect, and nested fields are
|
|
118
|
+
columns named by their path, such as `address.city`:
|
|
103
119
|
|
|
104
120
|
```console
|
|
105
121
|
tabalyst report orders.json
|
|
122
|
+
tabalyst report events.jsonl
|
|
106
123
|
```
|
|
107
124
|
|
|
125
|
+
In a JSONL file (`.jsonl` or `.ndjson`), each line is a record. A line that is
|
|
126
|
+
not valid JSON or not an object is excluded and counted, and the report is
|
|
127
|
+
partial. When a JSON file holds several arrays that are equally plausible,
|
|
128
|
+
Tabalyst stops and lists them instead of choosing: see
|
|
129
|
+
[Tabalyst Inspect](#tabalyst-inspect).
|
|
130
|
+
|
|
108
131
|
### Multiple files
|
|
109
132
|
|
|
110
133
|
Report several CSV files at once:
|
|
@@ -157,8 +180,10 @@ instead.
|
|
|
157
180
|
|
|
158
181
|
### Safe batch behavior
|
|
159
182
|
|
|
160
|
-
Before processing begins, Tabalyst resolves every input and planned
|
|
161
|
-
stops the entire batch if
|
|
183
|
+
Before processing begins, Tabalyst resolves every input and planned main HTML
|
|
184
|
+
and JSON output. It stops the entire batch if those names collide or if an
|
|
185
|
+
output already exists. With `--details`, each column page is checked after its
|
|
186
|
+
profile has been built, before that report's files are written.
|
|
162
187
|
Use `--force` only when replacing all matching report artifacts is intentional:
|
|
163
188
|
|
|
164
189
|
```console
|
|
@@ -220,18 +245,48 @@ Sampling reads CSV records as a stream. Random and stratified sampling keep
|
|
|
220
245
|
only the requested sample, plus stratum counts, in memory. Existing outputs
|
|
221
246
|
require `--force`, and an input file is never overwritten.
|
|
222
247
|
|
|
248
|
+
## Tabalyst Inspect
|
|
249
|
+
|
|
250
|
+
A JSON file can hold several arrays. **Tabalyst Inspect** reads a JSON, JSONL
|
|
251
|
+
or NDJSON file once, finds which array holds the records and writes its answer
|
|
252
|
+
in `<source>-inspect.json` beside the source:
|
|
253
|
+
|
|
254
|
+
```console
|
|
255
|
+
tabalyst inspect orders.json
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
Inspect: orders.json-inspect.json
|
|
260
|
+
Selection: $.customers[] (the only eligible collection)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The last section of that file, `config`, holds the rules that `tabalyst scan`
|
|
264
|
+
and `tabalyst report` apply to the source: the collection
|
|
265
|
+
(`structure.dataset_path`), the flatten settings, the array mode and the error
|
|
266
|
+
policy. Edit it, then scan or report as usual. Running `tabalyst inspect` again
|
|
267
|
+
refreshes the detection and keeps your `config`; `--reset-config` replaces it.
|
|
268
|
+
|
|
269
|
+
Inspect is optional: `tabalyst scan` and `tabalyst report` inspect a JSON file
|
|
270
|
+
themselves when it has no Inspect file. When several arrays are equally
|
|
271
|
+
plausible, or none holds objects, they stop with exit code `2` before analyzing
|
|
272
|
+
anything and list the candidates; set `config.structure.dataset_path` in the
|
|
273
|
+
Inspect file, or pass `--collection`. See
|
|
274
|
+
[Inspect JSON and JSONL files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/inspect-json-files.md)
|
|
275
|
+
and the [Inspect format](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/inspect-format.md).
|
|
276
|
+
|
|
223
277
|
## Tabalyst Scan
|
|
224
278
|
|
|
225
|
-
Describe every field of a CSV or
|
|
279
|
+
Describe every field of a CSV, JSON or JSONL file in one JSON document:
|
|
226
280
|
|
|
227
281
|
```console
|
|
228
282
|
tabalyst scan customers.csv
|
|
229
283
|
tabalyst scan orders.json --collection "$.customers[]"
|
|
284
|
+
tabalyst scan events.jsonl
|
|
230
285
|
```
|
|
231
286
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
287
|
+
Without `-o` or `-d`, the scan is a reusable `scan.json` under Tabalyst's local
|
|
288
|
+
storage directory (or `TABALYST_HOME`). The default workflow does not build a
|
|
289
|
+
DuckDB database. `-o` or `-d` writes
|
|
235
290
|
a standalone `<stem>.scan.json` export instead. Tabalyst Scan reads the file once as
|
|
236
291
|
a stream, with memory bounded by configurable limits, and records for each
|
|
237
292
|
field its presence, native types, missing values, frequencies, exact
|
|
@@ -244,12 +299,12 @@ interrupted scan never leaves a partial file. Files of 16 MiB or more are
|
|
|
244
299
|
analyzed by several worker processes, with the same result; `--workers`
|
|
245
300
|
chooses their number, `--workers 1` keeps one process.
|
|
246
301
|
|
|
247
|
-
`tabalyst report customers.csv` reuses the verified
|
|
248
|
-
content and requested scan settings are current
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
302
|
+
`tabalyst report customers.csv` reuses the verified stored scan when the
|
|
303
|
+
source content and requested scan settings are current, and so does a report on
|
|
304
|
+
a JSON or JSONL file. It creates a scan when none exists and atomically replaces
|
|
305
|
+
a stale one. The source check uses the SHA-256 of the whole content, and a scan
|
|
306
|
+
written by another version of Tabalyst is replaced. Existing DuckDB projects from 0.4.3 are left
|
|
307
|
+
untouched.
|
|
253
308
|
|
|
254
309
|
Build the report from a standalone scan document without reading the source again:
|
|
255
310
|
|
|
@@ -260,8 +315,8 @@ tabalyst report --scan customers.scan.json
|
|
|
260
315
|
Tabalyst refuses a scan whose source changed since it was written, or whose
|
|
261
316
|
settings differ from the `scan` settings of `--config`.
|
|
262
317
|
|
|
263
|
-
Inspect or clean disposable
|
|
264
|
-
CSV file:
|
|
318
|
+
Inspect or clean disposable query caches of existing DuckDB projects, for every
|
|
319
|
+
project or for one CSV file:
|
|
265
320
|
|
|
266
321
|
```console
|
|
267
322
|
tabalyst cache info
|
|
@@ -270,7 +325,8 @@ tabalyst cache clean
|
|
|
270
325
|
tabalyst cache clean customers.csv
|
|
271
326
|
```
|
|
272
327
|
|
|
273
|
-
`cache clean` leaves project scans and databases in place.
|
|
328
|
+
`cache clean` leaves project scans and databases in place. Ordinary scans and
|
|
329
|
+
reports do not create these query caches.
|
|
274
330
|
|
|
275
331
|
See [Scan CSV and JSON files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/scan-files.md)
|
|
276
332
|
and the [scan format](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/scan-format.md).
|
|
@@ -320,14 +376,21 @@ sample = tabalyst.sample_csv(
|
|
|
320
376
|
|
|
321
377
|
scan = tabalyst.scan("orders.json")
|
|
322
378
|
scans = tabalyst.generate_scans(["data/*.json"], output_dir="scans")
|
|
379
|
+
|
|
380
|
+
inspection = tabalyst.inspect("orders.json")
|
|
381
|
+
inspections = tabalyst.generate_inspections(["data/*.json", "logs/*.jsonl"])
|
|
323
382
|
reports = tabalyst.generate_reports(["scans/*.scan.json"], from_scan=True)
|
|
324
383
|
```
|
|
325
384
|
|
|
326
385
|
`analyze()` returns the JSON-serializable profile for one report.
|
|
327
386
|
`generate_reports()` returns the complete batch plan, successes, and failures.
|
|
328
|
-
`scan()` returns a scan result without writing anything
|
|
329
|
-
|
|
330
|
-
`
|
|
387
|
+
`scan()` returns a scan result without writing anything, at the level of the
|
|
388
|
+
engine: it does not read an Inspect file. `generate_scans()` writes one
|
|
389
|
+
standalone `.scan.json` document per source by default, and applies Inspect to
|
|
390
|
+
JSON and JSONL sources. Pass `project_storage=True` to use the command's
|
|
391
|
+
scan-only storage. `inspect()` writes the Inspect file of one JSON or JSONL
|
|
392
|
+
source and returns its path and document; `generate_inspections()` does it for
|
|
393
|
+
several sources. `from_scan=True`
|
|
331
394
|
builds reports from standalone scan documents.
|
|
332
395
|
Expected failures derive from `tabalyst.TabalystError`.
|
|
333
396
|
|
|
@@ -351,7 +414,8 @@ Configuration files are strict JSON. A minimal file is:
|
|
|
351
414
|
```
|
|
352
415
|
|
|
353
416
|
Analysis settings, CSV reading included, go in the `scan` object, shared by
|
|
354
|
-
`tabalyst report` and `tabalyst
|
|
417
|
+
`tabalyst report`, `tabalyst scan` and `tabalyst inspect`. The `config` of an
|
|
418
|
+
Inspect file outranks the `scan` object for the source it sits beside. Top-level settings shape the report
|
|
355
419
|
presentation, and `csv` configures `tabalyst sample`. Explicit CLI or Python
|
|
356
420
|
arguments override the configuration file, which overrides Tabalyst defaults.
|
|
357
421
|
No configuration file is loaded unless it is passed with `--config`. See the
|
|
@@ -368,7 +432,9 @@ Reports, samples, and scans read files as a stream, with memory bounded by
|
|
|
368
432
|
configurable limits, including duplicate-row detection
|
|
369
433
|
(`scan.limits.max_tracked_records`). Reports and scans show the share of the file read;
|
|
370
434
|
throughput estimates, recursive directory input, and parallel batch execution
|
|
371
|
-
will require later work.
|
|
435
|
+
will require later work. Inspect reads a JSON or JSONL source once from start to
|
|
436
|
+
end, with little memory; on the synthetic benchmarks it took roughly a tenth of the
|
|
437
|
+
time of a scan, a ratio that depends on the data.
|
|
372
438
|
|
|
373
439
|
## Examples and development
|
|
374
440
|
|
|
@@ -386,6 +452,7 @@ Additional documentation:
|
|
|
386
452
|
|
|
387
453
|
- [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/dev/architecture.md)
|
|
388
454
|
- [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
|
|
455
|
+
- [Inspect JSON and JSONL files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/inspect-json-files.md)
|
|
389
456
|
- [Release procedure](https://github.com/loribel-labs/tabalyst/blob/main/RELEASING.md)
|
|
390
457
|
|
|
391
458
|
Please report defects and feature requests through the
|
|
@@ -8,7 +8,8 @@ with structured data.
|
|
|
8
8
|
Its first tool is **Tabalyst Report**. The current CSV implementation,
|
|
9
9
|
**Tabalyst CSV Report**, analyzes a CSV file and produces both a structured JSON
|
|
10
10
|
profile and a self-contained interactive HTML report.
|
|
11
|
-
**Tabalyst Scan** describes CSV and
|
|
11
|
+
**Tabalyst Scan** describes CSV, JSON and JSONL files in a scan document,
|
|
12
|
+
**Tabalyst Inspect** finds how to read a JSON or JSONL file, and
|
|
12
13
|
**Tabalyst Sample** creates smaller CSV files.
|
|
13
14
|
|
|
14
15
|
Tabalyst is in beta. Its interfaces may still change while
|
|
@@ -34,6 +35,7 @@ changes often: update it before each new test.
|
|
|
34
35
|
| Several files, automatic names | `tabalyst report *.csv` | Beside each source |
|
|
35
36
|
| Several files, one directory | `tabalyst report *.csv -d reports/` | The directory `reports/` |
|
|
36
37
|
| One JSON file | `tabalyst report data.json` | `data.report.html` beside the source |
|
|
38
|
+
| One JSONL file | `tabalyst report events.jsonl` | `events.report.html` beside the source |
|
|
37
39
|
| From a scan document | `tabalyst report --scan data.scan.json` | `data.html` beside the scan |
|
|
38
40
|
|
|
39
41
|
The simplest command keeps the source filename:
|
|
@@ -51,6 +53,20 @@ customers.json
|
|
|
51
53
|
executions.json
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
Add `--details` to generate one self-contained HTML page per column:
|
|
57
|
+
|
|
58
|
+
```console
|
|
59
|
+
tabalyst report customers.csv --details
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The **Columns** table then links to those pages. Each page has its own sidebar
|
|
63
|
+
and shows values, counts, detected formats and other analysis directly. The
|
|
64
|
+
subfolder takes the HTML filename without `.html`; for `-o report.html`, pages
|
|
65
|
+
are in `report/`. Names use `col-01-` and a shortened slug of the column name,
|
|
66
|
+
in source order. Details are off by default. `--no-details` states that choice
|
|
67
|
+
explicitly; with `--force`, it removes prior generated column pages while
|
|
68
|
+
preserving other files in the folder.
|
|
69
|
+
|
|
54
70
|
Use `-o` to choose a different HTML filename for one source:
|
|
55
71
|
|
|
56
72
|
```console
|
|
@@ -59,16 +75,23 @@ tabalyst report customers.csv -o customer-analysis.html
|
|
|
59
75
|
|
|
60
76
|
### JSON files
|
|
61
77
|
|
|
62
|
-
A JSON file gets a report too, named `<stem>.report.html` so its
|
|
63
|
-
`<stem>.report.json` never replaces the source.
|
|
64
|
-
such as the `customers` array of
|
|
65
|
-
|
|
66
|
-
`address.city`:
|
|
78
|
+
A JSON or JSONL file gets a report too, named `<stem>.report.html` so its
|
|
79
|
+
profile `<stem>.report.json` never replaces the source. The report analyzes one
|
|
80
|
+
collection of records, such as the `customers` array of
|
|
81
|
+
`{"customers": [...]}`, chosen by Tabalyst Inspect, and nested fields are
|
|
82
|
+
columns named by their path, such as `address.city`:
|
|
67
83
|
|
|
68
84
|
```console
|
|
69
85
|
tabalyst report orders.json
|
|
86
|
+
tabalyst report events.jsonl
|
|
70
87
|
```
|
|
71
88
|
|
|
89
|
+
In a JSONL file (`.jsonl` or `.ndjson`), each line is a record. A line that is
|
|
90
|
+
not valid JSON or not an object is excluded and counted, and the report is
|
|
91
|
+
partial. When a JSON file holds several arrays that are equally plausible,
|
|
92
|
+
Tabalyst stops and lists them instead of choosing: see
|
|
93
|
+
[Tabalyst Inspect](#tabalyst-inspect).
|
|
94
|
+
|
|
72
95
|
### Multiple files
|
|
73
96
|
|
|
74
97
|
Report several CSV files at once:
|
|
@@ -121,8 +144,10 @@ instead.
|
|
|
121
144
|
|
|
122
145
|
### Safe batch behavior
|
|
123
146
|
|
|
124
|
-
Before processing begins, Tabalyst resolves every input and planned
|
|
125
|
-
stops the entire batch if
|
|
147
|
+
Before processing begins, Tabalyst resolves every input and planned main HTML
|
|
148
|
+
and JSON output. It stops the entire batch if those names collide or if an
|
|
149
|
+
output already exists. With `--details`, each column page is checked after its
|
|
150
|
+
profile has been built, before that report's files are written.
|
|
126
151
|
Use `--force` only when replacing all matching report artifacts is intentional:
|
|
127
152
|
|
|
128
153
|
```console
|
|
@@ -184,18 +209,48 @@ Sampling reads CSV records as a stream. Random and stratified sampling keep
|
|
|
184
209
|
only the requested sample, plus stratum counts, in memory. Existing outputs
|
|
185
210
|
require `--force`, and an input file is never overwritten.
|
|
186
211
|
|
|
212
|
+
## Tabalyst Inspect
|
|
213
|
+
|
|
214
|
+
A JSON file can hold several arrays. **Tabalyst Inspect** reads a JSON, JSONL
|
|
215
|
+
or NDJSON file once, finds which array holds the records and writes its answer
|
|
216
|
+
in `<source>-inspect.json` beside the source:
|
|
217
|
+
|
|
218
|
+
```console
|
|
219
|
+
tabalyst inspect orders.json
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
Inspect: orders.json-inspect.json
|
|
224
|
+
Selection: $.customers[] (the only eligible collection)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The last section of that file, `config`, holds the rules that `tabalyst scan`
|
|
228
|
+
and `tabalyst report` apply to the source: the collection
|
|
229
|
+
(`structure.dataset_path`), the flatten settings, the array mode and the error
|
|
230
|
+
policy. Edit it, then scan or report as usual. Running `tabalyst inspect` again
|
|
231
|
+
refreshes the detection and keeps your `config`; `--reset-config` replaces it.
|
|
232
|
+
|
|
233
|
+
Inspect is optional: `tabalyst scan` and `tabalyst report` inspect a JSON file
|
|
234
|
+
themselves when it has no Inspect file. When several arrays are equally
|
|
235
|
+
plausible, or none holds objects, they stop with exit code `2` before analyzing
|
|
236
|
+
anything and list the candidates; set `config.structure.dataset_path` in the
|
|
237
|
+
Inspect file, or pass `--collection`. See
|
|
238
|
+
[Inspect JSON and JSONL files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/inspect-json-files.md)
|
|
239
|
+
and the [Inspect format](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/inspect-format.md).
|
|
240
|
+
|
|
187
241
|
## Tabalyst Scan
|
|
188
242
|
|
|
189
|
-
Describe every field of a CSV or
|
|
243
|
+
Describe every field of a CSV, JSON or JSONL file in one JSON document:
|
|
190
244
|
|
|
191
245
|
```console
|
|
192
246
|
tabalyst scan customers.csv
|
|
193
247
|
tabalyst scan orders.json --collection "$.customers[]"
|
|
248
|
+
tabalyst scan events.jsonl
|
|
194
249
|
```
|
|
195
250
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
251
|
+
Without `-o` or `-d`, the scan is a reusable `scan.json` under Tabalyst's local
|
|
252
|
+
storage directory (or `TABALYST_HOME`). The default workflow does not build a
|
|
253
|
+
DuckDB database. `-o` or `-d` writes
|
|
199
254
|
a standalone `<stem>.scan.json` export instead. Tabalyst Scan reads the file once as
|
|
200
255
|
a stream, with memory bounded by configurable limits, and records for each
|
|
201
256
|
field its presence, native types, missing values, frequencies, exact
|
|
@@ -208,12 +263,12 @@ interrupted scan never leaves a partial file. Files of 16 MiB or more are
|
|
|
208
263
|
analyzed by several worker processes, with the same result; `--workers`
|
|
209
264
|
chooses their number, `--workers 1` keeps one process.
|
|
210
265
|
|
|
211
|
-
`tabalyst report customers.csv` reuses the verified
|
|
212
|
-
content and requested scan settings are current
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
266
|
+
`tabalyst report customers.csv` reuses the verified stored scan when the
|
|
267
|
+
source content and requested scan settings are current, and so does a report on
|
|
268
|
+
a JSON or JSONL file. It creates a scan when none exists and atomically replaces
|
|
269
|
+
a stale one. The source check uses the SHA-256 of the whole content, and a scan
|
|
270
|
+
written by another version of Tabalyst is replaced. Existing DuckDB projects from 0.4.3 are left
|
|
271
|
+
untouched.
|
|
217
272
|
|
|
218
273
|
Build the report from a standalone scan document without reading the source again:
|
|
219
274
|
|
|
@@ -224,8 +279,8 @@ tabalyst report --scan customers.scan.json
|
|
|
224
279
|
Tabalyst refuses a scan whose source changed since it was written, or whose
|
|
225
280
|
settings differ from the `scan` settings of `--config`.
|
|
226
281
|
|
|
227
|
-
Inspect or clean disposable
|
|
228
|
-
CSV file:
|
|
282
|
+
Inspect or clean disposable query caches of existing DuckDB projects, for every
|
|
283
|
+
project or for one CSV file:
|
|
229
284
|
|
|
230
285
|
```console
|
|
231
286
|
tabalyst cache info
|
|
@@ -234,7 +289,8 @@ tabalyst cache clean
|
|
|
234
289
|
tabalyst cache clean customers.csv
|
|
235
290
|
```
|
|
236
291
|
|
|
237
|
-
`cache clean` leaves project scans and databases in place.
|
|
292
|
+
`cache clean` leaves project scans and databases in place. Ordinary scans and
|
|
293
|
+
reports do not create these query caches.
|
|
238
294
|
|
|
239
295
|
See [Scan CSV and JSON files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/scan-files.md)
|
|
240
296
|
and the [scan format](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/scan-format.md).
|
|
@@ -284,14 +340,21 @@ sample = tabalyst.sample_csv(
|
|
|
284
340
|
|
|
285
341
|
scan = tabalyst.scan("orders.json")
|
|
286
342
|
scans = tabalyst.generate_scans(["data/*.json"], output_dir="scans")
|
|
343
|
+
|
|
344
|
+
inspection = tabalyst.inspect("orders.json")
|
|
345
|
+
inspections = tabalyst.generate_inspections(["data/*.json", "logs/*.jsonl"])
|
|
287
346
|
reports = tabalyst.generate_reports(["scans/*.scan.json"], from_scan=True)
|
|
288
347
|
```
|
|
289
348
|
|
|
290
349
|
`analyze()` returns the JSON-serializable profile for one report.
|
|
291
350
|
`generate_reports()` returns the complete batch plan, successes, and failures.
|
|
292
|
-
`scan()` returns a scan result without writing anything
|
|
293
|
-
|
|
294
|
-
`
|
|
351
|
+
`scan()` returns a scan result without writing anything, at the level of the
|
|
352
|
+
engine: it does not read an Inspect file. `generate_scans()` writes one
|
|
353
|
+
standalone `.scan.json` document per source by default, and applies Inspect to
|
|
354
|
+
JSON and JSONL sources. Pass `project_storage=True` to use the command's
|
|
355
|
+
scan-only storage. `inspect()` writes the Inspect file of one JSON or JSONL
|
|
356
|
+
source and returns its path and document; `generate_inspections()` does it for
|
|
357
|
+
several sources. `from_scan=True`
|
|
295
358
|
builds reports from standalone scan documents.
|
|
296
359
|
Expected failures derive from `tabalyst.TabalystError`.
|
|
297
360
|
|
|
@@ -315,7 +378,8 @@ Configuration files are strict JSON. A minimal file is:
|
|
|
315
378
|
```
|
|
316
379
|
|
|
317
380
|
Analysis settings, CSV reading included, go in the `scan` object, shared by
|
|
318
|
-
`tabalyst report` and `tabalyst
|
|
381
|
+
`tabalyst report`, `tabalyst scan` and `tabalyst inspect`. The `config` of an
|
|
382
|
+
Inspect file outranks the `scan` object for the source it sits beside. Top-level settings shape the report
|
|
319
383
|
presentation, and `csv` configures `tabalyst sample`. Explicit CLI or Python
|
|
320
384
|
arguments override the configuration file, which overrides Tabalyst defaults.
|
|
321
385
|
No configuration file is loaded unless it is passed with `--config`. See the
|
|
@@ -332,7 +396,9 @@ Reports, samples, and scans read files as a stream, with memory bounded by
|
|
|
332
396
|
configurable limits, including duplicate-row detection
|
|
333
397
|
(`scan.limits.max_tracked_records`). Reports and scans show the share of the file read;
|
|
334
398
|
throughput estimates, recursive directory input, and parallel batch execution
|
|
335
|
-
will require later work.
|
|
399
|
+
will require later work. Inspect reads a JSON or JSONL source once from start to
|
|
400
|
+
end, with little memory; on the synthetic benchmarks it took roughly a tenth of the
|
|
401
|
+
time of a scan, a ratio that depends on the data.
|
|
336
402
|
|
|
337
403
|
## Examples and development
|
|
338
404
|
|
|
@@ -350,6 +416,7 @@ Additional documentation:
|
|
|
350
416
|
|
|
351
417
|
- [Architecture](https://github.com/loribel-labs/tabalyst/blob/main/docs/dev/architecture.md)
|
|
352
418
|
- [Configuration](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/reference/configuration.md)
|
|
419
|
+
- [Inspect JSON and JSONL files](https://github.com/loribel-labs/tabalyst/blob/main/docs/en/how-to/inspect-json-files.md)
|
|
353
420
|
- [Release procedure](https://github.com/loribel-labs/tabalyst/blob/main/RELEASING.md)
|
|
354
421
|
|
|
355
422
|
Please report defects and feature requests through the
|
|
@@ -9,7 +9,8 @@ examples/
|
|
|
9
9
|
|-- input/
|
|
10
10
|
| |-- basic.csv
|
|
11
11
|
| |-- insurance-customers.csv
|
|
12
|
-
|
|
|
12
|
+
| |-- orders.json
|
|
13
|
+
| `-- web-events.jsonl
|
|
13
14
|
`-- output/
|
|
14
15
|
|-- basic/
|
|
15
16
|
| |-- report.html
|
|
@@ -18,8 +19,13 @@ examples/
|
|
|
18
19
|
|-- insurance-customers/
|
|
19
20
|
| |-- report.html
|
|
20
21
|
| |-- report.json
|
|
22
|
+
| |-- report/ (34 standalone column pages with --details)
|
|
21
23
|
| `-- executions.json
|
|
22
|
-
|
|
24
|
+
|-- orders/
|
|
25
|
+
| |-- report.html
|
|
26
|
+
| |-- report.json
|
|
27
|
+
| `-- executions.json
|
|
28
|
+
`-- web-events/
|
|
23
29
|
|-- report.html
|
|
24
30
|
|-- report.json
|
|
25
31
|
`-- executions.json
|
|
@@ -29,19 +35,27 @@ examples/
|
|
|
29
35
|
synthetic customer and contract records across 34 columns. Its people, contact
|
|
30
36
|
details, and contracts are fictional; email addresses use reserved `.example`
|
|
31
37
|
domains. `orders.json` is a synthetic shop export: 60 customers with nested
|
|
32
|
-
addresses, tags and orders
|
|
33
|
-
|
|
38
|
+
addresses, tags and orders. Its report has one dataset, `$.customers[]`, the only
|
|
39
|
+
collection of arrays of objects that Tabalyst Inspect finds in it.
|
|
40
|
+
`web-events.jsonl` is a synthetic web analytics log in JSON Lines: 300 events with
|
|
41
|
+
nested `user` and `device` objects, tags arrays, null values and an optional
|
|
42
|
+
field that appears late, plus three bad lines (a truncated record, a line that
|
|
43
|
+
is not JSON and a JSON array instead of an object). Its report is partial on
|
|
44
|
+
purpose: the three lines are excluded and counted, the 300 events are analyzed.
|
|
45
|
+
Its people and identifiers are fictional.
|
|
34
46
|
|
|
35
47
|
From the repository root, regenerate the outputs with:
|
|
36
48
|
|
|
37
49
|
```console
|
|
38
50
|
tabalyst report examples/input/basic.csv -o examples/output/basic/report.html --config examples/config.json --force
|
|
39
|
-
tabalyst report examples/input/insurance-customers.csv -o examples/output/insurance-customers/report.html --config examples/config.json --force
|
|
51
|
+
tabalyst report examples/input/insurance-customers.csv -o examples/output/insurance-customers/report.html --config examples/config.json --details --force
|
|
40
52
|
tabalyst report examples/input/orders.json -o examples/output/orders/report.html --config examples/config.json --force
|
|
53
|
+
tabalyst report examples/input/web-events.jsonl -o examples/output/web-events/report.html --config examples/config.json --force
|
|
41
54
|
```
|
|
42
55
|
|
|
43
56
|
Every output folder is independent and contains its own HTML report, canonical
|
|
44
|
-
JSON profile
|
|
57
|
+
JSON profile and cumulative `executions.json` history. The insurance example
|
|
58
|
+
also has standalone HTML pages under `report/`. Delete an output folder
|
|
45
59
|
before regenerating when a fresh one-entry execution history is required.
|
|
46
60
|
|
|
47
61
|
The input datasets and generated output are public repository examples. The wheel
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tabalyst"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Open-source toolkit for understanding and working with unfamiliar data."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -69,6 +69,7 @@ testpaths = ["tests"]
|
|
|
69
69
|
pythonpath = ["src"]
|
|
70
70
|
markers = [
|
|
71
71
|
"lot(name): Tabalyst Scan implementation lot that provides the tested behavior",
|
|
72
|
+
"inspect_lot(name): Tabalyst Inspect implementation lot that provides the tested behavior",
|
|
72
73
|
]
|
|
73
74
|
|
|
74
75
|
[tool.ruff]
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from tabalyst._version import __version__
|
|
4
4
|
from tabalyst.errors import ConfigurationError, InputError, ReportError, TabalystError
|
|
5
|
+
from tabalyst.inspect_service import InspectResult as InspectResult
|
|
6
|
+
from tabalyst.inspect_service import generate_inspections as generate_inspections
|
|
7
|
+
from tabalyst.inspect_service import inspect as inspect
|
|
5
8
|
from tabalyst.report_config import ReportConfig as ReportConfig
|
|
6
9
|
from tabalyst.report_service import generate_reports as generate_reports
|
|
7
10
|
from tabalyst.reporting import render_report as render_report
|
|
@@ -20,15 +23,18 @@ from tabalyst.service import analyze_scan as analyze_scan
|
|
|
20
23
|
__all__ = [
|
|
21
24
|
"ConfigurationError",
|
|
22
25
|
"InputError",
|
|
26
|
+
"InspectResult",
|
|
23
27
|
"ReportError",
|
|
24
28
|
"ScanConfig",
|
|
25
29
|
"ScanResult",
|
|
26
30
|
"TabalystError",
|
|
27
31
|
"__version__",
|
|
28
32
|
"analyze",
|
|
33
|
+
"generate_inspections",
|
|
29
34
|
"generate_reports",
|
|
30
35
|
"generate_samples",
|
|
31
36
|
"generate_scans",
|
|
37
|
+
"inspect",
|
|
32
38
|
"sample_csv",
|
|
33
39
|
"scan",
|
|
34
40
|
]
|
|
@@ -13,6 +13,15 @@ from pathlib import Path
|
|
|
13
13
|
|
|
14
14
|
from tabalyst.errors import ConfigurationError, InputError, ReportError
|
|
15
15
|
|
|
16
|
+
# The visible Inspect file of a source is its full name plus this suffix
|
|
17
|
+
# (design inspect 12.1). It is never a source itself.
|
|
18
|
+
INSPECT_SUFFIX = "-inspect.json"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def is_inspect_name(name: str) -> bool:
|
|
22
|
+
"""Whether a file name is that of an Inspect file."""
|
|
23
|
+
return name.endswith(INSPECT_SUFFIX)
|
|
24
|
+
|
|
16
25
|
|
|
17
26
|
def path_key(path: Path) -> str:
|
|
18
27
|
"""Identity of a path for comparisons, whatever its spelling and case."""
|
|
@@ -20,7 +29,11 @@ def path_key(path: Path) -> str:
|
|
|
20
29
|
|
|
21
30
|
|
|
22
31
|
def resolve_input_specs(input_specs: Sequence[str | Path]) -> list[Path]:
|
|
23
|
-
"""Resolve explicit files and shell-independent, non-recursive glob patterns.
|
|
32
|
+
"""Resolve explicit files and shell-independent, non-recursive glob patterns.
|
|
33
|
+
|
|
34
|
+
A pattern skips the Inspect files it matches; an explicit path to one is
|
|
35
|
+
refused (design inspect 12.1).
|
|
36
|
+
"""
|
|
24
37
|
if not input_specs:
|
|
25
38
|
raise ConfigurationError("At least one input file is required.")
|
|
26
39
|
|
|
@@ -34,7 +47,11 @@ def resolve_input_specs(input_specs: Sequence[str | Path]) -> list[Path]:
|
|
|
34
47
|
)
|
|
35
48
|
if glob.has_magic(text):
|
|
36
49
|
matches = sorted(
|
|
37
|
-
(
|
|
50
|
+
(
|
|
51
|
+
Path(match)
|
|
52
|
+
for match in glob.glob(text)
|
|
53
|
+
if Path(match).is_file() and not is_inspect_name(Path(match).name)
|
|
54
|
+
),
|
|
38
55
|
key=path_key,
|
|
39
56
|
)
|
|
40
57
|
if not matches:
|
|
@@ -45,6 +62,11 @@ def resolve_input_specs(input_specs: Sequence[str | Path]) -> list[Path]:
|
|
|
45
62
|
raise InputError(f"Input file does not exist: {path}")
|
|
46
63
|
if not path.is_file():
|
|
47
64
|
raise InputError(f"Input path is not a file: {path}")
|
|
65
|
+
if is_inspect_name(path.name):
|
|
66
|
+
raise ConfigurationError(
|
|
67
|
+
f"{path} is an Inspect file, not a source. Give the source it "
|
|
68
|
+
"describes; edit the Inspect file to change how it is read."
|
|
69
|
+
)
|
|
48
70
|
matches = [path]
|
|
49
71
|
|
|
50
72
|
for path in matches:
|
|
@@ -6,6 +6,7 @@ import typer
|
|
|
6
6
|
|
|
7
7
|
from tabalyst import __version__
|
|
8
8
|
from tabalyst.cli.cache import cache_app
|
|
9
|
+
from tabalyst.cli.inspect import inspect_command
|
|
9
10
|
from tabalyst.cli.report import report_command
|
|
10
11
|
from tabalyst.cli.sample import sample_command
|
|
11
12
|
from tabalyst.cli.scan import scan_command
|
|
@@ -39,6 +40,7 @@ def root(
|
|
|
39
40
|
"""Understand, explore, clean, and validate structured data."""
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
app.command("inspect")(inspect_command)
|
|
42
44
|
app.command("report")(report_command)
|
|
43
45
|
app.command("sample")(sample_command)
|
|
44
46
|
app.command("scan")(scan_command)
|
|
@@ -46,5 +48,10 @@ app.add_typer(cache_app, name="cache")
|
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
def main() -> None:
|
|
49
|
-
"""Run the Tabalyst command-line application.
|
|
50
|
-
|
|
51
|
+
"""Run the Tabalyst command-line application.
|
|
52
|
+
|
|
53
|
+
Windows wildcard expansion is left to Tabalyst: its own pattern resolver
|
|
54
|
+
skips the Inspect files that a shell-expanded ``*.json`` would pass as
|
|
55
|
+
explicit paths.
|
|
56
|
+
"""
|
|
57
|
+
app(windows_expand_args=False)
|