trellis-datamodel 0.3.3__py3-none-any.whl

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 (52) hide show
  1. trellis_datamodel/__init__.py +8 -0
  2. trellis_datamodel/adapters/__init__.py +41 -0
  3. trellis_datamodel/adapters/base.py +147 -0
  4. trellis_datamodel/adapters/dbt_core.py +975 -0
  5. trellis_datamodel/cli.py +292 -0
  6. trellis_datamodel/config.py +239 -0
  7. trellis_datamodel/models/__init__.py +13 -0
  8. trellis_datamodel/models/schemas.py +28 -0
  9. trellis_datamodel/routes/__init__.py +11 -0
  10. trellis_datamodel/routes/data_model.py +221 -0
  11. trellis_datamodel/routes/manifest.py +110 -0
  12. trellis_datamodel/routes/schema.py +183 -0
  13. trellis_datamodel/server.py +101 -0
  14. trellis_datamodel/static/_app/env.js +1 -0
  15. trellis_datamodel/static/_app/immutable/assets/0.ByDwyx3a.css +1 -0
  16. trellis_datamodel/static/_app/immutable/assets/2.DLAp_5AW.css +1 -0
  17. trellis_datamodel/static/_app/immutable/assets/trellis_squared.CTOnsdDx.svg +127 -0
  18. trellis_datamodel/static/_app/immutable/chunks/8ZaN1sxc.js +1 -0
  19. trellis_datamodel/static/_app/immutable/chunks/BfBfOTnK.js +1 -0
  20. trellis_datamodel/static/_app/immutable/chunks/C3yhlRfZ.js +2 -0
  21. trellis_datamodel/static/_app/immutable/chunks/CK3bXPEX.js +1 -0
  22. trellis_datamodel/static/_app/immutable/chunks/CXDUumOQ.js +1 -0
  23. trellis_datamodel/static/_app/immutable/chunks/DDNfEvut.js +1 -0
  24. trellis_datamodel/static/_app/immutable/chunks/DUdVct7e.js +1 -0
  25. trellis_datamodel/static/_app/immutable/chunks/QRltG_J6.js +2 -0
  26. trellis_datamodel/static/_app/immutable/chunks/zXDdy2c_.js +1 -0
  27. trellis_datamodel/static/_app/immutable/entry/app.abCkWeAJ.js +2 -0
  28. trellis_datamodel/static/_app/immutable/entry/start.B7CjH6Z7.js +1 -0
  29. trellis_datamodel/static/_app/immutable/nodes/0.bFI_DI3G.js +1 -0
  30. trellis_datamodel/static/_app/immutable/nodes/1.J_r941Qf.js +1 -0
  31. trellis_datamodel/static/_app/immutable/nodes/2.WqbMkq6o.js +27 -0
  32. trellis_datamodel/static/_app/version.json +1 -0
  33. trellis_datamodel/static/index.html +40 -0
  34. trellis_datamodel/static/robots.txt +3 -0
  35. trellis_datamodel/static/trellis_squared.svg +127 -0
  36. trellis_datamodel/tests/__init__.py +2 -0
  37. trellis_datamodel/tests/conftest.py +132 -0
  38. trellis_datamodel/tests/test_cli.py +526 -0
  39. trellis_datamodel/tests/test_data_model.py +151 -0
  40. trellis_datamodel/tests/test_dbt_schema.py +892 -0
  41. trellis_datamodel/tests/test_manifest.py +72 -0
  42. trellis_datamodel/tests/test_server_static.py +44 -0
  43. trellis_datamodel/tests/test_yaml_handler.py +228 -0
  44. trellis_datamodel/utils/__init__.py +2 -0
  45. trellis_datamodel/utils/yaml_handler.py +365 -0
  46. trellis_datamodel-0.3.3.dist-info/METADATA +333 -0
  47. trellis_datamodel-0.3.3.dist-info/RECORD +52 -0
  48. trellis_datamodel-0.3.3.dist-info/WHEEL +5 -0
  49. trellis_datamodel-0.3.3.dist-info/entry_points.txt +2 -0
  50. trellis_datamodel-0.3.3.dist-info/licenses/LICENSE +661 -0
  51. trellis_datamodel-0.3.3.dist-info/licenses/NOTICE +6 -0
  52. trellis_datamodel-0.3.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ {"version":"1766080526003"}
