dbdocs 1.5.0__tar.gz → 1.6.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dbdocs-1.5.0 → dbdocs-1.6.1}/PKG-INFO +2 -1
- {dbdocs-1.5.0 → dbdocs-1.6.1}/README.md +1 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/core/artifacts.py +12 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/core/config.py +5 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/graph.py +14 -7
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/extractor.py +25 -10
- dbdocs-1.6.1/dbdocs/extract/nodes.py +518 -0
- dbdocs-1.6.1/dbdocs/extract/tests_index.py +85 -0
- dbdocs-1.6.1/dbdocs/site/api_schema.py +371 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/builder.py +158 -13
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/css/icons.css +2 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/css/style.css +174 -5
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/graph/index.css +1 -1
- dbdocs-1.6.1/dbdocs/site/bundle/assets/graph/index.js +62 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/js/service.js +186 -2
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/js/ui.js +838 -104
- {dbdocs-1.5.0 → dbdocs-1.6.1}/pyproject.toml +1 -0
- dbdocs-1.5.0/dbdocs/extract/nodes.py +0 -144
- dbdocs-1.5.0/dbdocs/site/bundle/assets/graph/index.js +0 -62
- {dbdocs-1.5.0 → dbdocs-1.6.1}/.gitignore +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/LICENSE +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/__main__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/cli/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/cli/main.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/core/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/core/exceptions.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/core/log.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/_sqlglot_lineage.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/column_lineage.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/erd.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/dimensions.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/base.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/documentation.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/governance.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/modeling.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/performance.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/structure.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/dimensions/testing.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/extract/health/rules/registry.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/main.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/__init__.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/favicon.svg +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/js/app.js +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/js/data.js +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/vendor/marked.min.js +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/assets/vendor/minisearch.min.js +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/bundle/index.html +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/deploy.py +0 -0
- {dbdocs-1.5.0 → dbdocs-1.6.1}/dbdocs/site/inject.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbdocs
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.1
|
|
4
4
|
Summary: Alternative dbt docs site: Catalog + ERD + column-level lineage
|
|
5
5
|
Project-URL: Homepage, https://github.com/datnguye/dbt-docs
|
|
6
6
|
Project-URL: Repository, https://github.com/datnguye/dbt-docs
|
|
@@ -56,6 +56,7 @@ dbt's built-in docs stop short of telling you *which upstream column fed this do
|
|
|
56
56
|
- **Project Health Check** across the six [dbt-project-evaluator](https://dbt-labs.github.io/dbt-project-evaluator/) dimensions.
|
|
57
57
|
- **Versioned deploys** with a built-in version switcher, no plugins.
|
|
58
58
|
- **Full-text search** across names, columns, descriptions, tags, and SQL at the client-side, no backend.
|
|
59
|
+
- **Static REST API** (`api/v1/`) — addressable JSON for every node, lineage, and health, for headless / agent consumption.
|
|
59
60
|
- **Dark / light theme.**
|
|
60
61
|
|
|
61
62
|
## Install
|
|
@@ -32,6 +32,7 @@ dbt's built-in docs stop short of telling you *which upstream column fed this do
|
|
|
32
32
|
- **Project Health Check** across the six [dbt-project-evaluator](https://dbt-labs.github.io/dbt-project-evaluator/) dimensions.
|
|
33
33
|
- **Versioned deploys** with a built-in version switcher, no plugins.
|
|
34
34
|
- **Full-text search** across names, columns, descriptions, tags, and SQL at the client-side, no backend.
|
|
35
|
+
- **Static REST API** (`api/v1/`) — addressable JSON for every node, lineage, and health, for headless / agent consumption.
|
|
35
36
|
- **Dark / light theme.**
|
|
36
37
|
|
|
37
38
|
## Install
|
|
@@ -25,6 +25,18 @@ UNKNOWN = "_unknown"
|
|
|
25
25
|
#: unique_id prefixes surfaced as catalog nodes (tests/macros/etc. excluded).
|
|
26
26
|
NODE_PREFIXES = ("model.", "seed.", "snapshot.")
|
|
27
27
|
|
|
28
|
+
#: unique_id prefixes for manifest.nodes entries that carry only code (no columns).
|
|
29
|
+
CODE_ONLY_PREFIXES = ("analysis.", "operation.")
|
|
30
|
+
|
|
31
|
+
#: Manifest collections outside manifest.nodes, mapping id_prefix → attribute name.
|
|
32
|
+
COLLECTION_ATTRS: "dict[str, str]" = {
|
|
33
|
+
"metric.": "metrics",
|
|
34
|
+
"semantic_model.": "semantic_models",
|
|
35
|
+
"saved_query.": "saved_queries",
|
|
36
|
+
"unit_test.": "unit_tests",
|
|
37
|
+
"exposure.": "exposures",
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
|
|
29
41
|
def artifact_version(target_path: str, artifact: str) -> "int | None":
|
|
30
42
|
"""Resolve a dbt artifact's schema version int from its ``dbt_schema_version``.
|
|
@@ -81,6 +81,11 @@ class DbDocsConfig:
|
|
|
81
81
|
#: dotted import path whose ``register_rule`` calls add custom rules). Entry
|
|
82
82
|
#: points under ``dbdocs.health_rules`` are also discovered automatically.
|
|
83
83
|
health: dict = field(default_factory=dict)
|
|
84
|
+
#: The pinned About link shows by default; set false to hide it.
|
|
85
|
+
show_about: bool = True
|
|
86
|
+
#: Links rendered on the About page as external CTA buttons. Each entry is a
|
|
87
|
+
#: ``{label, href}`` dict; empty by default — no extra links.
|
|
88
|
+
about_links: list = field(default_factory=list)
|
|
84
89
|
|
|
85
90
|
@classmethod
|
|
86
91
|
def load(cls, path: "str | Path | None" = None) -> "DbDocsConfig":
|
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
``LineageGraph`` turns the manifest's ``parent_map`` (falling back to per-node
|
|
4
4
|
``depends_on.nodes``) into directed parent→child edges plus adjacency maps,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
covering all surfaced resource types (models/seeds/snapshots/sources/analyses/
|
|
6
|
+
operations/metrics/semantic_models/saved_queries/unit_tests/exposures) so that
|
|
7
|
+
``dependsOn``/``referencedBy`` on new pages resolve correctly.
|
|
8
|
+
|
|
9
|
+
The visual DAG view filters by ``data-rtype`` in the graph bundle, so including
|
|
10
|
+
non-physical types in the lineage graph does not pollute the DAG display. Macros
|
|
11
|
+
and test nodes are still excluded — they are never surfaced as navigable pages.
|
|
8
12
|
"""
|
|
9
13
|
|
|
10
14
|
from typing import Any
|
|
11
15
|
|
|
12
|
-
from dbdocs.core.artifacts import NODE_PREFIXES
|
|
16
|
+
from dbdocs.core.artifacts import CODE_ONLY_PREFIXES, COLLECTION_ATTRS, NODE_PREFIXES
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
class LineageGraph:
|
|
@@ -17,17 +21,17 @@ class LineageGraph:
|
|
|
17
21
|
|
|
18
22
|
def __init__(self, manifest: Any, node_ids: "set | None" = None) -> None:
|
|
19
23
|
self.manifest = manifest
|
|
20
|
-
#: Restrict edges to these ids. Defaults to models/seeds/snapshots +
|
|
21
|
-
#: sources derived from the manifest, matching ``nodes.build_nodes``.
|
|
22
24
|
self.node_ids = node_ids if node_ids is not None else self._default_node_ids()
|
|
23
25
|
|
|
24
26
|
def _default_node_ids(self) -> set:
|
|
25
27
|
ids = {
|
|
26
28
|
uid
|
|
27
29
|
for uid in (getattr(self.manifest, "nodes", {}) or {})
|
|
28
|
-
if str(uid).startswith(NODE_PREFIXES)
|
|
30
|
+
if str(uid).startswith(NODE_PREFIXES + CODE_ONLY_PREFIXES)
|
|
29
31
|
}
|
|
30
32
|
ids.update(getattr(self.manifest, "sources", {}) or {})
|
|
33
|
+
for attr in COLLECTION_ATTRS.values():
|
|
34
|
+
ids.update(getattr(self.manifest, attr, None) or {})
|
|
31
35
|
return ids
|
|
32
36
|
|
|
33
37
|
def build(self) -> dict:
|
|
@@ -69,4 +73,7 @@ class LineageGraph:
|
|
|
69
73
|
def _lookup(self, unique_id: str) -> Any:
|
|
70
74
|
if str(unique_id).startswith("source."):
|
|
71
75
|
return (getattr(self.manifest, "sources", {}) or {}).get(unique_id)
|
|
76
|
+
for prefix, attr in COLLECTION_ATTRS.items():
|
|
77
|
+
if str(unique_id).startswith(prefix):
|
|
78
|
+
return (getattr(self.manifest, attr, None) or {}).get(unique_id)
|
|
72
79
|
return (getattr(self.manifest, "nodes", {}) or {}).get(unique_id)
|
|
@@ -34,6 +34,7 @@ from pydantic import ValidationError
|
|
|
34
34
|
|
|
35
35
|
from dbdocs.core.log import logger
|
|
36
36
|
from dbdocs.extract.health.dimensions import DimensionAnalyzer
|
|
37
|
+
from dbdocs.extract.tests_index import manifest_test_node_details, manifest_test_node_metadata
|
|
37
38
|
|
|
38
39
|
# ---------------------------------------------------------------------------
|
|
39
40
|
# Test type → category mapping
|
|
@@ -108,13 +109,21 @@ def _is_unit_test(unique_id: str) -> bool:
|
|
|
108
109
|
return unique_id.startswith(_UNIT_TEST_PREFIX)
|
|
109
110
|
|
|
110
111
|
|
|
112
|
+
#: Adapter-emitted aliases for a successful test result, normalized to ``"pass"``
|
|
113
|
+
#: so the status pills / per-test badges tally correctly. Snowflake and a few
|
|
114
|
+
#: other adapters emit ``"success"`` for an asserting test that returned 0 rows.
|
|
115
|
+
_PASS_ALIASES = {"success", "ok"}
|
|
116
|
+
|
|
117
|
+
|
|
111
118
|
def _status_value(status: Any) -> str:
|
|
112
|
-
"""The plain status string for a result.
|
|
119
|
+
"""The plain status string for a result, with adapter aliases normalized.
|
|
113
120
|
|
|
114
121
|
artifact-parser models ``status`` as a versioned enum (e.g. ``Status1.pass_``
|
|
115
122
|
with value ``"pass"``); fall back to ``str`` for anything that isn't enum-like.
|
|
123
|
+
Adapter aliases for success (``"success"``/``"ok"``) collapse to ``"pass"``.
|
|
116
124
|
"""
|
|
117
|
-
|
|
125
|
+
raw = str(getattr(status, "value", status) or "unknown").lower()
|
|
126
|
+
return "pass" if raw in _PASS_ALIASES else raw
|
|
118
127
|
|
|
119
128
|
|
|
120
129
|
def _short_name(unique_id: str) -> str:
|
|
@@ -204,9 +213,12 @@ class HealthCheckExtractor:
|
|
|
204
213
|
is_unit = _is_unit_test(uid)
|
|
205
214
|
if is_unit:
|
|
206
215
|
test_type, model, column, category = "unit_test", self._unit_test_model(uid), "", "unit"
|
|
216
|
+
description, kwargs = "", {}
|
|
207
217
|
else:
|
|
208
|
-
|
|
218
|
+
node = self._nodes.get(uid)
|
|
219
|
+
test_type, model, column = self._resolve_metadata(node, uid)
|
|
209
220
|
category = TEST_CATEGORIES.get(test_type, _FALLBACK_CATEGORY)
|
|
221
|
+
description, kwargs = self._resolve_details(node)
|
|
210
222
|
return {
|
|
211
223
|
"rule": _short_name(uid),
|
|
212
224
|
"kind": "unit" if is_unit else "data",
|
|
@@ -216,24 +228,27 @@ class HealthCheckExtractor:
|
|
|
216
228
|
"status": _status_value(getattr(result, "status", "unknown")),
|
|
217
229
|
"failures": int(getattr(result, "failures", 0) or 0),
|
|
218
230
|
"message": str(getattr(result, "message", "") or ""),
|
|
231
|
+
"description": description,
|
|
232
|
+
"kwargs": kwargs,
|
|
219
233
|
"category": category,
|
|
220
234
|
"docs_url": _UNIT_TESTS_DOCS if is_unit else _TESTS_DOCS,
|
|
221
235
|
}
|
|
222
236
|
|
|
223
|
-
|
|
237
|
+
@staticmethod
|
|
238
|
+
def _resolve_details(node: Any) -> "tuple[str, dict]":
|
|
239
|
+
"""Manifest description + kwargs for a data-test node, or ``("", {})``."""
|
|
240
|
+
return manifest_test_node_details(node) if node is not None else ("", {})
|
|
241
|
+
|
|
242
|
+
def _resolve_metadata(self, node: Any, unique_id: str) -> "tuple[str, str, str]":
|
|
224
243
|
"""``(test_type, model, column)`` for a *data* test, manifest-first.
|
|
225
244
|
|
|
226
245
|
Reads the authoritative test type / tested model / column from the
|
|
227
246
|
manifest node when available; falls back to inferring the type from the
|
|
228
247
|
``unique_id`` (model/column then unknown) when it isn't.
|
|
229
248
|
"""
|
|
230
|
-
node = self._nodes.get(unique_id)
|
|
231
249
|
if node is not None:
|
|
232
|
-
|
|
233
|
-
test_type
|
|
234
|
-
model = _short_name(str(getattr(node, "attached_node", "") or ""))
|
|
235
|
-
column = str(getattr(node, "column_name", "") or "")
|
|
236
|
-
return test_type, model, column
|
|
250
|
+
test_type, attached, column = manifest_test_node_metadata(node)
|
|
251
|
+
return test_type, _short_name(attached), column
|
|
237
252
|
return _infer_test_type(unique_id), "", ""
|
|
238
253
|
|
|
239
254
|
def _unit_test_model(self, unique_id: str) -> str:
|