model2data 1.7.1__tar.gz → 1.8.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.
- {model2data-1.7.1/model2data.egg-info → model2data-1.8.0}/PKG-INFO +170 -25
- {model2data-1.7.1 → model2data-1.8.0}/README.md +250 -64
- {model2data-1.7.1 → model2data-1.8.0}/README_PYPI.md +166 -24
- model2data-1.8.0/model2data/_vendor/__init__.py +0 -0
- model2data-1.8.0/model2data/_vendor/pydbml/LICENSE +21 -0
- model2data-1.8.0/model2data/_vendor/pydbml/VENDORED.md +51 -0
- model2data-1.8.0/model2data/_vendor/pydbml/__init__.py +3 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/__init__.py +0 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/base.py +70 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/column.py +94 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/enum.py +89 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/expression.py +22 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/index.py +80 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/note.py +23 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/project.py +34 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/reference.py +120 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/sticky_note.py +24 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/table.py +158 -0
- model2data-1.8.0/model2data/_vendor/pydbml/_classes/table_group.py +49 -0
- model2data-1.8.0/model2data/_vendor/pydbml/classes/__init__.py +25 -0
- model2data-1.8.0/model2data/_vendor/pydbml/constants.py +4 -0
- model2data-1.8.0/model2data/_vendor/pydbml/database.py +207 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/__init__.py +0 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/column.py +158 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/common.py +39 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/enum.py +91 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/generic.py +43 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/index.py +110 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/project.py +53 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/records.py +80 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/reference.py +200 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/sticky_note.py +21 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/table.py +152 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/table_group.py +60 -0
- model2data-1.8.0/model2data/_vendor/pydbml/definitions/table_partial.py +48 -0
- model2data-1.8.0/model2data/_vendor/pydbml/exceptions.py +34 -0
- model2data-1.8.0/model2data/_vendor/pydbml/parser/__init__.py +1 -0
- model2data-1.8.0/model2data/_vendor/pydbml/parser/blueprints.py +372 -0
- model2data-1.8.0/model2data/_vendor/pydbml/parser/parser.py +360 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/__init__.py +0 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/base.py +38 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/__init__.py +0 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/__init__.py +11 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/column.py +55 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/enum.py +25 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/expression.py +7 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/index.py +49 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/note.py +14 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/project.py +29 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/reference.py +68 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/renderer.py +19 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/sticky_note.py +15 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/table.py +58 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/table_group.py +23 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/dbml/default/utils.py +31 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/__init__.py +0 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/__init__.py +8 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/column.py +45 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/enum.py +34 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/expression.py +7 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/index.py +65 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/note.py +43 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/reference.py +82 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/renderer.py +24 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/table.py +97 -0
- model2data-1.8.0/model2data/_vendor/pydbml/renderer/sql/default/utils.py +37 -0
- model2data-1.8.0/model2data/_vendor/pydbml/tools.py +54 -0
- model2data-1.8.0/model2data/cli.py +767 -0
- model2data-1.8.0/model2data/dbt/hint_tests.py +180 -0
- model2data-1.8.0/model2data/dbt/templates/hint_macros/model2data_hint_tests.sql +64 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/dbt/tests.py +32 -6
- {model2data-1.7.1 → model2data-1.8.0}/model2data/generate/core.py +8 -4
- model2data-1.8.0/model2data/generate/days.py +812 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/generate/faker.py +76 -22
- {model2data-1.7.1 → model2data-1.8.0}/model2data/generate/hints.py +14 -17
- model2data-1.8.0/model2data/generate/kinds.py +62 -0
- model2data-1.8.0/model2data/generate/relationships.py +112 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/generate/timeline.py +5 -9
- model2data-1.8.0/model2data/model/__init__.py +64 -0
- model2data-1.8.0/model2data/model/_yaml.py +284 -0
- model2data-1.8.0/model2data/model/dbml.py +375 -0
- model2data-1.8.0/model2data/model/document.py +272 -0
- model2data-1.8.0/model2data/model/dump.py +233 -0
- model2data-1.8.0/model2data/model/engine.py +184 -0
- model2data-1.8.0/model2data/model/errors.py +60 -0
- model2data-1.8.0/model2data/model/reader.py +105 -0
- model2data-1.8.0/model2data/model/types.py +172 -0
- model2data-1.8.0/model2data/model/validate.py +878 -0
- model2data-1.8.0/model2data/parse/dbml.py +78 -0
- model2data-1.8.0/model2data/spec/PUBLISHING.md +71 -0
- model2data-1.8.0/model2data/spec/README.md +330 -0
- model2data-1.8.0/model2data/spec/examples/coffee_webshop.model2data.yml +126 -0
- model2data-1.8.0/model2data/spec/model.schema.json +529 -0
- {model2data-1.7.1 → model2data-1.8.0/model2data.egg-info}/PKG-INFO +170 -25
- model2data-1.8.0/model2data.egg-info/SOURCES.txt +142 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data.egg-info/requires.txt +2 -0
- {model2data-1.7.1 → model2data-1.8.0}/pyproject.toml +18 -1
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_cli.py +9 -7
- model2data-1.8.0/tests/test_cli_model.py +248 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_coverage_gaps.py +10 -12
- model2data-1.8.0/tests/test_days.py +539 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_dbml_parser.py +107 -325
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_dbml_parser_fuzz.py +34 -30
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_dbt_integration.py +17 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_dbt_tests.py +38 -0
- model2data-1.8.0/tests/test_examples_model.py +61 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_generation.py +166 -0
- model2data-1.8.0/tests/test_hint_tests.py +410 -0
- model2data-1.8.0/tests/test_model.py +790 -0
- model2data-1.8.0/tests/test_model_dbml.py +362 -0
- model2data-1.8.0/tests/test_model_edges.py +225 -0
- model2data-1.8.0/tests/test_model_messages.py +250 -0
- model2data-1.8.0/tests/test_model_vocabulary.py +172 -0
- model2data-1.8.0/tests/test_reproducibility.py +116 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_shaping.py +23 -2
- model2data-1.8.0/tests/test_spec_alignment.py +126 -0
- model2data-1.8.0/tests/test_spec_schema.py +108 -0
- model2data-1.7.1/model2data/cli.py +0 -456
- model2data-1.7.1/model2data/generate/relationships.py +0 -52
- model2data-1.7.1/model2data/parse/dbml.py +0 -698
- model2data-1.7.1/model2data.egg-info/SOURCES.txt +0 -48
- {model2data-1.7.1 → model2data-1.8.0}/LICENSE +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/__init__.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/dbt/__init__.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/dbt/project.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/dbt/templates/dbt_project.yml.jinja +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/dbt/templates/macros/generate_schema_name.sql +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/dbt/templates/profiles.yml.jinja +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/generate/__init__.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/generate/options.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/parse/__init__.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data/utils.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data.egg-info/dependency_links.txt +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data.egg-info/entry_points.txt +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/model2data.egg-info/top_level.txt +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/setup.cfg +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_as_of_anchor.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_column_time_hints.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_dbt_naming.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_dbt_project.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_distributions.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_faker_name_inference.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_lone_country.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_options.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_release_stress.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_row_identity.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_table_seeds.py +0 -0
- {model2data-1.7.1 → model2data-1.8.0}/tests/test_timeline.py +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: model2data
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: Generate analytics-ready datasets from DBML models
|
|
5
5
|
Author: JB Analytica
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/JB-Analytica/model2data
|
|
8
|
+
Project-URL: Studio, https://studio.jbanalytica.com
|
|
8
9
|
Project-URL: Repository, https://github.com/JB-Analytica/model2data
|
|
9
10
|
Project-URL: Issues, https://github.com/JB-Analytica/model2data/issues
|
|
10
11
|
Project-URL: Changelog, https://github.com/JB-Analytica/model2data/blob/main/CHANGELOG.md
|
|
@@ -27,7 +28,9 @@ License-File: LICENSE
|
|
|
27
28
|
Requires-Dist: dbt-core>=1.11
|
|
28
29
|
Requires-Dist: dbt-duckdb>=1.11
|
|
29
30
|
Requires-Dist: faker>=37.12.0
|
|
31
|
+
Requires-Dist: jsonschema>=4.18
|
|
30
32
|
Requires-Dist: pandas>=2.3.3
|
|
33
|
+
Requires-Dist: pyparsing<4,>=3.1
|
|
31
34
|
Requires-Dist: pyyaml>=6.0.3
|
|
32
35
|
Requires-Dist: typer>=0.20.0
|
|
33
36
|
Provides-Extra: postgres
|
|
@@ -51,18 +54,24 @@ Dynamic: license-file
|
|
|
51
54
|
|
|
52
55
|
**Turn a data model into a running analytics stack in one command.**
|
|
53
56
|
|
|
54
|
-
Give `model2data` a [DBML](https://dbml.dbdiagram.io/docs/)
|
|
55
|
-
from an existing database — and it generates realistic,
|
|
57
|
+
Give `model2data` a data model — a `.model2data.yml` file, or a [DBML](https://dbml.dbdiagram.io/docs/)
|
|
58
|
+
schema hand-written or exported from an existing database — and it generates realistic,
|
|
59
|
+
relationship-preserving synthetic data
|
|
56
60
|
*and* a complete, runnable dbt project around it: seeds, staging models, tests, and a
|
|
57
61
|
DuckDB or Postgres profile. No sample data to hunt down, no dbt boilerplate to hand-write, no
|
|
58
62
|
production data to risk exposing.
|
|
59
63
|
|
|
60
64
|
```bash
|
|
61
65
|
pip install model2data
|
|
62
|
-
model2data --file examples/ecommerce.
|
|
66
|
+
model2data --file examples/ecommerce.model2data.yml --rows 200 --seed 42
|
|
63
67
|
cd dbt_ecommerce && dbt build
|
|
64
68
|
```
|
|
65
69
|
|
|
70
|
+
> **Prefer a browser?** [model2data studio](https://studio.jbanalytica.com) is the same
|
|
71
|
+
> engine as a web app: write DBML, watch the entity diagram redraw as you type, see what every
|
|
72
|
+
> column will generate before you generate it, then export CSVs or a runnable dbt project.
|
|
73
|
+
> Nothing to install, free to start.
|
|
74
|
+
|
|
66
75
|
That's a working analytics stack — real (synthetic) data, tested dbt models, queryable in
|
|
67
76
|
DuckDB — from a schema file, in seconds:
|
|
68
77
|
|
|
@@ -106,15 +115,95 @@ access required.
|
|
|
106
115
|
|
|
107
116
|
## How it works
|
|
108
117
|
|
|
109
|
-
1. **
|
|
118
|
+
1. **Read.** Reads the model — its tables, columns, keys, references and generation hints — from
|
|
119
|
+
a `.model2data.yml` document, checking it against [the spec](https://github.com/JB-Analytica/model2data/blob/main/model2data/spec/README.md), or
|
|
120
|
+
from DBML, which it converts to the same model first.
|
|
110
121
|
2. **Generate.** Produces synthetic values per column — typed generation for known SQL types
|
|
111
122
|
(int, date, timestamp, ...), name-aware inference for everything else (`email`, `phone`,
|
|
112
123
|
`city`, ...), foreign keys resolved against already-generated parent rows.
|
|
113
124
|
3. **Scaffold.** Writes a complete dbt project around that data: CSV seeds, staging models that
|
|
114
125
|
`ref()` those seeds, `not_null`/`unique`/`relationships` tests, `accepted_values` tests for
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
enum-typed columns, singular SQL tests for composite primary/unique keys, table and column
|
|
127
|
+
`description:` fields from the model, and a profile for DuckDB (zero-config, file-based) or
|
|
128
|
+
Postgres.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## The model file
|
|
133
|
+
|
|
134
|
+
A model is one YAML document, `<name>.model2data.yml` — or the same document as JSON. Its format
|
|
135
|
+
is [spec 0.2.0](https://github.com/JB-Analytica/model2data/blob/main/model2data/spec/README.md), with a JSON Schema your editor can autocomplete and
|
|
136
|
+
check against:
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
# yaml-language-server: $schema=https://www.jbanalytica.com/model2data/spec/0.2.0/model.schema.json
|
|
140
|
+
model2data: 0.2.0
|
|
141
|
+
name: coffee_webshop
|
|
142
|
+
|
|
143
|
+
enums:
|
|
144
|
+
order_status: [pending, paid, shipped, delivered, cancelled]
|
|
145
|
+
|
|
146
|
+
tables:
|
|
147
|
+
customers:
|
|
148
|
+
columns:
|
|
149
|
+
id: {type: bigint, pk: true}
|
|
150
|
+
email: {type: email, unique: true, not_null: true}
|
|
151
|
+
|
|
152
|
+
orders:
|
|
153
|
+
columns:
|
|
154
|
+
id: {type: bigint, pk: true}
|
|
155
|
+
customer_id:
|
|
156
|
+
type: bigint
|
|
157
|
+
not_null: true
|
|
158
|
+
references: customers.id
|
|
159
|
+
generate: {skew: 0.8}
|
|
160
|
+
total_amount:
|
|
161
|
+
type: numeric
|
|
162
|
+
generate:
|
|
163
|
+
min: 10
|
|
164
|
+
max: 5000
|
|
165
|
+
distribution: {kind: lognormal, median: 120, spread: 0.7}
|
|
166
|
+
status:
|
|
167
|
+
type: order_status
|
|
168
|
+
generate:
|
|
169
|
+
weights: {delivered: 20, cancelled: 1}
|
|
170
|
+
|
|
171
|
+
run:
|
|
172
|
+
seed: 1
|
|
173
|
+
as_of: 2026-01-01
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`generate` holds a column's hints; `run` saves the generation settings with the model, and every
|
|
177
|
+
CLI option overrides the one it names. `model2data validate FILE` checks a model and prints every
|
|
178
|
+
issue with its path in the document.
|
|
179
|
+
|
|
180
|
+
**DBML is supported input.** `--file` also takes a `.dbml` file, converted to the same model
|
|
181
|
+
before generating; a JSON note on a column (`[note: '{"min": 1}']`) becomes its `generate`, any
|
|
182
|
+
other note its description. `model2data convert schema.dbml -o schema.model2data.yml` writes the
|
|
183
|
+
model a DBML file converts to.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## model2data studio — the same engine, in the browser
|
|
188
|
+
|
|
189
|
+
[**model2data studio**](https://studio.jbanalytica.com) puts everything on this page behind a
|
|
190
|
+
web UI. It is built by JB Analytica on top of this library, it's the fastest way to try
|
|
191
|
+
model2data, and it's the better fit while a schema is still being designed:
|
|
192
|
+
|
|
193
|
+
- **Type DBML, see the diagram.** Syntax highlighting, autocomplete and live error checking; the
|
|
194
|
+
entity diagram redraws as you type. Click a column to trace what actually joins to it.
|
|
195
|
+
- **See what you'll get before you generate.** Every column shows an example of the value it will
|
|
196
|
+
produce, and columns nothing recognises are marked — so placeholder data is visible rather than
|
|
197
|
+
silent.
|
|
198
|
+
- **Generate and export.** Per-table row counts, then CSVs or a complete dbt project: the same
|
|
199
|
+
seeds, staging models, tests and DuckDB profile this CLI produces, reproducing the exact rows
|
|
200
|
+
you previewed.
|
|
201
|
+
- **Share the model.** A share link that also embeds as a chrome-free diagram in a Notion,
|
|
202
|
+
Confluence or wiki page.
|
|
203
|
+
|
|
204
|
+
Free to start, nothing to install: [studio.jbanalytica.com](https://studio.jbanalytica.com).
|
|
205
|
+
The CLI stays the right tool for scripting, CI and fixtures you commit; the studio is where a
|
|
206
|
+
model gets designed and shown.
|
|
118
207
|
|
|
119
208
|
---
|
|
120
209
|
|
|
@@ -129,12 +218,12 @@ pip install model2data
|
|
|
129
218
|
## Quick start
|
|
130
219
|
|
|
131
220
|
We bundle several example schemas in `examples/` — this walkthrough uses the e-commerce one
|
|
132
|
-
(`examples/ecommerce.
|
|
221
|
+
(`examples/ecommerce.model2data.yml`: customers, products, orders, order items, and reviews).
|
|
133
222
|
|
|
134
223
|
Generate a project with synthetic data:
|
|
135
224
|
|
|
136
225
|
```bash
|
|
137
|
-
model2data --file examples/ecommerce.
|
|
226
|
+
model2data --file examples/ecommerce.model2data.yml --rows 200 --seed 42
|
|
138
227
|
```
|
|
139
228
|
|
|
140
229
|
This creates a `dbt_ecommerce/` folder with your data and dbt setup.
|
|
@@ -147,7 +236,7 @@ re-rolls just that table, leaving every other table's seed CSV byte-identical. `
|
|
|
147
236
|
the country every generated person and address comes from:
|
|
148
237
|
|
|
149
238
|
```bash
|
|
150
|
-
model2data --file examples/ecommerce.
|
|
239
|
+
model2data --file examples/ecommerce.model2data.yml --rows 200 --seed 42 \
|
|
151
240
|
--as-of 2026-01-31 --table-seed orders=7 --locale nl_BE
|
|
152
241
|
```
|
|
153
242
|
|
|
@@ -170,24 +259,77 @@ To target Postgres instead, install the extra and pass `--adapter postgres`:
|
|
|
170
259
|
|
|
171
260
|
```bash
|
|
172
261
|
pip install "model2data[postgres]"
|
|
173
|
-
model2data --file examples/ecommerce.
|
|
262
|
+
model2data --file examples/ecommerce.model2data.yml --rows 200 --seed 42 --adapter postgres
|
|
174
263
|
```
|
|
175
264
|
|
|
176
265
|
Connection details are read from environment variables (`MODEL2DATA_PG_HOST`, `MODEL2DATA_PG_PORT`, `MODEL2DATA_PG_USER`, `MODEL2DATA_PG_PASSWORD`, `MODEL2DATA_PG_DATABASE`), defaulting to `localhost:5432` with a `postgres`/`postgres` user for local development.
|
|
177
266
|
|
|
178
|
-
After generation, the CLI prints a short summary — tables and rows generated, relationships found in the
|
|
267
|
+
After generation, the CLI prints a short summary — tables and rows generated, relationships found in the model, and any columns that fell back to generic placeholder text because neither their type nor name could be matched.
|
|
179
268
|
|
|
180
269
|
Pass `--unit-tests` to also generate deterministic dbt unit test fixtures (`models/staging/ut_stg_<table>.yml`) from the actually-generated seed rows:
|
|
181
270
|
|
|
182
271
|
```bash
|
|
183
|
-
model2data --file examples/ecommerce.
|
|
272
|
+
model2data --file examples/ecommerce.model2data.yml --rows 200 --seed 42 --unit-tests
|
|
184
273
|
```
|
|
185
274
|
|
|
186
275
|
This targets dbt-core's native unit testing feature, which works out of the box with the base
|
|
187
276
|
install — see [dbt-core versions](#dbt-core-versions) below.
|
|
188
277
|
|
|
278
|
+
Your model's hints also write **data tests** for the dbt project, so the model that generates the
|
|
279
|
+
fixtures guards the real pipeline too. Every hint that states a constraint becomes a generic test
|
|
280
|
+
on the staging model, shipped as a self-contained macro in `macros/model2data_hint_tests.sql` (no
|
|
281
|
+
dbt package, so `dbt build` still works offline):
|
|
282
|
+
|
|
283
|
+
| In the model | dbt test | Tolerance |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| `generate: {min, max}` on a numeric column | `model2data_between` | none |
|
|
286
|
+
| `generate: {after: other}` | `model2data_not_before` (where both are not null) | none |
|
|
287
|
+
| `generate: {null_rate}` | `model2data_max_null_share`: nulls at most `null_rate` + tolerance | `--test-tolerance`, default 0.1 |
|
|
288
|
+
| `generate: {distinct: n}` | `model2data_max_distinct`: at most `n` distinct values | none |
|
|
289
|
+
| `grain` on a table | `model2data_unique_combination` | none |
|
|
290
|
+
| an enum-typed column | `accepted_values` (always written) | none |
|
|
291
|
+
|
|
292
|
+
`true_rate`, `weights`, `skew`, `distribution` and the temporal shape hints (`business_hours`,
|
|
293
|
+
`growth`, `seasonality`) describe a statistical shape rather than a constraint a row can break, so
|
|
294
|
+
they write no test. `--hint-tests {error,warn,off}` sets these tests' severity (default `warn`:
|
|
295
|
+
they describe intent and should not break a first `dbt build` on real data); the structural tests
|
|
296
|
+
(`not_null`, `unique`, `relationships`) are unaffected. A model with no such hints generates the
|
|
297
|
+
same YAML as before. To call the mapping from Python, `model2data.dbt.hint_tests.hint_tests_for(model)`
|
|
298
|
+
returns the tests as data.
|
|
299
|
+
|
|
189
300
|
---
|
|
190
301
|
|
|
302
|
+
### Generate the next days
|
|
303
|
+
|
|
304
|
+
A table with `incremental` in the model moves on day by day: each day adds `new_per_day` rows
|
|
305
|
+
(their dates and timestamps falling on that day) and updates `update_rate` of the rows it already
|
|
306
|
+
holds, and an enum column with `transitions` moves from a state to one of the states allowed to
|
|
307
|
+
follow it. See `examples/ecommerce_daily.model2data.yml` and "Days after the first" in the
|
|
308
|
+
[spec](https://github.com/JB-Analytica/model2data/blob/main/model2data/spec/README.md).
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
model2data --file examples/ecommerce_daily.model2data.yml --seed 42 --as-of 2026-01-31 --days 7
|
|
312
|
+
model2data --file examples/ecommerce_daily.model2data.yml --seed 42 --as-of 2026-01-31 --next
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
`--days N` generates day 0, the run you would get without it, and N days after it (`--next` is
|
|
316
|
+
`--days 1`). The dbt seeds hold the state after the last day. `--days-format` picks what else is
|
|
317
|
+
written beside the project, outside `seeds/` so dbt does not load it: `batches` (the default),
|
|
318
|
+
`days/<table>/day_000.csv` whole and then one file per day with the rows inserted and updated that
|
|
319
|
+
day; `changelog`, `changelog/<table>.csv` with a `_day` and an `_op` (`insert` or `update`) column
|
|
320
|
+
on every row; or `final`, nothing more. A day depends only on the seed, `--as-of`, the day and the
|
|
321
|
+
model, so day *n* never changes when you generate more days or add an unrelated table. From Python:
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
from model2data.generate.days import generate_days
|
|
325
|
+
from model2data.model import load
|
|
326
|
+
|
|
327
|
+
days = generate_days(load("examples/ecommerce_daily.model2data.yml"), 7, seed=42)
|
|
328
|
+
days[3].tables["orders"].inserted # rows day 3 added
|
|
329
|
+
days[3].tables["orders"].updated # rows day 3 changed, with their new values
|
|
330
|
+
days[3].tables["orders"].state # the table after day 3
|
|
331
|
+
```
|
|
332
|
+
|
|
191
333
|
## Generated dbt project structure
|
|
192
334
|
|
|
193
335
|
The generated dbt project includes:
|
|
@@ -208,22 +350,23 @@ dbt_{project_name}/
|
|
|
208
350
|
├── data-tests/
|
|
209
351
|
│ └── unique_combination_stg_table1_col_a_col_b.sql # only for composite pk/unique keys
|
|
210
352
|
├── macros/
|
|
211
|
-
│
|
|
353
|
+
│ ├── generate_schema_name.sql
|
|
354
|
+
│ └── model2data_hint_tests.sql # only when the model's hints write tests
|
|
212
355
|
├── dbt_project.yml
|
|
213
356
|
├── profiles.yml # DuckDB or Postgres config, depending on --adapter
|
|
214
357
|
└── {project_name}_profile.duckdb # DuckDB adapter only
|
|
215
358
|
```
|
|
216
359
|
|
|
217
360
|
- **Seeds**: CSV files with generated synthetic data, plus `__seed_config.yml` — each seed's
|
|
218
|
-
`description:` (
|
|
361
|
+
`description:` (the table's `description` in the model) and the column-type overrides that keep
|
|
219
362
|
all-digit text columns (barcodes, zero-padded postcodes, ...) from being loaded as integers.
|
|
220
363
|
- **Staging Models**: Basic dbt models that `ref()` their seed. Using `ref()` rather than
|
|
221
364
|
declaring the seeds as dbt `sources` is what gives each model a real DAG edge to the seed
|
|
222
365
|
behind it, so one `dbt build` orders seeds before models on a fresh database.
|
|
223
366
|
- **Tests**: A YAML per staging model with column tests (`not_null`, `unique`, `relationships`,
|
|
224
|
-
and `accepted_values` for
|
|
225
|
-
`description:`
|
|
226
|
-
- **Composite key tests**: Composite primary/unique keys
|
|
367
|
+
and `accepted_values` for enum-typed columns), plus the hint tests above. A column's `description` in the model becomes
|
|
368
|
+
its `description:` field.
|
|
369
|
+
- **Composite key tests**: Composite primary/unique keys (`keys`, or several `pk: true` columns) get
|
|
227
370
|
a singular SQL test under `data-tests/`, dbt's configured `test-paths`.
|
|
228
371
|
- **Profiles**: Pre-configured for DuckDB (file-based) or Postgres (via env vars), with schema handling.
|
|
229
372
|
- **Unit tests** (opt-in via `--unit-tests`): `models/staging/ut_stg_<table>.yml` fixtures built
|
|
@@ -236,8 +379,8 @@ dbt_{project_name}/
|
|
|
236
379
|
|
|
237
380
|
If you want to go from a plain-English description of a data model straight to a running,
|
|
238
381
|
demo-ready dbt project, [LLMS.md](https://github.com/JB-Analytica/model2data/blob/main/LLMS.md) is written for an LLM/agent to read: it covers the
|
|
239
|
-
|
|
240
|
-
relationship syntaxes, self-references) and the exact command sequence to run. Point an
|
|
382
|
+
DBML feature set model2data understands (enums, notes, defaults, composite keys, both
|
|
383
|
+
relationship syntaxes, self-references), which it converts to a spec 0.2.0 model and the exact command sequence to run. Point an
|
|
241
384
|
LLM-backed coding assistant at it and describe your data model — it can author the DBML and run
|
|
242
385
|
model2data for you.
|
|
243
386
|
|
|
@@ -269,15 +412,15 @@ If you're pinned to an older dbt-core, use model2data 0.5.x, which supported dow
|
|
|
269
412
|
- Synthetic data generation is heuristic-based (typed generation, name-aware inference, enum/default awareness) and may not perfectly mimic real-world distributions or edge cases.
|
|
270
413
|
- DuckDB and Postgres are supported today; other databases require manual profile adjustments.
|
|
271
414
|
- No support for incremental models or advanced dbt features in generated projects.
|
|
272
|
-
- Composite foreign keys (across a bridge/join table) are generated as independent single-column FKs — each column's values are individually valid, but the *combination* isn't guaranteed to match a real parent composite key unless that key is separately enforced
|
|
273
|
-
-
|
|
415
|
+
- Composite foreign keys (across a bridge/join table) are generated as independent single-column FKs — each column's values are individually valid, but the *combination* isn't guaranteed to match a real parent composite key unless that key is separately enforced as a key of the child (`keys`).
|
|
416
|
+
- A model that doesn't conform to the spec — or DBML that can't be read, or converts to such a model — is refused with every issue and where it is, rather than generated from in part. A reference onto a column that is not a key is allowed with a warning: its values are drawn from the ones the parent column holds.
|
|
274
417
|
|
|
275
418
|
---
|
|
276
419
|
|
|
277
420
|
## Project status
|
|
278
421
|
|
|
279
422
|
As of `1.0.0`, model2data is considered **feature-complete for its intended use case**: turning a
|
|
280
|
-
|
|
423
|
+
data model into realistic synthetic data and a runnable dbt project, reliably. There's no active
|
|
281
424
|
roadmap of new capabilities planned — the focus from here is maintenance: bug fixes, keeping pace
|
|
282
425
|
with new dbt-core releases, and reviewing community contributions.
|
|
283
426
|
|
|
@@ -299,7 +442,7 @@ See [CONTRIBUTING.md](https://github.com/JB-Analytica/model2data/blob/main/CONTR
|
|
|
299
442
|
We welcome contributions!
|
|
300
443
|
|
|
301
444
|
- Open issues for bugs or feature requests.
|
|
302
|
-
- Submit PRs to add new
|
|
445
|
+
- Submit PRs to add new example models, custom data generators, or improvements.
|
|
303
446
|
- Ensure all new features include tests if possible.
|
|
304
447
|
|
|
305
448
|
See [CONTRIBUTING.md](https://github.com/JB-Analytica/model2data/blob/main/CONTRIBUTING.md) for detailed guidelines, and [DEVELOPMENT.md](https://github.com/JB-Analytica/model2data/blob/main/DEVELOPMENT.md) for the local dev setup and release process.
|
|
@@ -323,4 +466,6 @@ MIT License. See LICENSE for details.
|
|
|
323
466
|
<br>
|
|
324
467
|
Built and maintained by <a href="https://www.jbanalytica.com"><strong>JB Analytica</strong></a> —
|
|
325
468
|
Data & Analytics Engineering · Data Platform Architecture · Modern BI.
|
|
469
|
+
<br>
|
|
470
|
+
Try <a href="https://studio.jbanalytica.com"><strong>model2data studio</strong></a> — model2data in the browser, nothing to install.
|
|
326
471
|
</p>
|