@@ -0,0 +1,40 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <link rel="preconnect" href="https://fonts.googleapis.com">
7
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8
+ <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
9
+
10
+ <link rel="modulepreload" href="/_app/immutable/entry/start.B7CjH6Z7.js">
11
+ <link rel="modulepreload" href="/_app/immutable/chunks/CXDUumOQ.js">
12
+ <link rel="modulepreload" href="/_app/immutable/chunks/zXDdy2c_.js">
13
+ <link rel="modulepreload" href="/_app/immutable/chunks/8ZaN1sxc.js">
14
+ <link rel="modulepreload" href="/_app/immutable/entry/app.abCkWeAJ.js">
15
+ <link rel="modulepreload" href="/_app/immutable/chunks/QRltG_J6.js">
16
+ <link rel="modulepreload" href="/_app/immutable/chunks/DDNfEvut.js">
17
+ <link rel="modulepreload" href="/_app/immutable/chunks/BfBfOTnK.js">
18
+ <link rel="modulepreload" href="/_app/immutable/chunks/DUdVct7e.js">
19
+ </head>
20
+ <body data-sveltekit-preload-data="hover">
21
+ <div style="display: contents">
22
+ <script>
23
+ {
24
+ __sveltekit_nmm995 = {
25
+ base: ""
26
+ };
27
+
28
+ const element = document.currentScript.parentElement;
29
+
30
+ Promise.all([
31
+ import("/_app/immutable/entry/start.B7CjH6Z7.js"),
32
+ import("/_app/immutable/entry/app.abCkWeAJ.js")
33
+ ]).then(([kit, app]) => {
34
+ kit.start(app, element);
35
+ });
36
+ }
37
+ </script>
38
+ </div>
39
+ </body>
40
+ </html>
@@ -0,0 +1,3 @@
1
+ # allow crawling everything by default
2
+ User-agent: *
3
+ Disallow:
@@ -0,0 +1,127 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="632.17517"
6
+ height="616.88739"
7
+ viewBox="0 0 632.17517 616.88739"
8
+ version="1.1"
9
+ id="svg1"
10
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ xmlns:svg="http://www.w3.org/2000/svg">
14
+ <sodipodi:namedview
15
+ id="namedview1"
16
+ pagecolor="#505050"
17
+ bordercolor="#eeeeee"
18
+ borderopacity="1"
19
+ inkscape:showpageshadow="0"
20
+ inkscape:pageopacity="0"
21
+ inkscape:pagecheckerboard="0"
22
+ inkscape:deskcolor="#505050"
23
+ inkscape:document-units="px"
24
+ inkscape:export-bgcolor="#ffffff00" />
25
+ <defs
26
+ id="defs1" />
27
+ <g
28
+ inkscape:label="Layer 1"
29
+ inkscape:groupmode="layer"
30
+ id="layer1"
31
+ transform="translate(-85.523941,-103.00706)">
32
+ <rect
33
+ style="fill:#0f766e;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-dasharray:none"
34
+ id="rect2"
35
+ width="624.17517"
36
+ height="608.88739"
37
+ x="89.523941"
38
+ y="107.00706"
39
+ ry="63.55658" />
40
+ <g
41
+ id="g5"
42
+ transform="matrix(0.93741364,0,0,1,14.19907,8.8683603)">
43
+ <g
44
+ id="path2"
45
+ transform="translate(-4.1805851,-45.986436)">
46
+ <path
47
+ style="color:#000000;fill:#0f766e;-inkscape-stroke:none"
48
+ d="M 136.58691,304.3442 647.2728,256.50993"
49
+ id="path3" />
50
+ <path
51
+ style="color:#000000;fill:#ffffff;-inkscape-stroke:none"
52
+ d="M 656.86575,233.04229 145.1331,284.01172 138.49219,324.67578 649.17773,276.8418 Z"
53
+ id="path4"
54
+ sodipodi:nodetypes="ccccc" />
55
+ </g>
56
+ <g
57
+ id="path2-1"
58
+ transform="translate(18.716165,120.25339)">
59
+ <path
60
+ style="color:#000000;fill:#0f766e;-inkscape-stroke:none"
61
+ d="M 136.58691,304.3442 647.2728,256.50993"
62
+ id="path3-9" />
63
+ <path
64
+ style="color:#000000;fill:#ffffff;-inkscape-stroke:none"
65
+ d="M 656.86575,233.04229 145.1331,284.01172 138.49219,324.67578 649.17773,276.8418 Z"
66
+ id="path4-2"
67
+ sodipodi:nodetypes="ccccc" />
68
+ </g>
69
+ <g
70
+ id="path2-8"
71
+ transform="translate(10.891685,286.98367)">
72
+ <path
73
+ style="color:#000000;fill:#0f766e;-inkscape-stroke:none"
74
+ d="M 136.58691,304.3442 647.2728,256.50993"
75
+ id="path3-8" />
76
+ <path
77
+ style="color:#000000;fill:#ffffff;-inkscape-stroke:none"
78
+ d="M 656.86575,233.04229 145.1331,284.01172 138.49219,324.67578 649.17773,276.8418 Z"
79
+ id="path4-4"
80
+ sodipodi:nodetypes="ccccc" />
81
+ </g>
82
+ </g>
83
+ <g
84
+ id="g4"
85
+ transform="matrix(1,0,0,0.91186122,-1.4780604,38.567585)">
86
+ <g
87
+ id="path2-7"
88
+ transform="rotate(-88.136049,543.15395,249.92424)">
89
+ <path
90
+ style="color:#000000;fill:#0f766e;-inkscape-stroke:none"
91
+ d="M 136.58691,304.3442 647.2728,256.50993"
92
+ id="path3-1" />
93
+ <path
94
+ style="color:#000000;fill:#ffffff;-inkscape-stroke:none"
95
+ d="M 656.86575,233.04229 145.1331,284.01172 138.49219,324.67578 649.17773,276.8418 Z"
96
+ id="path4-5"
97
+ sodipodi:nodetypes="ccccc" />
98
+ </g>
99
+ <g
100
+ id="path2-7-4"
101
+ transform="rotate(-88.136049,461.52659,338.48873)">
102
+ <path
103
+ style="color:#000000;fill:#0f766e;-inkscape-stroke:none"
104
+ d="M 136.58691,304.3442 647.2728,256.50993"
105
+ id="path3-1-8" />
106
+ <path
107
+ style="color:#000000;fill:#ffffff;-inkscape-stroke:none"
108
+ d="M 656.86575,233.04229 145.1331,284.01172 138.49219,324.67578 649.17773,276.8418 Z"
109
+ id="path4-5-2"
110
+ sodipodi:nodetypes="ccccc" />
111
+ </g>
112
+ <g
113
+ id="path2-7-4-8"
114
+ transform="rotate(-88.136049,377.62543,435.71562)">
115
+ <path
116
+ style="color:#000000;fill:#0f766e;-inkscape-stroke:none"
117
+ d="M 136.58691,304.3442 647.2728,256.50993"
118
+ id="path3-1-8-5" />
119
+ <path
120
+ style="color:#000000;fill:#ffffff;-inkscape-stroke:none"
121
+ d="M 656.86575,233.04229 145.1331,284.01172 138.49219,324.67578 649.17773,276.8418 Z"
122
+ id="path4-5-2-1"
123
+ sodipodi:nodetypes="ccccc" />
124
+ </g>
125
+ </g>
126
+ </g>
127
+ </svg>
@@ -0,0 +1,2 @@
1
+ """Backend test suite."""
2
+
@@ -0,0 +1,132 @@
1
+ """Pytest fixtures for backend tests."""
2
+ import os
3
+ import sys
4
+ import tempfile
5
+ import json
6
+ import shutil
7
+ import pytest
8
+ from fastapi.testclient import TestClient
9
+
10
+
11
+ # Create a persistent temp directory for the entire test session
12
+ # This is set BEFORE any backend modules are imported
13
+ _TEST_TEMP_DIR = tempfile.mkdtemp(prefix="datamodel_test_")
14
+ os.environ["DATAMODEL_TEST_DIR"] = _TEST_TEMP_DIR
15
+
16
+ # Create required directory structure
17
+ os.makedirs(os.path.join(_TEST_TEMP_DIR, "models", "3_core"), exist_ok=True)
18
+
19
+ # Create minimal config.yml
20
+ with open(os.path.join(_TEST_TEMP_DIR, "config.yml"), "w") as f:
21
+ f.write("dbt_project_path: .\n")
22
+
23
+
24
+ def pytest_sessionfinish(session, exitstatus):
25
+ """Clean up temp directory after all tests complete."""
26
+ if os.path.exists(_TEST_TEMP_DIR):
27
+ shutil.rmtree(_TEST_TEMP_DIR, ignore_errors=True)
28
+
29
+
30
+ @pytest.fixture(autouse=True)
31
+ def clean_test_files():
32
+ """Clean up test files before each test to ensure isolation."""
33
+ # Clean data model file before each test
34
+ data_model_path = os.path.join(_TEST_TEMP_DIR, "data_model.yml")
35
+ if os.path.exists(data_model_path):
36
+ os.remove(data_model_path)
37
+
38
+ # Clean canvas layout file before each test
39
+ canvas_layout_path = os.path.join(_TEST_TEMP_DIR, "canvas_layout.yml")
40
+ if os.path.exists(canvas_layout_path):
41
+ os.remove(canvas_layout_path)
42
+
43
+ # Clean manifest file
44
+ manifest_path = os.path.join(_TEST_TEMP_DIR, "manifest.json")
45
+ if os.path.exists(manifest_path):
46
+ os.remove(manifest_path)
47
+
48
+ # Clean model yml files (recursively) to avoid cross-test leakage
49
+ models_dir = os.path.join(_TEST_TEMP_DIR, "models", "3_core")
50
+ if os.path.exists(models_dir):
51
+ for root, _, files in os.walk(models_dir):
52
+ for fname in files:
53
+ if fname.endswith((".yml", ".yaml")):
54
+ os.remove(os.path.join(root, fname))
55
+
56
+ yield
57
+
58
+
59
+ @pytest.fixture
60
+ def temp_dir():
61
+ """Return the shared test temp directory."""
62
+ return _TEST_TEMP_DIR
63
+
64
+
65
+ @pytest.fixture
66
+ def temp_data_model_path():
67
+ """Return path for the data model file (in session temp dir)."""
68
+ return os.path.join(_TEST_TEMP_DIR, "data_model.yml")
69
+
70
+
71
+ @pytest.fixture
72
+ def temp_canvas_layout_path():
73
+ """Return path for the canvas layout file (in session temp dir)."""
74
+ return os.path.join(_TEST_TEMP_DIR, "canvas_layout.yml")
75
+
76
+
77
+ @pytest.fixture
78
+ def temp_dbt_project():
79
+ """Return the test dbt project directory."""
80
+ return _TEST_TEMP_DIR
81
+
82
+
83
+ @pytest.fixture
84
+ def mock_manifest_data():
85
+ """Return mock manifest data."""
86
+ return {
87
+ "nodes": {
88
+ "model.project.users": {
89
+ "unique_id": "model.project.users",
90
+ "resource_type": "model",
91
+ "name": "users",
92
+ "schema": "public",
93
+ "alias": "users",
94
+ "original_file_path": "models/3_core/users.sql",
95
+ "columns": {
96
+ "id": {"name": "id", "type": "integer"},
97
+ "name": {"name": "name", "type": "text"},
98
+ },
99
+ "description": "User table",
100
+ "config": {"materialized": "table"},
101
+ "tags": ["core"],
102
+ },
103
+ "model.project.orders": {
104
+ "unique_id": "model.project.orders",
105
+ "resource_type": "model",
106
+ "name": "orders",
107
+ "schema": "public",
108
+ "alias": "orders",
109
+ "original_file_path": "models/3_core/orders.sql",
110
+ "columns": {},
111
+ "description": "Orders table",
112
+ "config": {"materialized": "view"},
113
+ "tags": [],
114
+ },
115
+ }
116
+ }
117
+
118
+
119
+ @pytest.fixture
120
+ def mock_manifest(mock_manifest_data):
121
+ """Create a mock manifest.json file."""
122
+ manifest_path = os.path.join(_TEST_TEMP_DIR, "manifest.json")
123
+ with open(manifest_path, "w") as f:
124
+ json.dump(mock_manifest_data, f)
125
+ return manifest_path
126
+
127
+
128
+ @pytest.fixture
129
+ def test_client(mock_manifest):
130
+ """Create a test client. Config is already set via environment variables."""
131
+ from trellis_datamodel.server import app
132
+ return TestClient(app)