tigerdatalab 3.0.4__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 (58) hide show
  1. tigerdatalab-3.0.4/LICENSE +21 -0
  2. tigerdatalab-3.0.4/PKG-INFO +235 -0
  3. tigerdatalab-3.0.4/README.md +195 -0
  4. tigerdatalab-3.0.4/pyproject.toml +47 -0
  5. tigerdatalab-3.0.4/setup.cfg +4 -0
  6. tigerdatalab-3.0.4/tests/test_bugs.py +135 -0
  7. tigerdatalab-3.0.4/tests/test_core.py +113 -0
  8. tigerdatalab-3.0.4/tests/test_dataops.py +52 -0
  9. tigerdatalab-3.0.4/tests/test_extensions.py +80 -0
  10. tigerdatalab-3.0.4/tests/test_file_locking.py +76 -0
  11. tigerdatalab-3.0.4/tests/test_large.py +27 -0
  12. tigerdatalab-3.0.4/tests/test_loaders.py +47 -0
  13. tigerdatalab-3.0.4/tests/test_semantic_audit.py +103 -0
  14. tigerdatalab-3.0.4/tests/test_stress.py +112 -0
  15. tigerdatalab-3.0.4/tigerdatalab/__init__.py +17 -0
  16. tigerdatalab-3.0.4/tigerdatalab/analytics/__init__.py +0 -0
  17. tigerdatalab-3.0.4/tigerdatalab/analytics/category.py +38 -0
  18. tigerdatalab-3.0.4/tigerdatalab/analytics/customer.py +51 -0
  19. tigerdatalab-3.0.4/tigerdatalab/analytics/growth.py +86 -0
  20. tigerdatalab-3.0.4/tigerdatalab/analytics/kpi.py +66 -0
  21. tigerdatalab-3.0.4/tigerdatalab/analytics/product.py +40 -0
  22. tigerdatalab-3.0.4/tigerdatalab/analytics/profitability.py +24 -0
  23. tigerdatalab-3.0.4/tigerdatalab/analytics/trends.py +79 -0
  24. tigerdatalab-3.0.4/tigerdatalab/cli/__init__.py +0 -0
  25. tigerdatalab-3.0.4/tigerdatalab/cli/main.py +77 -0
  26. tigerdatalab-3.0.4/tigerdatalab/config.py +47 -0
  27. tigerdatalab-3.0.4/tigerdatalab/core.py +319 -0
  28. tigerdatalab-3.0.4/tigerdatalab/dashboard/__init__.py +0 -0
  29. tigerdatalab-3.0.4/tigerdatalab/dashboard/builder.py +145 -0
  30. tigerdatalab-3.0.4/tigerdatalab/dataops/__init__.py +0 -0
  31. tigerdatalab-3.0.4/tigerdatalab/dataops/asset.py +144 -0
  32. tigerdatalab-3.0.4/tigerdatalab/exceptions.py +71 -0
  33. tigerdatalab-3.0.4/tigerdatalab/insights/__init__.py +0 -0
  34. tigerdatalab-3.0.4/tigerdatalab/insights/engine.py +175 -0
  35. tigerdatalab-3.0.4/tigerdatalab/insights/questions.py +112 -0
  36. tigerdatalab-3.0.4/tigerdatalab/io/__init__.py +0 -0
  37. tigerdatalab-3.0.4/tigerdatalab/io/loaders.py +237 -0
  38. tigerdatalab-3.0.4/tigerdatalab/quality/__init__.py +0 -0
  39. tigerdatalab-3.0.4/tigerdatalab/quality/anomalies.py +45 -0
  40. tigerdatalab-3.0.4/tigerdatalab/quality/cleaning.py +85 -0
  41. tigerdatalab-3.0.4/tigerdatalab/quality/profiler.py +146 -0
  42. tigerdatalab-3.0.4/tigerdatalab/quality/types.py +163 -0
  43. tigerdatalab-3.0.4/tigerdatalab/reporting/__init__.py +0 -0
  44. tigerdatalab-3.0.4/tigerdatalab/reporting/_safe_io.py +63 -0
  45. tigerdatalab-3.0.4/tigerdatalab/reporting/exporters.py +34 -0
  46. tigerdatalab-3.0.4/tigerdatalab/reporting/html.py +44 -0
  47. tigerdatalab-3.0.4/tigerdatalab/reporting/pdf.py +168 -0
  48. tigerdatalab-3.0.4/tigerdatalab/reporting/terminal.py +137 -0
  49. tigerdatalab-3.0.4/tigerdatalab/scale/__init__.py +0 -0
  50. tigerdatalab-3.0.4/tigerdatalab/scale/duckdb_engine.py +96 -0
  51. tigerdatalab-3.0.4/tigerdatalab/visualization/__init__.py +0 -0
  52. tigerdatalab-3.0.4/tigerdatalab/visualization/charts.py +247 -0
  53. tigerdatalab-3.0.4/tigerdatalab.egg-info/PKG-INFO +235 -0
  54. tigerdatalab-3.0.4/tigerdatalab.egg-info/SOURCES.txt +56 -0
  55. tigerdatalab-3.0.4/tigerdatalab.egg-info/dependency_links.txt +1 -0
  56. tigerdatalab-3.0.4/tigerdatalab.egg-info/entry_points.txt +2 -0
  57. tigerdatalab-3.0.4/tigerdatalab.egg-info/requires.txt +21 -0
  58. tigerdatalab-3.0.4/tigerdatalab.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abhi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.4
