det-elt 0.1.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.
- det_elt-0.1.0/LICENSE +21 -0
- det_elt-0.1.0/PKG-INFO +349 -0
- det_elt-0.1.0/README.md +292 -0
- det_elt-0.1.0/pyproject.toml +200 -0
- det_elt-0.1.0/setup.cfg +4 -0
- det_elt-0.1.0/src/det/__init__.py +117 -0
- det_elt-0.1.0/src/det/cli/__init__.py +21 -0
- det_elt-0.1.0/src/det/cli/app.py +38 -0
- det_elt-0.1.0/src/det/cli/approvals.py +193 -0
- det_elt-0.1.0/src/det/cli/common.py +291 -0
- det_elt-0.1.0/src/det/cli/dbt_cmd.py +151 -0
- det_elt-0.1.0/src/det/cli/extract_load.py +205 -0
- det_elt-0.1.0/src/det/cli/inspect_cmd.py +89 -0
- det_elt-0.1.0/src/det/cli/migrate_prune.py +297 -0
- det_elt-0.1.0/src/det/cli/ops.py +476 -0
- det_elt-0.1.0/src/det/cli/render_runs.py +168 -0
- det_elt-0.1.0/src/det/cli/scaffold.py +234 -0
- det_elt-0.1.0/src/det/destinations/__init__.py +1 -0
- det_elt-0.1.0/src/det/destinations/models.py +200 -0
- det_elt-0.1.0/src/det/errors.py +58 -0
- det_elt-0.1.0/src/det/ingestion/__init__.py +1 -0
- det_elt-0.1.0/src/det/ingestion/base.py +35 -0
- det_elt-0.1.0/src/det/ingestion/chunks.py +17 -0
- det_elt-0.1.0/src/det/ingestion/det_backend.py +193 -0
- det_elt-0.1.0/src/det/ingestion/dlt_backend.py +7 -0
- det_elt-0.1.0/src/det/ingestion/duckdb_writer.py +129 -0
- det_elt-0.1.0/src/det/ingestion/iceberg_catalog.py +278 -0
- det_elt-0.1.0/src/det/ingestion/iceberg_writer.py +563 -0
- det_elt-0.1.0/src/det/ingestion/jsonl.py +64 -0
- det_elt-0.1.0/src/det/ingestion/postgres_writer.py +136 -0
- det_elt-0.1.0/src/det/ingestion/sql_ddl.py +82 -0
- det_elt-0.1.0/src/det/ingestion/sql_replace.py +91 -0
- det_elt-0.1.0/src/det/ingestion/thin_backend.py +51 -0
- det_elt-0.1.0/src/det/logging.py +237 -0
- det_elt-0.1.0/src/det/mcp/__init__.py +5 -0
- det_elt-0.1.0/src/det/mcp/__main__.py +6 -0
- det_elt-0.1.0/src/det/mcp/airflow_inspect.py +471 -0
- det_elt-0.1.0/src/det/mcp/catalog.py +178 -0
- det_elt-0.1.0/src/det/mcp/context.py +43 -0
- det_elt-0.1.0/src/det/mcp/cube_client.py +204 -0
- det_elt-0.1.0/src/det/mcp/errors.py +38 -0
- det_elt-0.1.0/src/det/mcp/generate.py +413 -0
- det_elt-0.1.0/src/det/mcp/inspect/__init__.py +104 -0
- det_elt-0.1.0/src/det/mcp/inspect/_common.py +168 -0
- det_elt-0.1.0/src/det/mcp/inspect/_diagnose.py +177 -0
- det_elt-0.1.0/src/det/mcp/inspect/_partitions.py +405 -0
- det_elt-0.1.0/src/det/mcp/inspect/_sample.py +748 -0
- det_elt-0.1.0/src/det/mcp/params.py +325 -0
- det_elt-0.1.0/src/det/mcp/policy.py +377 -0
- det_elt-0.1.0/src/det/mcp/prompts.py +95 -0
- det_elt-0.1.0/src/det/mcp/query_sql.py +241 -0
- det_elt-0.1.0/src/det/mcp/reload.py +50 -0
- det_elt-0.1.0/src/det/mcp/resources.py +67 -0
- det_elt-0.1.0/src/det/mcp/server.py +501 -0
- det_elt-0.1.0/src/det/mcp/tools.py +1122 -0
- det_elt-0.1.0/src/det/optional_deps.py +61 -0
- det_elt-0.1.0/src/det/plugins.py +25 -0
- det_elt-0.1.0/src/det/py.typed +0 -0
- det_elt-0.1.0/src/det/runtime/__init__.py +1 -0
- det_elt-0.1.0/src/det/runtime/approval.py +756 -0
- det_elt-0.1.0/src/det/runtime/biglake_register.py +380 -0
- det_elt-0.1.0/src/det/runtime/bronze_land.py +145 -0
- det_elt-0.1.0/src/det/runtime/check.py +578 -0
- det_elt-0.1.0/src/det/runtime/coerce.py +182 -0
- det_elt-0.1.0/src/det/runtime/config.py +973 -0
- det_elt-0.1.0/src/det/runtime/dbt_runner.py +340 -0
- det_elt-0.1.0/src/det/runtime/discovery.py +388 -0
- det_elt-0.1.0/src/det/runtime/dlt_hygiene.py +185 -0
- det_elt-0.1.0/src/det/runtime/ids.py +133 -0
- det_elt-0.1.0/src/det/runtime/lake.py +1302 -0
- det_elt-0.1.0/src/det/runtime/layout.py +30 -0
- det_elt-0.1.0/src/det/runtime/lease/__init__.py +282 -0
- det_elt-0.1.0/src/det/runtime/lease/_common.py +175 -0
- det_elt-0.1.0/src/det/runtime/lease/dataset_lock.py +317 -0
- det_elt-0.1.0/src/det/runtime/lease/dataset_lock_body.py +179 -0
- det_elt-0.1.0/src/det/runtime/lease/dataset_lock_lake.py +389 -0
- det_elt-0.1.0/src/det/runtime/lease/dataset_lock_postgres.py +638 -0
- det_elt-0.1.0/src/det/runtime/lease/dataset_lock_types.py +96 -0
- det_elt-0.1.0/src/det/runtime/lease/lake_store.py +238 -0
- det_elt-0.1.0/src/det/runtime/lease/postgres_store.py +581 -0
- det_elt-0.1.0/src/det/runtime/lease/resolve.py +140 -0
- det_elt-0.1.0/src/det/runtime/lease/store.py +104 -0
- det_elt-0.1.0/src/det/runtime/load_rows.py +77 -0
- det_elt-0.1.0/src/det/runtime/manifest.py +151 -0
- det_elt-0.1.0/src/det/runtime/manifest_types.py +41 -0
- det_elt-0.1.0/src/det/runtime/mappers.py +13 -0
- det_elt-0.1.0/src/det/runtime/meta.py +146 -0
- det_elt-0.1.0/src/det/runtime/migrate.py +821 -0
- det_elt-0.1.0/src/det/runtime/naming.py +81 -0
- det_elt-0.1.0/src/det/runtime/object_store.py +250 -0
- det_elt-0.1.0/src/det/runtime/pipelines.py +190 -0
- det_elt-0.1.0/src/det/runtime/prune.py +520 -0
- det_elt-0.1.0/src/det/runtime/receipt_types.py +34 -0
- det_elt-0.1.0/src/det/runtime/receipts.py +590 -0
- det_elt-0.1.0/src/det/runtime/receipts_materialize.py +258 -0
- det_elt-0.1.0/src/det/runtime/registry.py +199 -0
- det_elt-0.1.0/src/det/runtime/runner.py +470 -0
- det_elt-0.1.0/src/det/runtime/secrets.py +425 -0
- det_elt-0.1.0/src/det/runtime/settings.py +325 -0
- det_elt-0.1.0/src/det/runtime/slo.py +237 -0
- det_elt-0.1.0/src/det/runtime/sql_types.py +232 -0
- det_elt-0.1.0/src/det/scaffold/__init__.py +16 -0
- det_elt-0.1.0/src/det/scaffold/adapt_scope.py +215 -0
- det_elt-0.1.0/src/det/scaffold/dbt.py +341 -0
- det_elt-0.1.0/src/det/scaffold/dbt_sql.py +712 -0
- det_elt-0.1.0/src/det/scaffold/dbt_yaml.py +435 -0
- det_elt-0.1.0/src/det/scaffold/flatten.py +248 -0
- det_elt-0.1.0/src/det/scaffold/init_pipeline.py +200 -0
- det_elt-0.1.0/src/det/scaffold/init_source.py +214 -0
- det_elt-0.1.0/src/det/scaffold/templates/silver.sql.j2 +55 -0
- det_elt-0.1.0/src/det/scaffold/templates/silver_relation.sql.j2 +38 -0
- det_elt-0.1.0/src/det/scaffold/templates/sources.yml.j2 +16 -0
- det_elt-0.1.0/src/det/scaffold/templates/stg.sql.j2 +14 -0
- det_elt-0.1.0/src/det/scaffold/templates/stg_relation.sql.j2 +34 -0
- det_elt-0.1.0/src/det/scaffold/view_warn.py +207 -0
- det_elt-0.1.0/src/det/sources/__init__.py +1 -0
- det_elt-0.1.0/src/det/sources/base.py +82 -0
- det_elt-0.1.0/src/det/sources/example_api/__init__.py +1 -0
- det_elt-0.1.0/src/det/sources/example_api/events.py +134 -0
- det_elt-0.1.0/src/det/sources/example_api/orders.py +275 -0
- det_elt-0.1.0/src/det/sources/http.py +500 -0
- det_elt-0.1.0/src/det/sources/http_json.py +120 -0
- det_elt-0.1.0/src/det/sources/noaa/__init__.py +1 -0
- det_elt-0.1.0/src/det/sources/noaa/fatalities.py +26 -0
- det_elt-0.1.0/src/det/sources/noaa/locations.py +26 -0
- det_elt-0.1.0/src/det/sources/noaa/storm_events.py +315 -0
- det_elt-0.1.0/src/det/sources/openlibrary/__init__.py +1 -0
- det_elt-0.1.0/src/det/sources/openlibrary/subjects.py +199 -0
- det_elt-0.1.0/src/det/testing/__init__.py +27 -0
- det_elt-0.1.0/src/det/testing/asserts.py +90 -0
- det_elt-0.1.0/src/det/testing/extract.py +113 -0
- det_elt-0.1.0/src/det/testing/project.py +169 -0
- det_elt-0.1.0/src/det/testing/pytest.py +33 -0
- det_elt-0.1.0/src/det/testing/registry.py +67 -0
- det_elt-0.1.0/src/det/testing/run.py +36 -0
- det_elt-0.1.0/src/det/testing/secrets.py +22 -0
- det_elt-0.1.0/src/det/validation/__init__.py +1 -0
- det_elt-0.1.0/src/det/validation/jsonschema_validator.py +66 -0
- det_elt-0.1.0/src/det_elt.egg-info/PKG-INFO +349 -0
- det_elt-0.1.0/src/det_elt.egg-info/SOURCES.txt +142 -0
- det_elt-0.1.0/src/det_elt.egg-info/dependency_links.txt +1 -0
- det_elt-0.1.0/src/det_elt.egg-info/entry_points.txt +3 -0
- det_elt-0.1.0/src/det_elt.egg-info/requires.txt +46 -0
- det_elt-0.1.0/src/det_elt.egg-info/top_level.txt +1 -0
det_elt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kadeem
|
|
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.
|
det_elt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: det-elt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DET — Data Extract Tool: config-driven extraction runtime with pluggable ingestion
|
|
5
|
+
Author-email: Kadeem <kletts2000@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/codekid/det
|
|
8
|
+
Project-URL: Repository, https://github.com/codekid/det
|
|
9
|
+
Project-URL: Issues, https://github.com/codekid/det/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/codekid/det/blob/main/docs/getting-started-library.md
|
|
11
|
+
Keywords: etl,data-extraction,pipeline,iceberg,bronze
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Python: <3.14,>=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: jsonschema>=4.23.0
|
|
22
|
+
Requires-Dist: pendulum>=3.0.0
|
|
23
|
+
Requires-Dist: pydantic>=2.10.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
25
|
+
Requires-Dist: requests>=2.32.0
|
|
26
|
+
Requires-Dist: structlog>=25.1.0
|
|
27
|
+
Requires-Dist: typer>=0.15.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.3.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.9.0; extra == "dev"
|
|
32
|
+
Provides-Extra: examples
|
|
33
|
+
Requires-Dist: beautifulsoup4>=4.12.0; extra == "examples"
|
|
34
|
+
Requires-Dist: dlt[filesystem]>=1.24.0; extra == "examples"
|
|
35
|
+
Provides-Extra: duckdb
|
|
36
|
+
Requires-Dist: duckdb>=1.5.1; extra == "duckdb"
|
|
37
|
+
Provides-Extra: scaffold
|
|
38
|
+
Requires-Dist: jinja2>=3.1.0; extra == "scaffold"
|
|
39
|
+
Provides-Extra: dbt
|
|
40
|
+
Requires-Dist: dbt-core>=1.9; extra == "dbt"
|
|
41
|
+
Requires-Dist: dbt-duckdb>=1.9; extra == "dbt"
|
|
42
|
+
Provides-Extra: bigquery
|
|
43
|
+
Requires-Dist: dbt-bigquery>=1.9; extra == "bigquery"
|
|
44
|
+
Requires-Dist: google-cloud-bigquery>=3.0; extra == "bigquery"
|
|
45
|
+
Provides-Extra: postgres
|
|
46
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "postgres"
|
|
47
|
+
Provides-Extra: s3
|
|
48
|
+
Requires-Dist: s3fs>=2024.0.0; extra == "s3"
|
|
49
|
+
Provides-Extra: gcs
|
|
50
|
+
Requires-Dist: gcsfs>=2024.0.0; extra == "gcs"
|
|
51
|
+
Provides-Extra: iceberg
|
|
52
|
+
Requires-Dist: pyiceberg>=0.8; extra == "iceberg"
|
|
53
|
+
Requires-Dist: pyarrow>=15; extra == "iceberg"
|
|
54
|
+
Provides-Extra: mcp
|
|
55
|
+
Requires-Dist: mcp<2,>=1.2; extra == "mcp"
|
|
56
|
+
Dynamic: license-file
|
|
57
|
+
|
|
58
|
+
# DET — Data Extract Tool
|
|
59
|
+
|
|
60
|
+
Extract → **raw** (wire bytes) → **bronze** (typed Iceberg). **dbt** owns silver and gold.
|
|
61
|
+
**dlt never lands bronze** — it may help HTTP; DET owns validation, meta, and writers.
|
|
62
|
+
|
|
63
|
+
## Choose your path
|
|
64
|
+
|
|
65
|
+
| Audience | Start here |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| **Operator** (this monorepo: CLI, examples, dbt, Airflow, MCP) | [Try it](#try-it-fixtures-no-noaa-download) below |
|
|
68
|
+
| **Library** (embed extract→raw→bronze in your app) | **[docs/getting-started-library.md](docs/getting-started-library.md)** · [docs/api.md](docs/api.md) |
|
|
69
|
+
|
|
70
|
+
This checkout is the operator product **and** the library source. Embedders install
|
|
71
|
+
the PyPI package **`det-elt`** (import `det`) and keep pipelines under
|
|
72
|
+
*their* `project_root` — they do not need this monorepo’s dbt/Airflow layout.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Try it (fixtures, no NOAA download)
|
|
77
|
+
|
|
78
|
+
Python 3.12+ and [uv](https://github.com/astral-sh/uv):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv venv && make install # recommended extras (includes iceberg + examples)
|
|
82
|
+
export DET_LAKE_PATH="$PWD/data/lake"
|
|
83
|
+
|
|
84
|
+
uv run det run -p noaa.storm_events -s 2026-08-06 \
|
|
85
|
+
--set source.overrides.local_csv_dir=fixtures/storm_events \
|
|
86
|
+
--set source.overrides.filename_substr=details
|
|
87
|
+
|
|
88
|
+
uv run det dbt -p noaa.storm_events
|
|
89
|
+
duckdb data/analytics.duckdb -c "select * from gold.gold_yearly_damage"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
You should see:
|
|
93
|
+
|
|
94
|
+
- Raw: `data/lake/raw/noaa/storm_events_v1/…/data/` + `meta/manifest.json`
|
|
95
|
+
- Bronze: Iceberg table at `data/lake/bronze/noaa/storm_events_v1/`
|
|
96
|
+
- Silver/gold in `data/analytics.duckdb`
|
|
97
|
+
|
|
98
|
+
`make run-local` is a **thin JSONL** smoke (`destination.type=filesystem`). It does not
|
|
99
|
+
feed Iceberg `iceberg_scan` in dbt. Use the commands above for the full path, or
|
|
100
|
+
`make all` only if you intend JSONL bronze.
|
|
101
|
+
|
|
102
|
+
Use `uv run det …` (or `make …`). If macOS hides the editable `.pth`, `make unhide` or
|
|
103
|
+
`PYTHONPATH=src .venv/bin/det …`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Mental model
|
|
108
|
+
|
|
109
|
+
```mermaid
|
|
110
|
+
flowchart LR
|
|
111
|
+
src[Source plugin] --> extract
|
|
112
|
+
extract --> raw[Raw lake]
|
|
113
|
+
raw --> load
|
|
114
|
+
load --> bronze[Iceberg bronze]
|
|
115
|
+
bronze --> stg[dbt stg]
|
|
116
|
+
stg --> silver --> gold
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Layer | Owner | Rule |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| Raw | DET | Append extract runs; rebuild source of truth |
|
|
122
|
+
| Bronze | DET | Typed landing; prune bronze only, never raw |
|
|
123
|
+
| Silver / gold | dbt | Dedupe and marts; latest extract wins |
|
|
124
|
+
|
|
125
|
+
**Interval:** `-s` inclusive start, `-e` exclusive end (default start + 1 day).
|
|
126
|
+
`det run` shares one `__extract_run_datetime` across raw, bronze, and rows.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Install extras
|
|
131
|
+
|
|
132
|
+
Base `det` is the runtime + CLI (no Iceberg / DuckDB / example HTTP deps).
|
|
133
|
+
**Recommended first line:** install with the `iceberg` extra.
|
|
134
|
+
|
|
135
|
+
| Extra | For |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `iceberg` | **Recommended** — default lake bronze |
|
|
138
|
+
| `examples` | In-tree HTTP sources (`dlt`, BeautifulSoup) |
|
|
139
|
+
| `duckdb` | DuckDB destination / DuckDB-backed prune |
|
|
140
|
+
| `scaffold` | Jinja2 dbt/pipeline scaffolding |
|
|
141
|
+
| `dbt` | `det dbt` |
|
|
142
|
+
| `postgres` | SQL serving destination |
|
|
143
|
+
| `s3` / `gcs` | Object-store lake URI |
|
|
144
|
+
| `mcp` | Cursor inspect / dry-run server |
|
|
145
|
+
| `dev` | pytest, ruff |
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# App / embed (PyPI):
|
|
149
|
+
uv pip install "det-elt[iceberg]"
|
|
150
|
+
uv pip install "det-elt[iceberg,duckdb,dbt]"
|
|
151
|
+
# This checkout (editable):
|
|
152
|
+
uv pip install -e ".[iceberg]" # recommended
|
|
153
|
+
uv pip install -e ".[iceberg,duckdb,dbt]" # typical app repo
|
|
154
|
+
make install # full extras for operator dev
|
|
155
|
+
make test
|
|
156
|
+
uv run det check
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Pipeline YAML
|
|
162
|
+
|
|
163
|
+
Canonical id is `provider.source` (`-p noaa.storm_events`). Defaults live in the
|
|
164
|
+
plugin; YAML wires schema, destination, and optional dbt knobs. Plugins are
|
|
165
|
+
discovered from `src/det/sources/<provider>/<source>.py` (`name` must equal
|
|
166
|
+
`provider.source`) — they are not listed in `plugins.py`. Out-of-tree packages may
|
|
167
|
+
use entry points `det.sources` / `det.mappers`.
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
name: noaa.storm_events
|
|
171
|
+
source:
|
|
172
|
+
type: noaa.storm_events
|
|
173
|
+
destination:
|
|
174
|
+
type: iceberg # default lake; filesystem = JSONL; duckdb / postgres = SQL
|
|
175
|
+
partition: extract_run # Iceberg only; omit = extract_run; small tables: none
|
|
176
|
+
wire_version: 1 # lake id is always {name}_vN (including _v1)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Schema defaults to `schemas/<provider>/<source>/<source>.schema.yaml`.
|
|
180
|
+
Greenfield: `det init-pipeline --name example_api.events --source-type example_api.events`.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Destinations
|
|
185
|
+
|
|
186
|
+
Lake root: `DET_LAKE_PATH` / `--lake-path` (default `./data/lake`). Same hive under
|
|
187
|
+
one root (`raw/` + `bronze/` prefixes) — dual buckets are not supported. There is
|
|
188
|
+
no `destination.type: s3`.
|
|
189
|
+
|
|
190
|
+
**`DET_LAKE_MODE`** (policy around the URI; unset → `local`):
|
|
191
|
+
|
|
192
|
+
| Mode | Allowed lake | Typical use |
|
|
193
|
+
| --- | --- | --- |
|
|
194
|
+
| `local` | filesystem path or `memory://` (tests) | laptop, CI default suite, Compose default |
|
|
195
|
+
| `cloud` | `s3://…` or `gs://…` / `gcs://…` | object store; CI MinIO soak covers S3 extract→Iceberg→`iceberg_scan`/`det dbt`; GCS soak covers extract→Iceberg (PyIceberg) |
|
|
196
|
+
|
|
197
|
+
`--lake-path` cannot bypass mode. `det check` errors on mismatch and warns when
|
|
198
|
+
`mode=cloud`. Compose: `DET_LAKE_MODE` + overridable `DET_LAKE_PATH` (see
|
|
199
|
+
`airflow/.env.example`). Local analytics/ops DuckDB stay on the worker filesystem.
|
|
200
|
+
Prod on GCS: BigQuery reads Iceberg bronze via **BigLake** — see
|
|
201
|
+
[docs/gcp-biglake.md](docs/gcp-biglake.md) (architecture C).
|
|
202
|
+
|
|
203
|
+
| `destination.type` | Bronze |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| **`iceberg`** | Default. Parquet table at `<lake>/bronze/<provider>/<source>_vN/` |
|
|
206
|
+
| `filesystem` | Hive JSONL (thin / fixtures). Cannot share that path with Iceberg |
|
|
207
|
+
| `duckdb` | `bronze_{provider}.{source}_vN` — needs `connection` |
|
|
208
|
+
| `postgres` | Same SQL names — `connection_env: DET_POSTGRES_DSN` (never a DSN in YAML) |
|
|
209
|
+
|
|
210
|
+
There is **no** `destination.type: bigquery`. On `gs://`, bronze stays Iceberg;
|
|
211
|
+
BQ is a reader (BigLake), not a DET lander.
|
|
212
|
+
|
|
213
|
+
Iceberg-only: `destination.partition` is `extract_run` (default — identity on
|
|
214
|
+
`__extract_run_datetime` for load replace / silver watermark) or `none`
|
|
215
|
+
(unpartitioned; use for small tables). Spec applies on **create** only. Live
|
|
216
|
+
mismatch **hard-fails** until `det migrate … --recreate-iceberg` (full table
|
|
217
|
+
purge, then rewrite `-s`/`-e` or `--all-raw`; latest raw per interval unless
|
|
218
|
+
`--all-raw-runs`) or a manual wipe. Not the raw hive layout.
|
|
219
|
+
|
|
220
|
+
`det dbt -p …` sets `DET_BRONZE_SOURCE` from the pipeline (`iceberg` → `iceberg_scan`
|
|
221
|
+
in `sources.yml` for DuckDB). On `s3://` lakes, `det dbt` auto-selects profile
|
|
222
|
+
target `duckdb_s3` (httpfs + S3 secret from the same `AWS_*` as extract/load).
|
|
223
|
+
On `gs://`, it does **not** force DuckDB S3 — set `DET_DBT_TARGET=bigquery` /
|
|
224
|
+
`--target bigquery` for BigLake-backed silver/gold/ops ([docs/gcp-biglake.md](docs/gcp-biglake.md)).
|
|
225
|
+
Register BigLake tables with `det biglake-register` (dry-run → `det approve` → `--apply`).
|
|
226
|
+
See [docs/gcp-biglake.md](docs/gcp-biglake.md) for connection IAM and sandbox teardown.
|
|
227
|
+
Layout contract: [docs/lake-layout.md](docs/lake-layout.md).
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Everyday CLI
|
|
232
|
+
|
|
233
|
+
`-p` is a canonical id, `provider/source`, or a YAML path. Project root:
|
|
234
|
+
`--project-root` > `DET_PROJECT_ROOT` > cwd.
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
det run -p noaa.storm_events -s 2026-08-06
|
|
238
|
+
det extract -p noaa.storm_events -s 2026-08-06
|
|
239
|
+
det load -p noaa.storm_events -s 2026-08-06
|
|
240
|
+
det dbt -p noaa.storm_events
|
|
241
|
+
det check # CI runs det check --strict (warnings fail the build)
|
|
242
|
+
det list-pipelines
|
|
243
|
+
det list-sources
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Command | When |
|
|
247
|
+
| --- | --- |
|
|
248
|
+
| `det prune -p … -s … --keep 1 --dry-run` then `--apply` | Drop old bronze extract-run siblings (never raw) |
|
|
249
|
+
| `det migrate -p … --to-bronze … --schema … --mapper identity -s … -e …` | Rebuild bronze from raw after a contract change |
|
|
250
|
+
| `det scaffold-dbt -p …` | Emit stg/silver + `sources.yml` |
|
|
251
|
+
| `det runs` / `det runs-materialize` | Attempt receipts; optional ops Iceberg + `det dbt --select tag:ops` |
|
|
252
|
+
| `det lock-show` / `lock-release --force` | Lake lease on `(pipeline, interval)` if a worker died |
|
|
253
|
+
|
|
254
|
+
Logs: console on a laptop TTY, JSON off TTY (`DET_LOG_FORMAT`). Secrets: names in
|
|
255
|
+
YAML (`auth_env`, `connection_env`); values in env. `det check` fails passwordful DSNs
|
|
256
|
+
in committed config.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## dbt
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
det dbt -p noaa.storm_events # stg_<provider>__<source>+ ; sets lake env
|
|
264
|
+
det dbt # full analytics project (excludes tag:ops)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Scaffolded `stg_*` use `det_bronze_from`. Gold is hand-written. Nested flatten /
|
|
268
|
+
relations: `dbt.stg` in pipeline YAML, then `det scaffold-dbt -p …`. Contract
|
|
269
|
+
triangle (schema → sources.yml → dbt.stg): [docs/contract-triangle.md](docs/contract-triangle.md).
|
|
270
|
+
More: [`.cursor/skills/det-dbt/SKILL.md`](.cursor/skills/det-dbt/SKILL.md).
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Local Airflow (dev only)
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
make airflow-up # http://localhost:8080 — airflow / airflow
|
|
278
|
+
make airflow-down
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Compose is **LocalExecutor**, not production. DAGs: extract → load, silver/gold dbt,
|
|
282
|
+
ops receipts — decoupled. Env: `airflow/.env.example`. Guide:
|
|
283
|
+
[`.cursor/skills/det-airflow/SKILL.md`](.cursor/skills/det-airflow/SKILL.md).
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Cube (local semantic layer)
|
|
288
|
+
|
|
289
|
+
Gold and ops metrics for agents. Not Cube Cloud MCP.
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
make cube-up # http://localhost:4000
|
|
293
|
+
make cube-down
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Compose reads `data/analytics.duckdb` (`yearly_damage`) and `data/det_ops.duckdb`
|
|
297
|
+
(`run_daily`). Copy `cube/.env.example` → `cube/.env`. MCP: `cube_meta` /
|
|
298
|
+
`cube_load` (`DET_CUBE_BASE_URL`, `DET_CUBE_API_SECRET`). Do not run `det dbt`
|
|
299
|
+
against a DuckDB file Cube has open. Certified metrics go through Cube; silver
|
|
300
|
+
or ops row detail uses MCP `query_analytics`.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## MCP (Cursor)
|
|
305
|
+
|
|
306
|
+
Read-only inspect + dry-run (no extract/load/prune-apply/DagRuns).
|
|
307
|
+
`uv pip install -e ".[mcp]"`; [`.cursor/mcp.json`](.cursor/mcp.json) already launches it.
|
|
308
|
+
Agent contract: [AGENTS.md](AGENTS.md). Tools + policy:
|
|
309
|
+
[`.cursor/rules/det-mcp.mdc`](.cursor/rules/det-mcp.mdc).
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Repo map
|
|
314
|
+
|
|
315
|
+
```text
|
|
316
|
+
src/det/ CLI, runtime, example sources, writers, det.testing
|
|
317
|
+
configs/pipelines/ provider.source YAML
|
|
318
|
+
schemas/ bronze JSON Schema
|
|
319
|
+
docs/api.md public Python API (SemVer / __all__)
|
|
320
|
+
docs/getting-started-library.md embedder first hour
|
|
321
|
+
docs/lake-layout.md hive / SQL compatibility
|
|
322
|
+
docs/gcp-biglake.md gs:// Iceberg + BigLake + dbt-BQ (architecture C)
|
|
323
|
+
dbt/ silver + gold + ops
|
|
324
|
+
cube/ local Cube Core (gold + ops metrics)
|
|
325
|
+
dags/ + airflow/ local Compose
|
|
326
|
+
fixtures/ offline NOAA CSVs
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Example sources implement `extract_to_raw` / `records_from_raw`. Land near-wire bytes;
|
|
330
|
+
unexpected fields fail JSON Schema. Analytics renames live in `dbt.stg`. True wire
|
|
331
|
+
breaks bump `wire_version`. dlt: `RESTClient` / `@dlt.resource` as iterators only —
|
|
332
|
+
never `dlt.pipeline` for landing.
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Troubleshooting
|
|
337
|
+
|
|
338
|
+
| Symptom | Fix |
|
|
339
|
+
| --- | --- |
|
|
340
|
+
| `No module named 'det'` | `make unhide` or `PYTHONPATH=src .venv/bin/det` (macOS hidden `.pth`) |
|
|
341
|
+
| `dbt CLI not found` | `uv run det dbt` so the venv `dbt` is on PATH |
|
|
342
|
+
| `No raw partitions` | Load the **same** `-s`/`-e` you extracted |
|
|
343
|
+
| Iceberg partition YAML ≠ live | `det migrate … --recreate-iceberg` (or wipe the bronze table path); plain load/migrate hard-fails |
|
|
344
|
+
| Iceberg dbt vs JSONL lake | Don’t mix `make run-local` (filesystem) with `det dbt -p` (pipeline is iceberg) |
|
|
345
|
+
| DuckDB lock in Airflow | Absolute `DET_ANALYTICS_DUCKDB`; one dbt process at a time |
|
|
346
|
+
| Cube MCP `cube_unavailable` | `make cube-up`; copy `cube/.env.example` → `cube/.env` |
|
|
347
|
+
| DuckDB lock with Cube | Do not `det dbt` while Cube has that file open |
|
|
348
|
+
| `DET_LAKE_MODE=local forbids…` / `requires an s3://` | Align mode and path: local + filesystem, or cloud + `s3://`/`gs://` |
|
|
349
|
+
| GCS soak / localgcp | Set `STORAGE_EMULATOR_HOST` (`http://127.0.0.1:4443`), `GOOGLE_CLOUD_PROJECT`, `DET_GCS_BUCKET`; `pytest -m gcs`. BigLake needs a real GCP project — see [docs/gcp-biglake.md](docs/gcp-biglake.md) |
|
det_elt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# DET — Data Extract Tool
|
|
2
|
+
|
|
3
|
+
Extract → **raw** (wire bytes) → **bronze** (typed Iceberg). **dbt** owns silver and gold.
|
|
4
|
+
**dlt never lands bronze** — it may help HTTP; DET owns validation, meta, and writers.
|
|
5
|
+
|
|
6
|
+
## Choose your path
|
|
7
|
+
|
|
8
|
+
| Audience | Start here |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| **Operator** (this monorepo: CLI, examples, dbt, Airflow, MCP) | [Try it](#try-it-fixtures-no-noaa-download) below |
|
|
11
|
+
| **Library** (embed extract→raw→bronze in your app) | **[docs/getting-started-library.md](docs/getting-started-library.md)** · [docs/api.md](docs/api.md) |
|
|
12
|
+
|
|
13
|
+
This checkout is the operator product **and** the library source. Embedders install
|
|
14
|
+
the PyPI package **`det-elt`** (import `det`) and keep pipelines under
|
|
15
|
+
*their* `project_root` — they do not need this monorepo’s dbt/Airflow layout.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Try it (fixtures, no NOAA download)
|
|
20
|
+
|
|
21
|
+
Python 3.12+ and [uv](https://github.com/astral-sh/uv):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv venv && make install # recommended extras (includes iceberg + examples)
|
|
25
|
+
export DET_LAKE_PATH="$PWD/data/lake"
|
|
26
|
+
|
|
27
|
+
uv run det run -p noaa.storm_events -s 2026-08-06 \
|
|
28
|
+
--set source.overrides.local_csv_dir=fixtures/storm_events \
|
|
29
|
+
--set source.overrides.filename_substr=details
|
|
30
|
+
|
|
31
|
+
uv run det dbt -p noaa.storm_events
|
|
32
|
+
duckdb data/analytics.duckdb -c "select * from gold.gold_yearly_damage"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
You should see:
|
|
36
|
+
|
|
37
|
+
- Raw: `data/lake/raw/noaa/storm_events_v1/…/data/` + `meta/manifest.json`
|
|
38
|
+
- Bronze: Iceberg table at `data/lake/bronze/noaa/storm_events_v1/`
|
|
39
|
+
- Silver/gold in `data/analytics.duckdb`
|
|
40
|
+
|
|
41
|
+
`make run-local` is a **thin JSONL** smoke (`destination.type=filesystem`). It does not
|
|
42
|
+
feed Iceberg `iceberg_scan` in dbt. Use the commands above for the full path, or
|
|
43
|
+
`make all` only if you intend JSONL bronze.
|
|
44
|
+
|
|
45
|
+
Use `uv run det …` (or `make …`). If macOS hides the editable `.pth`, `make unhide` or
|
|
46
|
+
`PYTHONPATH=src .venv/bin/det …`.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Mental model
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
flowchart LR
|
|
54
|
+
src[Source plugin] --> extract
|
|
55
|
+
extract --> raw[Raw lake]
|
|
56
|
+
raw --> load
|
|
57
|
+
load --> bronze[Iceberg bronze]
|
|
58
|
+
bronze --> stg[dbt stg]
|
|
59
|
+
stg --> silver --> gold
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Layer | Owner | Rule |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Raw | DET | Append extract runs; rebuild source of truth |
|
|
65
|
+
| Bronze | DET | Typed landing; prune bronze only, never raw |
|
|
66
|
+
| Silver / gold | dbt | Dedupe and marts; latest extract wins |
|
|
67
|
+
|
|
68
|
+
**Interval:** `-s` inclusive start, `-e` exclusive end (default start + 1 day).
|
|
69
|
+
`det run` shares one `__extract_run_datetime` across raw, bronze, and rows.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Install extras
|
|
74
|
+
|
|
75
|
+
Base `det` is the runtime + CLI (no Iceberg / DuckDB / example HTTP deps).
|
|
76
|
+
**Recommended first line:** install with the `iceberg` extra.
|
|
77
|
+
|
|
78
|
+
| Extra | For |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `iceberg` | **Recommended** — default lake bronze |
|
|
81
|
+
| `examples` | In-tree HTTP sources (`dlt`, BeautifulSoup) |
|
|
82
|
+
| `duckdb` | DuckDB destination / DuckDB-backed prune |
|
|
83
|
+
| `scaffold` | Jinja2 dbt/pipeline scaffolding |
|
|
84
|
+
| `dbt` | `det dbt` |
|
|
85
|
+
| `postgres` | SQL serving destination |
|
|
86
|
+
| `s3` / `gcs` | Object-store lake URI |
|
|
87
|
+
| `mcp` | Cursor inspect / dry-run server |
|
|
88
|
+
| `dev` | pytest, ruff |
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# App / embed (PyPI):
|
|
92
|
+
uv pip install "det-elt[iceberg]"
|
|
93
|
+
uv pip install "det-elt[iceberg,duckdb,dbt]"
|
|
94
|
+
# This checkout (editable):
|
|
95
|
+
uv pip install -e ".[iceberg]" # recommended
|
|
96
|
+
uv pip install -e ".[iceberg,duckdb,dbt]" # typical app repo
|
|
97
|
+
make install # full extras for operator dev
|
|
98
|
+
make test
|
|
99
|
+
uv run det check
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Pipeline YAML
|
|
105
|
+
|
|
106
|
+
Canonical id is `provider.source` (`-p noaa.storm_events`). Defaults live in the
|
|
107
|
+
plugin; YAML wires schema, destination, and optional dbt knobs. Plugins are
|
|
108
|
+
discovered from `src/det/sources/<provider>/<source>.py` (`name` must equal
|
|
109
|
+
`provider.source`) — they are not listed in `plugins.py`. Out-of-tree packages may
|
|
110
|
+
use entry points `det.sources` / `det.mappers`.
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
name: noaa.storm_events
|
|
114
|
+
source:
|
|
115
|
+
type: noaa.storm_events
|
|
116
|
+
destination:
|
|
117
|
+
type: iceberg # default lake; filesystem = JSONL; duckdb / postgres = SQL
|
|
118
|
+
partition: extract_run # Iceberg only; omit = extract_run; small tables: none
|
|
119
|
+
wire_version: 1 # lake id is always {name}_vN (including _v1)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Schema defaults to `schemas/<provider>/<source>/<source>.schema.yaml`.
|
|
123
|
+
Greenfield: `det init-pipeline --name example_api.events --source-type example_api.events`.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Destinations
|
|
128
|
+
|
|
129
|
+
Lake root: `DET_LAKE_PATH` / `--lake-path` (default `./data/lake`). Same hive under
|
|
130
|
+
one root (`raw/` + `bronze/` prefixes) — dual buckets are not supported. There is
|
|
131
|
+
no `destination.type: s3`.
|
|
132
|
+
|
|
133
|
+
**`DET_LAKE_MODE`** (policy around the URI; unset → `local`):
|
|
134
|
+
|
|
135
|
+
| Mode | Allowed lake | Typical use |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| `local` | filesystem path or `memory://` (tests) | laptop, CI default suite, Compose default |
|
|
138
|
+
| `cloud` | `s3://…` or `gs://…` / `gcs://…` | object store; CI MinIO soak covers S3 extract→Iceberg→`iceberg_scan`/`det dbt`; GCS soak covers extract→Iceberg (PyIceberg) |
|
|
139
|
+
|
|
140
|
+
`--lake-path` cannot bypass mode. `det check` errors on mismatch and warns when
|
|
141
|
+
`mode=cloud`. Compose: `DET_LAKE_MODE` + overridable `DET_LAKE_PATH` (see
|
|
142
|
+
`airflow/.env.example`). Local analytics/ops DuckDB stay on the worker filesystem.
|
|
143
|
+
Prod on GCS: BigQuery reads Iceberg bronze via **BigLake** — see
|
|
144
|
+
[docs/gcp-biglake.md](docs/gcp-biglake.md) (architecture C).
|
|
145
|
+
|
|
146
|
+
| `destination.type` | Bronze |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| **`iceberg`** | Default. Parquet table at `<lake>/bronze/<provider>/<source>_vN/` |
|
|
149
|
+
| `filesystem` | Hive JSONL (thin / fixtures). Cannot share that path with Iceberg |
|
|
150
|
+
| `duckdb` | `bronze_{provider}.{source}_vN` — needs `connection` |
|
|
151
|
+
| `postgres` | Same SQL names — `connection_env: DET_POSTGRES_DSN` (never a DSN in YAML) |
|
|
152
|
+
|
|
153
|
+
There is **no** `destination.type: bigquery`. On `gs://`, bronze stays Iceberg;
|
|
154
|
+
BQ is a reader (BigLake), not a DET lander.
|
|
155
|
+
|
|
156
|
+
Iceberg-only: `destination.partition` is `extract_run` (default — identity on
|
|
157
|
+
`__extract_run_datetime` for load replace / silver watermark) or `none`
|
|
158
|
+
(unpartitioned; use for small tables). Spec applies on **create** only. Live
|
|
159
|
+
mismatch **hard-fails** until `det migrate … --recreate-iceberg` (full table
|
|
160
|
+
purge, then rewrite `-s`/`-e` or `--all-raw`; latest raw per interval unless
|
|
161
|
+
`--all-raw-runs`) or a manual wipe. Not the raw hive layout.
|
|
162
|
+
|
|
163
|
+
`det dbt -p …` sets `DET_BRONZE_SOURCE` from the pipeline (`iceberg` → `iceberg_scan`
|
|
164
|
+
in `sources.yml` for DuckDB). On `s3://` lakes, `det dbt` auto-selects profile
|
|
165
|
+
target `duckdb_s3` (httpfs + S3 secret from the same `AWS_*` as extract/load).
|
|
166
|
+
On `gs://`, it does **not** force DuckDB S3 — set `DET_DBT_TARGET=bigquery` /
|
|
167
|
+
`--target bigquery` for BigLake-backed silver/gold/ops ([docs/gcp-biglake.md](docs/gcp-biglake.md)).
|
|
168
|
+
Register BigLake tables with `det biglake-register` (dry-run → `det approve` → `--apply`).
|
|
169
|
+
See [docs/gcp-biglake.md](docs/gcp-biglake.md) for connection IAM and sandbox teardown.
|
|
170
|
+
Layout contract: [docs/lake-layout.md](docs/lake-layout.md).
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Everyday CLI
|
|
175
|
+
|
|
176
|
+
`-p` is a canonical id, `provider/source`, or a YAML path. Project root:
|
|
177
|
+
`--project-root` > `DET_PROJECT_ROOT` > cwd.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
det run -p noaa.storm_events -s 2026-08-06
|
|
181
|
+
det extract -p noaa.storm_events -s 2026-08-06
|
|
182
|
+
det load -p noaa.storm_events -s 2026-08-06
|
|
183
|
+
det dbt -p noaa.storm_events
|
|
184
|
+
det check # CI runs det check --strict (warnings fail the build)
|
|
185
|
+
det list-pipelines
|
|
186
|
+
det list-sources
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| Command | When |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `det prune -p … -s … --keep 1 --dry-run` then `--apply` | Drop old bronze extract-run siblings (never raw) |
|
|
192
|
+
| `det migrate -p … --to-bronze … --schema … --mapper identity -s … -e …` | Rebuild bronze from raw after a contract change |
|
|
193
|
+
| `det scaffold-dbt -p …` | Emit stg/silver + `sources.yml` |
|
|
194
|
+
| `det runs` / `det runs-materialize` | Attempt receipts; optional ops Iceberg + `det dbt --select tag:ops` |
|
|
195
|
+
| `det lock-show` / `lock-release --force` | Lake lease on `(pipeline, interval)` if a worker died |
|
|
196
|
+
|
|
197
|
+
Logs: console on a laptop TTY, JSON off TTY (`DET_LOG_FORMAT`). Secrets: names in
|
|
198
|
+
YAML (`auth_env`, `connection_env`); values in env. `det check` fails passwordful DSNs
|
|
199
|
+
in committed config.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## dbt
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
det dbt -p noaa.storm_events # stg_<provider>__<source>+ ; sets lake env
|
|
207
|
+
det dbt # full analytics project (excludes tag:ops)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Scaffolded `stg_*` use `det_bronze_from`. Gold is hand-written. Nested flatten /
|
|
211
|
+
relations: `dbt.stg` in pipeline YAML, then `det scaffold-dbt -p …`. Contract
|
|
212
|
+
triangle (schema → sources.yml → dbt.stg): [docs/contract-triangle.md](docs/contract-triangle.md).
|
|
213
|
+
More: [`.cursor/skills/det-dbt/SKILL.md`](.cursor/skills/det-dbt/SKILL.md).
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Local Airflow (dev only)
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
make airflow-up # http://localhost:8080 — airflow / airflow
|
|
221
|
+
make airflow-down
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Compose is **LocalExecutor**, not production. DAGs: extract → load, silver/gold dbt,
|
|
225
|
+
ops receipts — decoupled. Env: `airflow/.env.example`. Guide:
|
|
226
|
+
[`.cursor/skills/det-airflow/SKILL.md`](.cursor/skills/det-airflow/SKILL.md).
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Cube (local semantic layer)
|
|
231
|
+
|
|
232
|
+
Gold and ops metrics for agents. Not Cube Cloud MCP.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
make cube-up # http://localhost:4000
|
|
236
|
+
make cube-down
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Compose reads `data/analytics.duckdb` (`yearly_damage`) and `data/det_ops.duckdb`
|
|
240
|
+
(`run_daily`). Copy `cube/.env.example` → `cube/.env`. MCP: `cube_meta` /
|
|
241
|
+
`cube_load` (`DET_CUBE_BASE_URL`, `DET_CUBE_API_SECRET`). Do not run `det dbt`
|
|
242
|
+
against a DuckDB file Cube has open. Certified metrics go through Cube; silver
|
|
243
|
+
or ops row detail uses MCP `query_analytics`.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## MCP (Cursor)
|
|
248
|
+
|
|
249
|
+
Read-only inspect + dry-run (no extract/load/prune-apply/DagRuns).
|
|
250
|
+
`uv pip install -e ".[mcp]"`; [`.cursor/mcp.json`](.cursor/mcp.json) already launches it.
|
|
251
|
+
Agent contract: [AGENTS.md](AGENTS.md). Tools + policy:
|
|
252
|
+
[`.cursor/rules/det-mcp.mdc`](.cursor/rules/det-mcp.mdc).
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Repo map
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
src/det/ CLI, runtime, example sources, writers, det.testing
|
|
260
|
+
configs/pipelines/ provider.source YAML
|
|
261
|
+
schemas/ bronze JSON Schema
|
|
262
|
+
docs/api.md public Python API (SemVer / __all__)
|
|
263
|
+
docs/getting-started-library.md embedder first hour
|
|
264
|
+
docs/lake-layout.md hive / SQL compatibility
|
|
265
|
+
docs/gcp-biglake.md gs:// Iceberg + BigLake + dbt-BQ (architecture C)
|
|
266
|
+
dbt/ silver + gold + ops
|
|
267
|
+
cube/ local Cube Core (gold + ops metrics)
|
|
268
|
+
dags/ + airflow/ local Compose
|
|
269
|
+
fixtures/ offline NOAA CSVs
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Example sources implement `extract_to_raw` / `records_from_raw`. Land near-wire bytes;
|
|
273
|
+
unexpected fields fail JSON Schema. Analytics renames live in `dbt.stg`. True wire
|
|
274
|
+
breaks bump `wire_version`. dlt: `RESTClient` / `@dlt.resource` as iterators only —
|
|
275
|
+
never `dlt.pipeline` for landing.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Troubleshooting
|
|
280
|
+
|
|
281
|
+
| Symptom | Fix |
|
|
282
|
+
| --- | --- |
|
|
283
|
+
| `No module named 'det'` | `make unhide` or `PYTHONPATH=src .venv/bin/det` (macOS hidden `.pth`) |
|
|
284
|
+
| `dbt CLI not found` | `uv run det dbt` so the venv `dbt` is on PATH |
|
|
285
|
+
| `No raw partitions` | Load the **same** `-s`/`-e` you extracted |
|
|
286
|
+
| Iceberg partition YAML ≠ live | `det migrate … --recreate-iceberg` (or wipe the bronze table path); plain load/migrate hard-fails |
|
|
287
|
+
| Iceberg dbt vs JSONL lake | Don’t mix `make run-local` (filesystem) with `det dbt -p` (pipeline is iceberg) |
|
|
288
|
+
| DuckDB lock in Airflow | Absolute `DET_ANALYTICS_DUCKDB`; one dbt process at a time |
|
|
289
|
+
| Cube MCP `cube_unavailable` | `make cube-up`; copy `cube/.env.example` → `cube/.env` |
|
|
290
|
+
| DuckDB lock with Cube | Do not `det dbt` while Cube has that file open |
|
|
291
|
+
| `DET_LAKE_MODE=local forbids…` / `requires an s3://` | Align mode and path: local + filesystem, or cloud + `s3://`/`gs://` |
|
|
292
|
+
| GCS soak / localgcp | Set `STORAGE_EMULATOR_HOST` (`http://127.0.0.1:4443`), `GOOGLE_CLOUD_PROJECT`, `DET_GCS_BUCKET`; `pytest -m gcs`. BigLake needs a real GCP project — see [docs/gcp-biglake.md](docs/gcp-biglake.md) |
|