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,292 @@
1
+ """
2
+ Trellis CLI
3
+
4
+ Command-line interface for Trellis - visual data model editor for dbt projects.
5
+ """
6
+
7
+ import typer
8
+ import uvicorn
9
+ import webbrowser
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ from trellis_datamodel import __version__
14
+ from trellis_datamodel import config as cfg
15
+ from trellis_datamodel.config import (
16
+ load_config,
17
+ find_config_file,
18
+ print_config,
19
+ )
20
+
21
+ app = typer.Typer(
22
+ name="trellis",
23
+ help="Trellis - Visual data model editor for dbt projects",
24
+ add_completion=False,
25
+ no_args_is_help=True,
26
+ )
27
+
28
+
29
+ @app.callback(invoke_without_command=True)
30
+ def main(
31
+ ctx: typer.Context,
32
+ version: bool = typer.Option(False, "--version", help="Show version and exit"),
33
+ ):
34
+ """Trellis - Visual data model editor for dbt projects."""
35
+ if version:
36
+ typer.echo(__version__)
37
+ raise typer.Exit()
38
+ if ctx.invoked_subcommand is None:
39
+ typer.echo(ctx.get_help())
40
+ raise typer.Exit()
41
+
42
+
43
+ @app.command()
44
+ def run(
45
+ port: int = typer.Option(8089, "--port", "-p", help="Port to run the server on"),
46
+ config: Optional[str] = typer.Option(
47
+ None, "--config", "-c", help="Path to config file (trellis.yml or config.yml)"
48
+ ),
49
+ no_browser: bool = typer.Option(
50
+ False, "--no-browser", help="Don't open browser automatically"
51
+ ),
52
+ ):
53
+ """
54
+ Start the Trellis server.
55
+
56
+ Looks for trellis.yml or config.yml in the current directory.
57
+ """
58
+ # Load configuration
59
+ import os
60
+
61
+ # Allow running without config file if test environment variables are set
62
+ is_test_mode = bool(
63
+ os.environ.get("DATAMODEL_DATA_MODEL_PATH")
64
+ or os.environ.get("DATAMODEL_TEST_DIR")
65
+ )
66
+
67
+ config_path = config
68
+ if not config_path:
69
+ found_config = find_config_file()
70
+ if not found_config:
71
+ if is_test_mode:
72
+ # Test mode: allow running without config file
73
+ config_path = None
74
+ else:
75
+ cwd = os.getcwd()
76
+ expected_path = os.path.join(cwd, "trellis.yml")
77
+ typer.echo(
78
+ typer.style(
79
+ "Error: No config file found.",
80
+ fg=typer.colors.RED,
81
+ )
82
+ )
83
+ typer.echo(f" Expected location: {expected_path}")
84
+ typer.echo()
85
+ typer.echo(" Run 'trellis init' to create a starter config file.")
86
+ raise typer.Exit(1)
87
+ else:
88
+ config_path = found_config
89
+
90
+ if config_path:
91
+ load_config(config_path)
92
+ elif is_test_mode:
93
+ # Test mode: config will be loaded from environment variables
94
+ load_config(None)
95
+
96
+ # Print startup info
97
+ typer.echo(
98
+ typer.style(f"🌿 Trellis Data v{__version__}", fg=typer.colors.GREEN, bold=True)
99
+ )
100
+ typer.echo(f" Loading config from {config_path}")
101
+ print_config()
102
+ typer.echo()
103
+
104
+ # Start server
105
+ url = f"http://localhost:{port}"
106
+ typer.echo(typer.style(f" Server running at {url}", fg=typer.colors.CYAN))
107
+ typer.echo(" Press Ctrl+C to stop")
108
+ typer.echo()
109
+
110
+ # Open browser
111
+ if not no_browser:
112
+ try:
113
+ webbrowser.open(url)
114
+ except Exception:
115
+ pass # Ignore browser errors
116
+
117
+ # Run server
118
+ uvicorn.run(
119
+ "trellis_datamodel.server:app",
120
+ host="0.0.0.0",
121
+ port=port,
122
+ reload=False, # Disable reload in production
123
+ log_level="info",
124
+ )
125
+
126
+
127
+ @app.command(name="serve")
128
+ def serve(
129
+ port: int = typer.Option(8089, "--port", "-p", help="Port to run the server on"),
130
+ config: Optional[str] = typer.Option(
131
+ None, "--config", "-c", help="Path to config file (trellis.yml or config.yml)"
132
+ ),
133
+ no_browser: bool = typer.Option(
134
+ False, "--no-browser", help="Don't open browser automatically"
135
+ ),
136
+ ):
137
+ """
138
+ Start the Trellis server (alias for 'run').
139
+
140
+ Looks for trellis.yml or config.yml in the current directory.
141
+ """
142
+ # Delegate to run function
143
+ run(port=port, config=config, no_browser=no_browser)
144
+
145
+
146
+ @app.command()
147
+ def init():
148
+ """
149
+ Initialize a new trellis.yml config file in the current directory.
150
+ """
151
+ config_file = Path("trellis.yml")
152
+ if config_file.exists():
153
+ typer.echo(
154
+ typer.style(
155
+ "trellis.yml already exists in this directory.", fg=typer.colors.YELLOW
156
+ )
157
+ )
158
+ raise typer.Exit(1)
159
+
160
+ default_config = """\
161
+ # Trellis configuration
162
+ framework: dbt-core
163
+ dbt_project_path: "."
164
+ dbt_manifest_path: "target/manifest.json"
165
+ dbt_catalog_path: "target/catalog.json"
166
+ data_model_file: "data_model.yml"
167
+ dbt_model_paths: [] # Empty = include all models
168
+ """
169
+ config_file.write_text(default_config)
170
+ typer.echo(typer.style("✓ Created trellis.yml", fg=typer.colors.GREEN))
171
+ typer.echo(" Edit the file to configure your dbt project paths, then run:")
172
+ typer.echo(typer.style(" trellis run", fg=typer.colors.CYAN))
173
+
174
+
175
+ @app.command(name="generate-company-data")
176
+ def generate_company_data(
177
+ config: Optional[str] = typer.Option(
178
+ None, "--config", "-c", help="Path to config file (trellis.yml or config.yml)"
179
+ ),
180
+ ):
181
+ """
182
+ Generate mock commercial company data for modeling exercises.
183
+
184
+ Creates CSV files in dbt_company_dummy/data/ directory with realistic
185
+ commercial company data including departments, employees, leads, customers,
186
+ products, orders, and order items.
187
+
188
+ The path to the dbt company dummy project can be configured in trellis.yml
189
+ via the 'dbt_company_dummy_path' option (defaults to './dbt_company_dummy').
190
+ """
191
+ import sys
192
+ import importlib.util
193
+
194
+ # Load config to get dbt_company_dummy_path
195
+ config_path = config
196
+ if not config_path:
197
+ found_config = find_config_file()
198
+ if found_config:
199
+ config_path = found_config
200
+
201
+ if config_path:
202
+ load_config(config_path)
203
+
204
+ # Use configured path or fallback to default relative to current working directory
205
+ import os
206
+
207
+ if cfg.DBT_COMPANY_DUMMY_PATH:
208
+ generator_path = Path(cfg.DBT_COMPANY_DUMMY_PATH) / "generate_data.py"
209
+ else:
210
+ # Fallback: check current working directory first (for installed packages)
211
+ # This is the most common case when running from repo root
212
+ cwd_dummy_path = Path(os.getcwd()) / "dbt_company_dummy" / "generate_data.py"
213
+ if cwd_dummy_path.exists():
214
+ generator_path = cwd_dummy_path
215
+ else:
216
+ # Try repo root (for development when running from source)
217
+ project_root = Path(__file__).parent.parent
218
+ repo_dummy_path = project_root / "dbt_company_dummy" / "generate_data.py"
219
+ if repo_dummy_path.exists():
220
+ generator_path = repo_dummy_path
221
+ else:
222
+ # Last resort: use current working directory even if it doesn't exist yet
223
+ # (will show helpful error message)
224
+ generator_path = cwd_dummy_path
225
+
226
+ if not generator_path.exists():
227
+ import os
228
+
229
+ typer.echo(
230
+ typer.style(
231
+ f"Error: Generator script not found at {generator_path}",
232
+ fg=typer.colors.RED,
233
+ )
234
+ )
235
+ typer.echo()
236
+ typer.echo("The generator script should be located at:")
237
+ if cfg.DBT_COMPANY_DUMMY_PATH:
238
+ typer.echo(f" {cfg.DBT_COMPANY_DUMMY_PATH}/generate_data.py")
239
+ typer.echo()
240
+ typer.echo("This path is configured in your trellis.yml file.")
241
+ else:
242
+ typer.echo(f" {os.getcwd()}/dbt_company_dummy/generate_data.py")
243
+ typer.echo()
244
+ typer.echo(
245
+ "Make sure you're running this command from the repository root,"
246
+ )
247
+ typer.echo(
248
+ "or configure 'dbt_company_dummy_path' in your trellis.yml file."
249
+ )
250
+ raise typer.Exit(1)
251
+
252
+ # Load and run the generator module
253
+ try:
254
+ spec = importlib.util.spec_from_file_location("generate_data", generator_path)
255
+ generator_module = importlib.util.module_from_spec(spec)
256
+ sys.modules["generate_data"] = generator_module
257
+ spec.loader.exec_module(generator_module)
258
+
259
+ # Call the main function
260
+ generator_module.main()
261
+
262
+ typer.echo()
263
+ typer.echo(
264
+ typer.style(
265
+ "✓ Company dummy data generation completed successfully!",
266
+ fg=typer.colors.GREEN,
267
+ )
268
+ )
269
+ except ImportError as e:
270
+ typer.echo(
271
+ typer.style(
272
+ f"Error: Missing dependency - {e}",
273
+ fg=typer.colors.RED,
274
+ )
275
+ )
276
+ typer.echo(
277
+ " Install required dependencies with: pip install trellis-datamodel[dbt-example]"
278
+ )
279
+ typer.echo(" Or install directly: pip install pandas faker")
280
+ raise typer.Exit(1)
281
+ except Exception as e:
282
+ typer.echo(
283
+ typer.style(
284
+ f"Error generating data: {e}",
285
+ fg=typer.colors.RED,
286
+ )
287
+ )
288
+ raise typer.Exit(1)
289
+
290
+
291
+ if __name__ == "__main__":
292
+ app()
@@ -0,0 +1,239 @@
1
+ """
2
+ Configuration loading for Trellis Data.
3
+ Centralizes all path resolution logic.
4
+
5
+ For testing, set environment variable DATAMODEL_TEST_DIR to a temp directory path.
6
+ This will override all paths to use that directory.
7
+ """
8
+
9
+ import os
10
+ import yaml
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ # Check for test mode - allows overriding config via environment
15
+ _TEST_DIR = os.environ.get("DATAMODEL_TEST_DIR", "")
16
+
17
+ if _TEST_DIR:
18
+ # Test mode: use temp directory paths
19
+ CONFIG_PATH = os.path.join(_TEST_DIR, "config.yml")
20
+ FRAMEWORK: str = os.environ.get("DATAMODEL_FRAMEWORK", "dbt-core")
21
+ MANIFEST_PATH: str = os.environ.get(
22
+ "DATAMODEL_MANIFEST_PATH", os.path.join(_TEST_DIR, "manifest.json")
23
+ )
24
+ CATALOG_PATH: str = os.environ.get(
25
+ "DATAMODEL_CATALOG_PATH", os.path.join(_TEST_DIR, "catalog.json")
26
+ )
27
+ DATA_MODEL_PATH: str = os.environ.get(
28
+ "DATAMODEL_DATA_MODEL_PATH", os.path.join(_TEST_DIR, "data_model.yml")
29
+ )
30
+ CANVAS_LAYOUT_PATH: str = os.environ.get(
31
+ "DATAMODEL_CANVAS_LAYOUT_PATH", os.path.join(_TEST_DIR, "canvas_layout.yml")
32
+ )
33
+ CANVAS_LAYOUT_VERSION_CONTROL: bool = (
34
+ os.environ.get("DATAMODEL_CANVAS_LAYOUT_VERSION_CONTROL", "true").lower()
35
+ == "true"
36
+ )
37
+ DBT_PROJECT_PATH: str = _TEST_DIR
38
+ DBT_MODEL_PATHS: list[str] = ["3_core"]
39
+ FRONTEND_BUILD_DIR: str = os.path.join(_TEST_DIR, "frontend/build")
40
+ DBT_COMPANY_DUMMY_PATH: str = os.path.join(_TEST_DIR, "dbt_company_dummy")
41
+ else:
42
+ # Production mode: will be set by load_config()
43
+ CONFIG_PATH: str = ""
44
+ FRAMEWORK: str = "dbt-core"
45
+ MANIFEST_PATH: str = ""
46
+ CATALOG_PATH: str = ""
47
+ DATA_MODEL_PATH: str = ""
48
+ CANVAS_LAYOUT_PATH: str = ""
49
+ CANVAS_LAYOUT_VERSION_CONTROL: bool = True
50
+ DBT_PROJECT_PATH: str = ""
51
+ DBT_MODEL_PATHS: list[str] = []
52
+ FRONTEND_BUILD_DIR: str = ""
53
+ DBT_COMPANY_DUMMY_PATH: str = ""
54
+
55
+
56
+ def find_config_file(config_override: Optional[str] = None) -> Optional[str]:
57
+ """
58
+ Find config file in order of priority:
59
+ 1. CLI override (--config)
60
+ 2. trellis.yml in current directory
61
+ 3. config.yml in current directory (fallback)
62
+
63
+ Returns:
64
+ Path to config file or None if not found
65
+ """
66
+ if config_override:
67
+ if os.path.exists(config_override):
68
+ return os.path.abspath(config_override)
69
+ return None
70
+
71
+ cwd = os.getcwd()
72
+
73
+ # Try trellis.yml first
74
+ trellis_yml = os.path.join(cwd, "trellis.yml")
75
+ if os.path.exists(trellis_yml):
76
+ return os.path.abspath(trellis_yml)
77
+
78
+ # Fallback to config.yml
79
+ config_yml = os.path.join(cwd, "config.yml")
80
+ if os.path.exists(config_yml):
81
+ return os.path.abspath(config_yml)
82
+
83
+ return None
84
+
85
+
86
+ def load_config(config_path: Optional[str] = None) -> None:
87
+ """Load and resolve all paths from config file."""
88
+ global FRAMEWORK, MANIFEST_PATH, DATA_MODEL_PATH, DBT_MODEL_PATHS, CATALOG_PATH, DBT_PROJECT_PATH, CANVAS_LAYOUT_PATH, CANVAS_LAYOUT_VERSION_CONTROL, CONFIG_PATH, FRONTEND_BUILD_DIR, DBT_COMPANY_DUMMY_PATH
89
+
90
+ # Skip loading config file in test mode (paths already set via environment)
91
+ if _TEST_DIR:
92
+ return
93
+
94
+ # Find config file
95
+ if config_path:
96
+ CONFIG_PATH = config_path
97
+ else:
98
+ found_config = find_config_file()
99
+ if not found_config:
100
+ # No config found - use defaults
101
+ CONFIG_PATH = ""
102
+ return
103
+ CONFIG_PATH = found_config
104
+
105
+ if not os.path.exists(CONFIG_PATH):
106
+ return
107
+
108
+ try:
109
+ with open(CONFIG_PATH, "r") as f:
110
+ config = yaml.safe_load(f) or {}
111
+
112
+ # 0. Get framework (defaults to dbt-core)
113
+ FRAMEWORK = config.get("framework", "dbt-core")
114
+
115
+ # 1. Get dbt_project_path (Required for resolving other paths)
116
+ if "dbt_project_path" in config:
117
+ p = config["dbt_project_path"]
118
+ if not os.path.isabs(p):
119
+ # Resolve relative to config file location
120
+ DBT_PROJECT_PATH = os.path.abspath(
121
+ os.path.join(os.path.dirname(CONFIG_PATH), p)
122
+ )
123
+ else:
124
+ DBT_PROJECT_PATH = p
125
+ else:
126
+ DBT_PROJECT_PATH = ""
127
+
128
+ # 2. Resolve Manifest
129
+ if "dbt_manifest_path" in config:
130
+ p = config["dbt_manifest_path"]
131
+ if not os.path.isabs(p) and DBT_PROJECT_PATH:
132
+ MANIFEST_PATH = os.path.abspath(os.path.join(DBT_PROJECT_PATH, p))
133
+ elif os.path.isabs(p):
134
+ MANIFEST_PATH = p
135
+ else:
136
+ MANIFEST_PATH = os.path.abspath(
137
+ os.path.join(os.path.dirname(CONFIG_PATH), p)
138
+ )
139
+
140
+ # 3. Resolve Catalog
141
+ if "dbt_catalog_path" in config:
142
+ p = config["dbt_catalog_path"]
143
+ if not os.path.isabs(p) and DBT_PROJECT_PATH:
144
+ CATALOG_PATH = os.path.abspath(os.path.join(DBT_PROJECT_PATH, p))
145
+ elif os.path.isabs(p):
146
+ CATALOG_PATH = p
147
+ else:
148
+ CATALOG_PATH = os.path.abspath(
149
+ os.path.join(os.path.dirname(CONFIG_PATH), p)
150
+ )
151
+
152
+ # 4. Resolve Data Model (env var takes precedence)
153
+ if (
154
+ "DATAMODEL_DATA_MODEL_PATH" not in os.environ
155
+ and "data_model_file" in config
156
+ ):
157
+ p = config["data_model_file"]
158
+ if not os.path.isabs(p):
159
+ base_path = DBT_PROJECT_PATH or os.path.dirname(CONFIG_PATH)
160
+ DATA_MODEL_PATH = os.path.abspath(os.path.join(base_path, p))
161
+ else:
162
+ DATA_MODEL_PATH = p
163
+
164
+ # 5. Model path filters
165
+ if "dbt_model_paths" in config:
166
+ DBT_MODEL_PATHS = config["dbt_model_paths"]
167
+
168
+ # 6. Resolve Canvas Layout (defaults to canvas_layout.yml next to data model)
169
+ if "canvas_layout_file" in config:
170
+ p = config["canvas_layout_file"]
171
+ if not os.path.isabs(p):
172
+ base_path = DBT_PROJECT_PATH or os.path.dirname(CONFIG_PATH)
173
+ CANVAS_LAYOUT_PATH = os.path.abspath(os.path.join(base_path, p))
174
+ else:
175
+ CANVAS_LAYOUT_PATH = p
176
+ else:
177
+ # Default: canvas_layout.yml next to data_model.yml
178
+ if DATA_MODEL_PATH:
179
+ data_model_dir = os.path.dirname(DATA_MODEL_PATH)
180
+ CANVAS_LAYOUT_PATH = os.path.abspath(
181
+ os.path.join(data_model_dir, "canvas_layout.yml")
182
+ )
183
+ else:
184
+ CANVAS_LAYOUT_PATH = os.path.abspath(
185
+ os.path.join(os.path.dirname(CONFIG_PATH), "canvas_layout.yml")
186
+ )
187
+
188
+ # 7. Canvas layout version control setting
189
+ if "canvas_layout_version_control" in config:
190
+ CANVAS_LAYOUT_VERSION_CONTROL = config["canvas_layout_version_control"]
191
+
192
+ # 8. Frontend build directory
193
+ fe_env = os.environ.get("DATAMODEL_FRONTEND_BUILD_DIR")
194
+ if fe_env:
195
+ FRONTEND_BUILD_DIR = fe_env
196
+ elif "frontend_build_dir" in config:
197
+ p = config["frontend_build_dir"]
198
+ if not os.path.isabs(p):
199
+ FRONTEND_BUILD_DIR = os.path.abspath(
200
+ os.path.join(os.path.dirname(CONFIG_PATH), p)
201
+ )
202
+ else:
203
+ FRONTEND_BUILD_DIR = p
204
+ else:
205
+ # Default: frontend/build next to config file (repo root)
206
+ FRONTEND_BUILD_DIR = os.path.abspath(
207
+ os.path.join(os.path.dirname(CONFIG_PATH), "frontend", "build")
208
+ )
209
+
210
+ # 9. Resolve dbt company dummy path (only if explicitly configured)
211
+ # If not configured, leave empty so CLI can use smart fallback logic
212
+ if "dbt_company_dummy_path" in config:
213
+ p = config["dbt_company_dummy_path"]
214
+ if not os.path.isabs(p):
215
+ DBT_COMPANY_DUMMY_PATH = os.path.abspath(
216
+ os.path.join(os.path.dirname(CONFIG_PATH), p)
217
+ )
218
+ else:
219
+ DBT_COMPANY_DUMMY_PATH = p
220
+ # Note: No default set here - CLI handles fallback to cwd/dbt_company_dummy
221
+
222
+ except Exception as e:
223
+ print(f"Error loading config: {e}")
224
+
225
+
226
+ def print_config() -> None:
227
+ """Print current configuration for debugging."""
228
+ print(f"Using Config: {CONFIG_PATH}")
229
+ print(f"Framework: {FRAMEWORK}")
230
+ print(f"Project Path: {DBT_PROJECT_PATH}")
231
+ print(f"Frontend build dir: {FRONTEND_BUILD_DIR}")
232
+ print(f"Looking for manifest at: {MANIFEST_PATH}")
233
+ print(f"Looking for catalog at: {CATALOG_PATH}")
234
+ print(f"Looking for data model at: {DATA_MODEL_PATH}")
235
+ print(f"Looking for canvas layout at: {CANVAS_LAYOUT_PATH}")
236
+ print(f"Canvas layout version control: {CANVAS_LAYOUT_VERSION_CONTROL}")
237
+ print(f"Filtering models by paths: {DBT_MODEL_PATHS}")
238
+ if DBT_COMPANY_DUMMY_PATH:
239
+ print(f"dbt company dummy path: {DBT_COMPANY_DUMMY_PATH}")
@@ -0,0 +1,13 @@
1
+ """Pydantic models for API request/response schemas."""
2
+ from .schemas import (
3
+ DataModelUpdate,
4
+ DbtSchemaRequest,
5
+ ModelSchemaRequest,
6
+ )
7
+
8
+ __all__ = [
9
+ "DataModelUpdate",
10
+ "DbtSchemaRequest",
11
+ "ModelSchemaRequest",
12
+ ]
13
+
@@ -0,0 +1,28 @@
1
+ """Pydantic models for API request/response schemas."""
2
+ from pydantic import BaseModel
3
+ from typing import List, Optional, Dict, Any
4
+
5
+
6
+ class DataModelUpdate(BaseModel):
7
+ """Schema for updating the data model."""
8
+ version: float = 0.1
9
+ entities: List[Dict[str, Any]]
10
+ relationships: List[Dict[str, Any]]
11
+
12
+
13
+ class DbtSchemaRequest(BaseModel):
14
+ """Schema for creating a new dbt schema file."""
15
+ entity_id: str
16
+ model_name: str
17
+ fields: List[Dict[str, str]]
18
+ description: Optional[str] = None
19
+ tags: Optional[List[str]] = None
20
+
21
+
22
+ class ModelSchemaRequest(BaseModel):
23
+ """Schema for updating model columns and metadata."""
24
+ columns: List[Dict[str, Any]]
25
+ description: Optional[str] = None
26
+ tags: Optional[List[str]] = None
27
+ version: Optional[int] = None
28
+
@@ -0,0 +1,11 @@
1
+ """API route modules."""
2
+ from .manifest import router as manifest_router
3
+ from .data_model import router as data_model_router
4
+ from .schema import router as schema_router
5
+
6
+ __all__ = [
7
+ "manifest_router",
8
+ "data_model_router",
9
+ "schema_router",
10
+ ]
11
+