2
+ Name: tigerdatalab
3
+ Version: 3.0.4
4
+ Summary: Automated Data Analytics + Data Quality + Visualization + BI + DataOps platform, built on pandas/numpy/duckdb/plotly.
5
+ Author: Abhi
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/abhi15724/tigerdatalab
8
+ Project-URL: Repository, https://github.com/abhi15724/tigerdatalab
9
+ Keywords: data-analysis,business-intelligence,eda,dataops,dashboard,pandas,duckdb,plotly
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: pandas>=1.5
23
+ Requires-Dist: numpy>=1.23
24
+ Requires-Dist: openpyxl>=3.1
25
+ Requires-Dist: plotly>=5.18
26
+ Requires-Dist: charset-normalizer>=3.0
27
+ Provides-Extra: large
28
+ Requires-Dist: duckdb>=0.10; extra == "large"
29
+ Requires-Dist: pyarrow>=14.0; extra == "large"
30
+ Provides-Extra: pdf
31
+ Requires-Dist: reportlab>=4.0; extra == "pdf"
32
+ Provides-Extra: all
33
+ Requires-Dist: duckdb>=0.10; extra == "all"
34
+ Requires-Dist: pyarrow>=14.0; extra == "all"
35
+ Requires-Dist: reportlab>=4.0; extra == "all"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7.4; extra == "dev"
38
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ # TigerDataLab
42
+
43
+ **TigerDataLab** is an automated Data Analytics + Data Quality + Visualization
44
+ + Business Intelligence + DataOps layer built **on top of** pandas, numpy,
45
+ duckdb and plotly — not a replacement for them.
46
+
47
+ ```python
48
+ import pandas as pd
49
+ import numpy as np # still there when you want low-level control
50
+
51
+ import tigerdatalab as tdl
52
+
53
+ result = tdl.analyze("sales.xlsx")
54
+ print(result.summary())
55
+ result.report("analysis") # dashboard + PDF + HTML + JSON, all in one call
56
+ ```
57
+
58
+ For very large data:
59
+
60
+ ```python
61
+ data = tdl.large("sales.parquet")
62
+ data.count()
63
+ data.aggregate("category", "SUM(revenue) AS revenue", "SUM(profit) AS profit")
64
+ ```
65
+
66
+ ## Install
67
+
68
+ ```bash
69
+ pip install tigerdatalab # core (pandas, numpy, openpyxl, plotly)
70
+ pip install "tigerdatalab[all]" # + duckdb, pyarrow, reportlab (large data + PDF)
71
+ ```
72
+
73
+ Local development:
74
+
75
+ ```bash
76
+ git clone <repo>
77
+ cd tigerdatalab
78
+ pip install -e ".[all,dev]"
79
+ ```
80
+
81
+ ## What `analyze()` does automatically
82
+
83
+ 1. Loads CSV / Excel (.xlsx, .xlsm) / JSON / Parquet / SQL / SQLite / DuckDB files.
84
+ 2. Detects column data types (never runs `.quantile()` on boolean columns).
85
+ 3. Detects business semantics (revenue, cost, profit, quantity, customer,
86
+ product, category, date, discount, order, price) from column names,
87
+ dtypes and value patterns.
88
+ 4. Profiles data quality (missing values, duplicates, outliers, invalid
89
+ dates, negative values where they shouldn't occur) into a 0–100 score.
90
+ 5. Cleans conservatively (whitespace, date normalization, numeric coercion,
91
+ duplicate removal) — every operation is logged, nothing is silently
92
+ destroyed.
93
+ 6. Calculates business KPIs (revenue, profit, margin, AOV, ASP, customers,
94
+ orders, products, discounts).
95
+ 7. Computes a performance trend (daily/monthly, MoM/YoY/rolling average)
96
+ when a date + numeric metric pair exists — and degrades gracefully
97
+ (never crashes) when it doesn't.
98
+ 8. Runs category, product, and customer analysis (each degrades gracefully
99
+ with a clear message if the relevant identifier column isn't present —
100
+ it never invents numbers).
101
+ 9. Generates a rule-based, evidence-backed business insight engine
102
+ (finding → evidence → impact → recommendation), with no LLM required.
103
+ 10. Builds 11 chart types (column, bar, line, pie, donut, histogram,
104
+ scatter, box plot, heatmap, pareto, KPI cards) — each with explicit
105
+ title/x-axis/y-axis/metric/aggregation metadata.
106
+ 11. Renders a responsive, interactive Plotly dashboard, a static HTML
107
+ report, a professional PDF business report, and JSON exports.
108
+
109
+ ## Public API
110
+
111
+ ```python
112
+ result = tdl.analyze("sales.xlsx")
113
+
114
+ result.summary() # str
115
+ result.kpis() # dict
116
+ result.quality() # dict
117
+ result.statistics() # dict
118
+ result.trends() # dict
119
+ result.customers() # dict
120
+ result.products() # dict
121
+ result.categories() # dict
122
+ result.insights() # list[dict] — severity/title/evidence/impact/recommendation
123
+ result.recommendations() # list[str]
124
+ result.visualize() # list[ChartSpec] — 12 chart types incl. KPI Cards
125
+ result.growth() # dict — growing/declining products & categories
126
+ result.anomalies() # dict — z-score anomaly rows/columns
127
+ result.ask() # dict — answers to the standard business-question set
128
+ result.ask("which_category_generates_the_most_revenue") # single answer
129
+
130
+ result.dashboard("analysis/dashboard.html")
131
+ result.export("analysis") # cleaned_data.xlsx, insights/quality/statistics/kpis.json
132
+ result.report("analysis") # export() + dashboard.html + analysis_report.html
133
+ # + business_insights.pdf + charts/*.html
134
+ ```
135
+
136
+ ## DataOps — controlled, audited writes
137
+
138
+ ```python
139
+ data = tdl.open("sales.xlsx")
140
+
141
+ data.update(where={"product_id": "SKU-1"}, values={"price": 499})
142
+ data.insert({"product_id": "SKU-99", "product": "Mouse", "price": 399})
143
+ data.delete(where={"product_id": "SKU-99"})
144
+ data.upsert({"product_id": "SKU-1", "price": 509}, key="product_id")
145
+
146
+ data.rollback() # undo the last operation
147
+ data.save() # write back to the original file
148
+ data.save_audit_log("analysis/audit.json")
149
+ ```
150
+
151
+ `update()`/`delete()` raise a clear error (`UpdateMatchedZeroRowsError`,
152
+ `DeleteMatchedZeroRowsError`) instead of silently doing nothing.
153
+
154
+ ## Large data (DuckDB-backed, lazy)
155
+
156
+ ```python
157
+ data = tdl.large("large_sales.parquet") # CSV/Parquet, no full pandas load
158
+ data.count()
159
+ data.aggregate("category", "SUM(revenue) AS revenue")
160
+ data.query("SELECT category, AVG(revenue) FROM data GROUP BY category") # destructive SQL is refused
161
+ ```
162
+
163
+ ## CLI
164
+
165
+ ```bash
166
+ tigerdatalab analyze sales.csv
167
+ tigerdatalab dashboard sales.csv -o analysis/dashboard.html
168
+ tigerdatalab profile sales.csv
169
+ tigerdatalab quality sales.csv
170
+ tigerdatalab clean sales.csv -o cleaned.xlsx
171
+ tigerdatalab report sales.csv -o analysis
172
+ ```
173
+
174
+ ## SQL files
175
+
176
+ ```python
177
+ result = tdl.analyze("sales.sql") # CREATE TABLE / INSERT INTO / SELECT
178
+ ```
179
+
180
+ Executed via DuckDB (falls back to sqlite3 if duckdb isn't installed).
181
+ `DROP` / `TRUNCATE` / `DELETE` / `ALTER` are always refused — use the
182
+ DataOps API for explicit, audited writes instead.
183
+
184
+ ## Privacy & security
185
+
186
+ Everything runs **locally**. TigerDataLab never uploads your data, never
187
+ calls an external API by default, and never executes arbitrary shell
188
+ commands from a dataset. The business-insight engine is fully deterministic
189
+ and rule-based — no LLM key required. An `InsightProvider` interface exists
190
+ for anyone who wants to plug in an optional LLM-backed provider later.
191
+
192
+ ## Project layout
193
+
194
+ ```
195
+ tigerdatalab/
196
+ ├── core.py # AnalysisResult, analyze(), open(), large()
197
+ ├── config.py
198
+ ├── exceptions.py
199
+ ├── io/loaders.py # csv/excel/json/parquet/sql/sqlite/duckdb loading
200
+ ├── quality/ # types.py, profiler.py, cleaning.py
201
+ ├── analytics/ # kpi.py, trends.py, customer.py, product.py, category.py, profitability.py
202
+ ├── insights/engine.py # rule-based insight generation
203
+ ├── visualization/charts.py # chart engine (11 chart types)
204
+ ├── dashboard/builder.py # interactive HTML dashboard
205
+ ├── reporting/ # html.py, pdf.py, exporters.py
206
+ ├── dataops/asset.py # update/insert/delete/upsert/merge/rollback/audit
207
+ ├── scale/duckdb_engine.py # lazy large-data aggregation
208
+ └── cli/main.py
209
+ ```
210
+
211
+ ## Testing
212
+
213
+ ```bash
214
+ python -m pytest
215
+ python examples/sales_analysis.py
216
+ python examples/dataops_example.py
217
+ python examples/large_data_example.py
218
+ ```
219
+
220
+ 31 tests cover CSV/Excel/Parquet/SQL loading, boolean-safe type detection,
221
+ missing-date/no-trend handling, empty/one-column/categorical-only datasets,
222
+ DataOps (insert/update/delete/upsert/rollback/audit), and DuckDB large-data
223
+ aggregation.
224
+
225
+ ## Publishing to PyPI
226
+
227
+ ```bash
228
+ pip install build twine
229
+ python -m build
230
+ twine upload dist/*
231
+ ```
232
+
233
+ ## Version
234
+
235
+ 3.0.0 — see `CHANGELOG.md` for the full list of fixes over the 2.2.0 prototype.
@@ -0,0 +1,195 @@
1
+ # TigerDataLab
2
+
3
+ **TigerDataLab** is an automated Data Analytics + Data Quality + Visualization
4
+ + Business Intelligence + DataOps layer built **on top of** pandas, numpy,
5
+ duckdb and plotly — not a replacement for them.
6
+
7
+ ```python
8
+ import pandas as pd
9
+ import numpy as np # still there when you want low-level control
10
+
11
+ import tigerdatalab as tdl
12
+
13
+ result = tdl.analyze("sales.xlsx")
14
+ print(result.summary())
15
+ result.report("analysis") # dashboard + PDF + HTML + JSON, all in one call
16
+ ```
17
+
18
+ For very large data:
19
+
20
+ ```python
21
+ data = tdl.large("sales.parquet")
22
+ data.count()
23
+ data.aggregate("category", "SUM(revenue) AS revenue", "SUM(profit) AS profit")
24
+ ```
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install tigerdatalab # core (pandas, numpy, openpyxl, plotly)
30
+ pip install "tigerdatalab[all]" # + duckdb, pyarrow, reportlab (large data + PDF)
31
+ ```
32
+
33
+ Local development:
34
+
35
+ ```bash
36
+ git clone <repo>
37
+ cd tigerdatalab
38
+ pip install -e ".[all,dev]"
39
+ ```
40
+
41
+ ## What `analyze()` does automatically
42
+
43
+ 1. Loads CSV / Excel (.xlsx, .xlsm) / JSON / Parquet / SQL / SQLite / DuckDB files.
44
+ 2. Detects column data types (never runs `.quantile()` on boolean columns).
45
+ 3. Detects business semantics (revenue, cost, profit, quantity, customer,
46
+ product, category, date, discount, order, price) from column names,
47
+ dtypes and value patterns.
48
+ 4. Profiles data quality (missing values, duplicates, outliers, invalid
49
+ dates, negative values where they shouldn't occur) into a 0–100 score.
50
+ 5. Cleans conservatively (whitespace, date normalization, numeric coercion,
51
+ duplicate removal) — every operation is logged, nothing is silently
52
+ destroyed.
53
+ 6. Calculates business KPIs (revenue, profit, margin, AOV, ASP, customers,
54
+ orders, products, discounts).
55
+ 7. Computes a performance trend (daily/monthly, MoM/YoY/rolling average)
56
+ when a date + numeric metric pair exists — and degrades gracefully
57
+ (never crashes) when it doesn't.
58
+ 8. Runs category, product, and customer analysis (each degrades gracefully
59
+ with a clear message if the relevant identifier column isn't present —
60
+ it never invents numbers).
61
+ 9. Generates a rule-based, evidence-backed business insight engine
62
+ (finding → evidence → impact → recommendation), with no LLM required.
63
+ 10. Builds 11 chart types (column, bar, line, pie, donut, histogram,
64
+ scatter, box plot, heatmap, pareto, KPI cards) — each with explicit
65
+ title/x-axis/y-axis/metric/aggregation metadata.
66
+ 11. Renders a responsive, interactive Plotly dashboard, a static HTML
67
+ report, a professional PDF business report, and JSON exports.
68
+
69
+ ## Public API
70
+
71
+ ```python
72
+ result = tdl.analyze("sales.xlsx")
73
+
74
+ result.summary() # str
75
+ result.kpis() # dict
76
+ result.quality() # dict
77
+ result.statistics() # dict
78
+ result.trends() # dict
79
+ result.customers() # dict
80
+ result.products() # dict
81
+ result.categories() # dict
82
+ result.insights() # list[dict] — severity/title/evidence/impact/recommendation
83
+ result.recommendations() # list[str]
84
+ result.visualize() # list[ChartSpec] — 12 chart types incl. KPI Cards
85
+ result.growth() # dict — growing/declining products & categories
86
+ result.anomalies() # dict — z-score anomaly rows/columns
87
+ result.ask() # dict — answers to the standard business-question set
88
+ result.ask("which_category_generates_the_most_revenue") # single answer
89
+
90
+ result.dashboard("analysis/dashboard.html")
91
+ result.export("analysis") # cleaned_data.xlsx, insights/quality/statistics/kpis.json
92
+ result.report("analysis") # export() + dashboard.html + analysis_report.html
93
+ # + business_insights.pdf + charts/*.html
94
+ ```
95
+
96
+ ## DataOps — controlled, audited writes
97
+
98
+ ```python
99
+ data = tdl.open("sales.xlsx")
100
+
101
+ data.update(where={"product_id": "SKU-1"}, values={"price": 499})
102
+ data.insert({"product_id": "SKU-99", "product": "Mouse", "price": 399})
103
+ data.delete(where={"product_id": "SKU-99"})
104
+ data.upsert({"product_id": "SKU-1", "price": 509}, key="product_id")
105
+
106
+ data.rollback() # undo the last operation
107
+ data.save() # write back to the original file
108
+ data.save_audit_log("analysis/audit.json")
109
+ ```
110
+
111
+ `update()`/`delete()` raise a clear error (`UpdateMatchedZeroRowsError`,
112
+ `DeleteMatchedZeroRowsError`) instead of silently doing nothing.
113
+
114
+ ## Large data (DuckDB-backed, lazy)
115
+
116
+ ```python
117
+ data = tdl.large("large_sales.parquet") # CSV/Parquet, no full pandas load
118
+ data.count()
119
+ data.aggregate("category", "SUM(revenue) AS revenue")
120
+ data.query("SELECT category, AVG(revenue) FROM data GROUP BY category") # destructive SQL is refused
121
+ ```
122
+
123
+ ## CLI
124
+
125
+ ```bash
126
+ tigerdatalab analyze sales.csv
127
+ tigerdatalab dashboard sales.csv -o analysis/dashboard.html
128
+ tigerdatalab profile sales.csv
129
+ tigerdatalab quality sales.csv
130
+ tigerdatalab clean sales.csv -o cleaned.xlsx
131
+ tigerdatalab report sales.csv -o analysis
132
+ ```
133
+
134
+ ## SQL files
135
+
136
+ ```python
137
+ result = tdl.analyze("sales.sql") # CREATE TABLE / INSERT INTO / SELECT
138
+ ```
139
+
140
+ Executed via DuckDB (falls back to sqlite3 if duckdb isn't installed).
141
+ `DROP` / `TRUNCATE` / `DELETE` / `ALTER` are always refused — use the
142
+ DataOps API for explicit, audited writes instead.
143
+
144
+ ## Privacy & security
145
+
146
+ Everything runs **locally**. TigerDataLab never uploads your data, never
147
+ calls an external API by default, and never executes arbitrary shell
148
+ commands from a dataset. The business-insight engine is fully deterministic
149
+ and rule-based — no LLM key required. An `InsightProvider` interface exists
150
+ for anyone who wants to plug in an optional LLM-backed provider later.
151
+
152
+ ## Project layout
153
+
154
+ ```
155
+ tigerdatalab/
156
+ ├── core.py # AnalysisResult, analyze(), open(), large()
157
+ ├── config.py
158
+ ├── exceptions.py
159
+ ├── io/loaders.py # csv/excel/json/parquet/sql/sqlite/duckdb loading
160
+ ├── quality/ # types.py, profiler.py, cleaning.py
161
+ ├── analytics/ # kpi.py, trends.py, customer.py, product.py, category.py, profitability.py
162
+ ├── insights/engine.py # rule-based insight generation
163
+ ├── visualization/charts.py # chart engine (11 chart types)
164
+ ├── dashboard/builder.py # interactive HTML dashboard
165
+ ├── reporting/ # html.py, pdf.py, exporters.py
166
+ ├── dataops/asset.py # update/insert/delete/upsert/merge/rollback/audit
167
+ ├── scale/duckdb_engine.py # lazy large-data aggregation
168
+ └── cli/main.py
169
+ ```
170
+
171
+ ## Testing
172
+
173
+ ```bash
174
+ python -m pytest
175
+ python examples/sales_analysis.py
176
+ python examples/dataops_example.py
177
+ python examples/large_data_example.py
178
+ ```
179
+
180
+ 31 tests cover CSV/Excel/Parquet/SQL loading, boolean-safe type detection,
181
+ missing-date/no-trend handling, empty/one-column/categorical-only datasets,
182
+ DataOps (insert/update/delete/upsert/rollback/audit), and DuckDB large-data
183
+ aggregation.
184
+
185
+ ## Publishing to PyPI
186
+
187
+ ```bash
188
+ pip install build twine
189
+ python -m build
190
+ twine upload dist/*
191
+ ```
192
+
193
+ ## Version
194
+
195
+ 3.0.0 — see `CHANGELOG.md` for the full list of fixes over the 2.2.0 prototype.
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tigerdatalab"
7
+ version = "3.0.4"
8
+ description = "Automated Data Analytics + Data Quality + Visualization + BI + DataOps platform, built on pandas/numpy/duckdb/plotly."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Abhi" }]
13
+ keywords = ["data-analysis", "business-intelligence", "eda", "dataops", "dashboard", "pandas", "duckdb", "plotly"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Science/Research",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Scientific/Engineering :: Information Analysis",
24
+ ]
25
+ dependencies = [
26
+ "pandas>=1.5",
27
+ "numpy>=1.23",
28
+ "openpyxl>=3.1",
29
+ "plotly>=5.18",
30
+ "charset-normalizer>=3.0",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ large = ["duckdb>=0.10", "pyarrow>=14.0"]
35
+ pdf = ["reportlab>=4.0"]
36
+ all = ["duckdb>=0.10", "pyarrow>=14.0", "reportlab>=4.0"]
37
+ dev = ["pytest>=7.4", "pytest-cov>=4.1"]
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/abhi15724/tigerdatalab"
41
+ Repository = "https://github.com/abhi15724/tigerdatalab"
42
+
43
+ [project.scripts]
44
+ tigerdatalab = "tigerdatalab.cli.main:main"
45
+
46
+ [tool.setuptools.packages.find]
47
+ include = ["tigerdatalab*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,135 @@
1
+ """Regression tests for the specific bugs the spec calls out by name."""
2
+ import pandas as pd
3
+ import pytest
4
+
5
+ import tigerdatalab as tdl
6
+ from tigerdatalab.quality.types import detect_dtype, safe_quantile, detect_all_dtypes
7
+ from tigerdatalab.quality.profiler import outlier_report
8
+ from tigerdatalab.exceptions import EmptyDatasetError, UnsupportedFileTypeError
9
+
10
+
11
+ def test_summary_attribute_exists(csv_path):
12
+ result = tdl.analyze(str(csv_path), verbose=False)
13
+ assert hasattr(result, "summary")
14
+ assert isinstance(result.summary(), str)
15
+
16
+
17
+ def test_boolean_column_never_quantiled(sales_df):
18
+ # in_stock is a real boolean column in the fixture
19
+ assert detect_dtype(sales_df["in_stock"]) == "boolean"
20
+ assert safe_quantile(sales_df["in_stock"], 0.5) is None
21
+
22
+ dtypes = detect_all_dtypes(sales_df)
23
+ report = outlier_report(sales_df, dtypes)
24
+ assert "in_stock" not in report["by_column"]
25
+
26
+
27
+ def test_output_paths_are_paths_not_dicts(csv_path, tmp_path):
28
+ result = tdl.analyze(str(csv_path), verbose=False)
29
+ outputs = result.export(tmp_path / "analysis")
30
+ for key, path in outputs.items():
31
+ assert hasattr(path, "exists"), f"{key} output should be a Path-like object"
32
+ assert path.exists()
33
+
34
+
35
+ def test_messy_dates_do_not_crash(messy_df, tmp_path):
36
+ p = tmp_path / "messy.csv"
37
+ messy_df.to_csv(p, index=False)
38
+ result = tdl.analyze(str(p), verbose=False)
39
+ assert result.profile["rows"] >= 1
40
+
41
+
42
+ def test_missing_trend_does_not_crash_dashboard(no_date_df, tmp_path):
43
+ p = tmp_path / "no_date.csv"
44
+ no_date_df.to_csv(p, index=False)
45
+ result = tdl.analyze(str(p), verbose=False)
46
+ out = result.dashboard(tmp_path / "dashboard.html")
47
+ html = out.read_text()
48
+ assert "Not enough data" in html
49
+
50
+
51
+ def test_multiple_chart_types_present(csv_path):
52
+ result = tdl.analyze(str(csv_path), verbose=False)
53
+ keys = {s.key for s in result.visualize()}
54
+ # more than "only one chart type"
55
+ assert len({"performance_trend", "category_pie", "product_bar", "correlation_heatmap"} & keys) == 4
56
+
57
+
58
+ def test_every_chart_has_explanatory_metadata(csv_path):
59
+ result = tdl.analyze(str(csv_path), verbose=False)
60
+ for spec in result.visualize():
61
+ meta = spec.to_meta()
62
+ assert meta["title"]
63
+
64
+
65
+ def test_empty_dataset_raises_clear_error(tmp_path):
66
+ p = tmp_path / "empty.csv"
67
+ pd.DataFrame({"a": [], "b": []}).to_csv(p, index=False)
68
+ with pytest.raises(EmptyDatasetError):
69
+ tdl.analyze(str(p), verbose=False)
70
+
71
+
72
+ def test_unsupported_extension_raises_clear_error(tmp_path):
73
+ p = tmp_path / "file.abc"
74
+ p.write_text("nothing")
75
+ with pytest.raises(UnsupportedFileTypeError):
76
+ tdl.analyze(str(p), verbose=False)
77
+
78
+
79
+ def test_one_column_numeric_only_dataset(tmp_path):
80
+ p = tmp_path / "single.csv"
81
+ pd.DataFrame({"revenue": [1, 2, 3, 4, 5]}).to_csv(p, index=False)
82
+ result = tdl.analyze(str(p), verbose=False)
83
+ assert result.kpis().get("total_revenue") == 15
84
+
85
+
86
+ def test_camelcase_columns_detected_correctly(tmp_path):
87
+ # Reproduces: a catalog-style CSV with camelCase headers (mrp,
88
+ # discountPercent, discountedSellingPrice, name, Category ...) failed
89
+ # to detect the plain "name" column as a product identifier, so
90
+ # result.products() incorrectly reported "not available".
91
+ p = tmp_path / "catalog.csv"
92
+ p.write_text(
93
+ "Category,name,mrp,discountPercent,discountedSellingPrice,quantity\n"
94
+ "Snacks,Choco Bar,100,10,90,5\n"
95
+ "Snacks,Wafer Pack,80,20,64,10\n"
96
+ "Beverages,Cola Can,50,5,47.5,20\n"
97
+ )
98
+ result = tdl.analyze(str(p), verbose=False)
99
+ assert result.semantics.get("product") == "name"
100
+ assert result.products()["available"] is True
101
+
102
+
103
+ def test_print_table_runs_without_error(capsys):
104
+ result = tdl.analyze("tests/data/finance.csv", verbose=False)
105
+ result.print_table()
106
+ captured = capsys.readouterr()
107
+ assert "DATASET OVERVIEW" in captured.out
108
+ assert "BUSINESS KPIs" in captured.out
109
+
110
+
111
+ def test_non_utf8_csv_does_not_crash(tmp_path):
112
+ # Reproduces: UnicodeDecodeError: 'utf-8' codec can't decode byte 0x92
113
+ # This happens on real-world CSVs exported from Excel/Windows, which
114
+ # are commonly cp1252 and contain "smart quote" characters (0x91-0x94).
115
+ p = tmp_path / "windows_export.csv"
116
+ with open(p, "wb") as f:
117
+ f.write("order_id,customer_name,notes,revenue\n".encode("utf-8"))
118
+ f.write(
119
+ ("1,O'Brien,Client said " + chr(0x2019) + "great job" + chr(0x2019)
120
+ + " today,100.0\n").encode("cp1252")
121
+ )
122
+ f.write("2,Smith,ok,200.5\n".encode("utf-8"))
123
+ f.write("3,Lee,fine,150.25\n".encode("utf-8"))
124
+
125
+ result = tdl.analyze(str(p), verbose=False)
126
+ assert result.profile["rows"] == 3
127
+ assert result.load_meta["encoding"] != "utf-8"
128
+ assert "\u2019" in result.cleaned_df["notes"].iloc[0]
129
+
130
+
131
+ def test_categorical_only_dataset(tmp_path):
132
+ p = tmp_path / "cat_only.csv"
133
+ pd.DataFrame({"category": ["A", "B", "A", "C"], "region": ["N", "S", "E", "W"]}).to_csv(p, index=False)
134
+ result = tdl.analyze(str(p), verbose=False)
135
+ assert result.profile["rows"] == 4