dvt-core 0.58.6__cp311-cp311-macosx_10_9_x86_64.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 (324) hide show
  1. dbt/__init__.py +7 -0
  2. dbt/_pydantic_shim.py +26 -0
  3. dbt/artifacts/__init__.py +0 -0
  4. dbt/artifacts/exceptions/__init__.py +1 -0
  5. dbt/artifacts/exceptions/schemas.py +31 -0
  6. dbt/artifacts/resources/__init__.py +116 -0
  7. dbt/artifacts/resources/base.py +67 -0
  8. dbt/artifacts/resources/types.py +93 -0
  9. dbt/artifacts/resources/v1/analysis.py +10 -0
  10. dbt/artifacts/resources/v1/catalog.py +23 -0
  11. dbt/artifacts/resources/v1/components.py +274 -0
  12. dbt/artifacts/resources/v1/config.py +277 -0
  13. dbt/artifacts/resources/v1/documentation.py +11 -0
  14. dbt/artifacts/resources/v1/exposure.py +51 -0
  15. dbt/artifacts/resources/v1/function.py +52 -0
  16. dbt/artifacts/resources/v1/generic_test.py +31 -0
  17. dbt/artifacts/resources/v1/group.py +21 -0
  18. dbt/artifacts/resources/v1/hook.py +11 -0
  19. dbt/artifacts/resources/v1/macro.py +29 -0
  20. dbt/artifacts/resources/v1/metric.py +172 -0
  21. dbt/artifacts/resources/v1/model.py +145 -0
  22. dbt/artifacts/resources/v1/owner.py +10 -0
  23. dbt/artifacts/resources/v1/saved_query.py +111 -0
  24. dbt/artifacts/resources/v1/seed.py +41 -0
  25. dbt/artifacts/resources/v1/semantic_layer_components.py +72 -0
  26. dbt/artifacts/resources/v1/semantic_model.py +314 -0
  27. dbt/artifacts/resources/v1/singular_test.py +14 -0
  28. dbt/artifacts/resources/v1/snapshot.py +91 -0
  29. dbt/artifacts/resources/v1/source_definition.py +84 -0
  30. dbt/artifacts/resources/v1/sql_operation.py +10 -0
  31. dbt/artifacts/resources/v1/unit_test_definition.py +77 -0
  32. dbt/artifacts/schemas/__init__.py +0 -0
  33. dbt/artifacts/schemas/base.py +191 -0
  34. dbt/artifacts/schemas/batch_results.py +24 -0
  35. dbt/artifacts/schemas/catalog/__init__.py +11 -0
  36. dbt/artifacts/schemas/catalog/v1/__init__.py +0 -0
  37. dbt/artifacts/schemas/catalog/v1/catalog.py +59 -0
  38. dbt/artifacts/schemas/freshness/__init__.py +1 -0
  39. dbt/artifacts/schemas/freshness/v3/__init__.py +0 -0
  40. dbt/artifacts/schemas/freshness/v3/freshness.py +158 -0
  41. dbt/artifacts/schemas/manifest/__init__.py +2 -0
  42. dbt/artifacts/schemas/manifest/v12/__init__.py +0 -0
  43. dbt/artifacts/schemas/manifest/v12/manifest.py +211 -0
  44. dbt/artifacts/schemas/results.py +147 -0
  45. dbt/artifacts/schemas/run/__init__.py +2 -0
  46. dbt/artifacts/schemas/run/v5/__init__.py +0 -0
  47. dbt/artifacts/schemas/run/v5/run.py +184 -0
  48. dbt/artifacts/schemas/upgrades/__init__.py +4 -0
  49. dbt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
  50. dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
  51. dbt/artifacts/utils/validation.py +153 -0
  52. dbt/cli/__init__.py +1 -0
  53. dbt/cli/context.py +17 -0
  54. dbt/cli/exceptions.py +57 -0
  55. dbt/cli/flags.py +560 -0
  56. dbt/cli/main.py +2403 -0
  57. dbt/cli/option_types.py +121 -0
  58. dbt/cli/options.py +80 -0
  59. dbt/cli/params.py +844 -0
  60. dbt/cli/requires.py +490 -0
  61. dbt/cli/resolvers.py +50 -0
  62. dbt/cli/types.py +40 -0
  63. dbt/clients/__init__.py +0 -0
  64. dbt/clients/checked_load.py +83 -0
  65. dbt/clients/git.py +164 -0
  66. dbt/clients/jinja.py +206 -0
  67. dbt/clients/jinja_static.py +245 -0
  68. dbt/clients/registry.py +192 -0
  69. dbt/clients/yaml_helper.py +68 -0
  70. dbt/compilation.py +876 -0
  71. dbt/compute/__init__.py +14 -0
  72. dbt/compute/engines/__init__.py +12 -0
  73. dbt/compute/engines/spark_engine.cpython-311-darwin.so +0 -0
  74. dbt/compute/engines/spark_engine.py +642 -0
  75. dbt/compute/federated_executor.cpython-311-darwin.so +0 -0
  76. dbt/compute/federated_executor.py +1080 -0
  77. dbt/compute/filter_pushdown.cpython-311-darwin.so +0 -0
  78. dbt/compute/filter_pushdown.py +273 -0
  79. dbt/compute/jar_provisioning.cpython-311-darwin.so +0 -0
  80. dbt/compute/jar_provisioning.py +255 -0
  81. dbt/compute/java_compat.cpython-311-darwin.so +0 -0
  82. dbt/compute/java_compat.py +689 -0
  83. dbt/compute/jdbc_utils.cpython-311-darwin.so +0 -0
  84. dbt/compute/jdbc_utils.py +678 -0
  85. dbt/compute/metadata/__init__.py +40 -0
  86. dbt/compute/metadata/adapters_registry.cpython-311-darwin.so +0 -0
  87. dbt/compute/metadata/adapters_registry.py +370 -0
  88. dbt/compute/metadata/registry.cpython-311-darwin.so +0 -0
  89. dbt/compute/metadata/registry.py +674 -0
  90. dbt/compute/metadata/store.cpython-311-darwin.so +0 -0
  91. dbt/compute/metadata/store.py +1499 -0
  92. dbt/compute/smart_selector.cpython-311-darwin.so +0 -0
  93. dbt/compute/smart_selector.py +377 -0
  94. dbt/compute/strategies/__init__.py +55 -0
  95. dbt/compute/strategies/base.cpython-311-darwin.so +0 -0
  96. dbt/compute/strategies/base.py +165 -0
  97. dbt/compute/strategies/dataproc.cpython-311-darwin.so +0 -0
  98. dbt/compute/strategies/dataproc.py +207 -0
  99. dbt/compute/strategies/emr.cpython-311-darwin.so +0 -0
  100. dbt/compute/strategies/emr.py +203 -0
  101. dbt/compute/strategies/local.cpython-311-darwin.so +0 -0
  102. dbt/compute/strategies/local.py +443 -0
  103. dbt/compute/strategies/standalone.cpython-311-darwin.so +0 -0
  104. dbt/compute/strategies/standalone.py +262 -0
  105. dbt/config/__init__.py +4 -0
  106. dbt/config/catalogs.py +94 -0
  107. dbt/config/compute.cpython-311-darwin.so +0 -0
  108. dbt/config/compute.py +513 -0
  109. dbt/config/dvt_profile.cpython-311-darwin.so +0 -0
  110. dbt/config/dvt_profile.py +342 -0
  111. dbt/config/profile.py +422 -0
  112. dbt/config/project.py +873 -0
  113. dbt/config/project_utils.py +28 -0
  114. dbt/config/renderer.py +231 -0
  115. dbt/config/runtime.py +553 -0
  116. dbt/config/selectors.py +208 -0
  117. dbt/config/utils.py +77 -0
  118. dbt/constants.py +28 -0
  119. dbt/context/__init__.py +0 -0
  120. dbt/context/base.py +745 -0
  121. dbt/context/configured.py +135 -0
  122. dbt/context/context_config.py +382 -0
  123. dbt/context/docs.py +82 -0
  124. dbt/context/exceptions_jinja.py +178 -0
  125. dbt/context/macro_resolver.py +195 -0
  126. dbt/context/macros.py +171 -0
  127. dbt/context/manifest.py +72 -0
  128. dbt/context/providers.py +2249 -0
  129. dbt/context/query_header.py +13 -0
  130. dbt/context/secret.py +58 -0
  131. dbt/context/target.py +74 -0
  132. dbt/contracts/__init__.py +0 -0
  133. dbt/contracts/files.py +413 -0
  134. dbt/contracts/graph/__init__.py +0 -0
  135. dbt/contracts/graph/manifest.py +1904 -0
  136. dbt/contracts/graph/metrics.py +97 -0
  137. dbt/contracts/graph/model_config.py +70 -0
  138. dbt/contracts/graph/node_args.py +42 -0
  139. dbt/contracts/graph/nodes.py +1806 -0
  140. dbt/contracts/graph/semantic_manifest.py +232 -0
  141. dbt/contracts/graph/unparsed.py +811 -0
  142. dbt/contracts/project.py +417 -0
  143. dbt/contracts/results.py +53 -0
  144. dbt/contracts/selection.py +23 -0
  145. dbt/contracts/sql.py +85 -0
  146. dbt/contracts/state.py +68 -0
  147. dbt/contracts/util.py +46 -0
  148. dbt/deprecations.py +348 -0
  149. dbt/deps/__init__.py +0 -0
  150. dbt/deps/base.py +152 -0
  151. dbt/deps/git.py +195 -0
  152. dbt/deps/local.py +79 -0
  153. dbt/deps/registry.py +130 -0
  154. dbt/deps/resolver.py +149 -0
  155. dbt/deps/tarball.py +120 -0
  156. dbt/docs/source/_ext/dbt_click.py +119 -0
  157. dbt/docs/source/conf.py +32 -0
  158. dbt/env_vars.py +64 -0
  159. dbt/event_time/event_time.py +40 -0
  160. dbt/event_time/sample_window.py +60 -0
  161. dbt/events/__init__.py +15 -0
  162. dbt/events/base_types.py +36 -0
  163. dbt/events/core_types_pb2.py +2 -0
  164. dbt/events/logging.py +108 -0
  165. dbt/events/types.py +2516 -0
  166. dbt/exceptions.py +1486 -0
  167. dbt/flags.py +89 -0
  168. dbt/graph/__init__.py +11 -0
  169. dbt/graph/cli.py +249 -0
  170. dbt/graph/graph.py +172 -0
  171. dbt/graph/queue.py +214 -0
  172. dbt/graph/selector.py +374 -0
  173. dbt/graph/selector_methods.py +975 -0
  174. dbt/graph/selector_spec.py +222 -0
  175. dbt/graph/thread_pool.py +18 -0
  176. dbt/hooks.py +21 -0
  177. dbt/include/README.md +49 -0
  178. dbt/include/__init__.py +3 -0
  179. dbt/include/data/adapters_registry.duckdb +0 -0
  180. dbt/include/data/build_registry.py +242 -0
  181. dbt/include/data/csv/adapter_queries.csv +33 -0
  182. dbt/include/data/csv/syntax_rules.csv +9 -0
  183. dbt/include/data/csv/type_mappings_bigquery.csv +28 -0
  184. dbt/include/data/csv/type_mappings_databricks.csv +30 -0
  185. dbt/include/data/csv/type_mappings_mysql.csv +40 -0
  186. dbt/include/data/csv/type_mappings_oracle.csv +30 -0
  187. dbt/include/data/csv/type_mappings_postgres.csv +56 -0
  188. dbt/include/data/csv/type_mappings_redshift.csv +33 -0
  189. dbt/include/data/csv/type_mappings_snowflake.csv +38 -0
  190. dbt/include/data/csv/type_mappings_sqlserver.csv +35 -0
  191. dbt/include/starter_project/.gitignore +4 -0
  192. dbt/include/starter_project/README.md +15 -0
  193. dbt/include/starter_project/__init__.py +3 -0
  194. dbt/include/starter_project/analyses/.gitkeep +0 -0
  195. dbt/include/starter_project/dbt_project.yml +36 -0
  196. dbt/include/starter_project/macros/.gitkeep +0 -0
  197. dbt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
  198. dbt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
  199. dbt/include/starter_project/models/example/schema.yml +21 -0
  200. dbt/include/starter_project/seeds/.gitkeep +0 -0
  201. dbt/include/starter_project/snapshots/.gitkeep +0 -0
  202. dbt/include/starter_project/tests/.gitkeep +0 -0
  203. dbt/internal_deprecations.py +26 -0
  204. dbt/jsonschemas/__init__.py +3 -0
  205. dbt/jsonschemas/jsonschemas.py +309 -0
  206. dbt/jsonschemas/project/0.0.110.json +4717 -0
  207. dbt/jsonschemas/project/0.0.85.json +2015 -0
  208. dbt/jsonschemas/resources/0.0.110.json +2636 -0
  209. dbt/jsonschemas/resources/0.0.85.json +2536 -0
  210. dbt/jsonschemas/resources/latest.json +6773 -0
  211. dbt/links.py +4 -0
  212. dbt/materializations/__init__.py +0 -0
  213. dbt/materializations/incremental/__init__.py +0 -0
  214. dbt/materializations/incremental/microbatch.py +236 -0
  215. dbt/mp_context.py +8 -0
  216. dbt/node_types.py +37 -0
  217. dbt/parser/__init__.py +23 -0
  218. dbt/parser/analysis.py +21 -0
  219. dbt/parser/base.py +548 -0
  220. dbt/parser/common.py +266 -0
  221. dbt/parser/docs.py +52 -0
  222. dbt/parser/fixtures.py +51 -0
  223. dbt/parser/functions.py +30 -0
  224. dbt/parser/generic_test.py +100 -0
  225. dbt/parser/generic_test_builders.py +333 -0
  226. dbt/parser/hooks.py +118 -0
  227. dbt/parser/macros.py +137 -0
  228. dbt/parser/manifest.py +2204 -0
  229. dbt/parser/models.py +573 -0
  230. dbt/parser/partial.py +1178 -0
  231. dbt/parser/read_files.py +445 -0
  232. dbt/parser/schema_generic_tests.py +422 -0
  233. dbt/parser/schema_renderer.py +111 -0
  234. dbt/parser/schema_yaml_readers.py +935 -0
  235. dbt/parser/schemas.py +1466 -0
  236. dbt/parser/search.py +149 -0
  237. dbt/parser/seeds.py +28 -0
  238. dbt/parser/singular_test.py +20 -0
  239. dbt/parser/snapshots.py +44 -0
  240. dbt/parser/sources.py +558 -0
  241. dbt/parser/sql.py +62 -0
  242. dbt/parser/unit_tests.py +621 -0
  243. dbt/plugins/__init__.py +20 -0
  244. dbt/plugins/contracts.py +9 -0
  245. dbt/plugins/exceptions.py +2 -0
  246. dbt/plugins/manager.py +163 -0
  247. dbt/plugins/manifest.py +21 -0
  248. dbt/profiler.py +20 -0
  249. dbt/py.typed +1 -0
  250. dbt/query_analyzer.cpython-311-darwin.so +0 -0
  251. dbt/query_analyzer.py +410 -0
  252. dbt/runners/__init__.py +2 -0
  253. dbt/runners/exposure_runner.py +7 -0
  254. dbt/runners/no_op_runner.py +45 -0
  255. dbt/runners/saved_query_runner.py +7 -0
  256. dbt/selected_resources.py +8 -0
  257. dbt/task/__init__.py +0 -0
  258. dbt/task/base.py +503 -0
  259. dbt/task/build.py +197 -0
  260. dbt/task/clean.py +56 -0
  261. dbt/task/clone.py +161 -0
  262. dbt/task/compile.py +150 -0
  263. dbt/task/compute.cpython-311-darwin.so +0 -0
  264. dbt/task/compute.py +458 -0
  265. dbt/task/debug.py +505 -0
  266. dbt/task/deps.py +280 -0
  267. dbt/task/docs/__init__.py +3 -0
  268. dbt/task/docs/api/__init__.py +23 -0
  269. dbt/task/docs/api/catalog.cpython-311-darwin.so +0 -0
  270. dbt/task/docs/api/catalog.py +204 -0
  271. dbt/task/docs/api/lineage.cpython-311-darwin.so +0 -0
  272. dbt/task/docs/api/lineage.py +234 -0
  273. dbt/task/docs/api/profile.cpython-311-darwin.so +0 -0
  274. dbt/task/docs/api/profile.py +204 -0
  275. dbt/task/docs/api/spark.cpython-311-darwin.so +0 -0
  276. dbt/task/docs/api/spark.py +186 -0
  277. dbt/task/docs/generate.py +947 -0
  278. dbt/task/docs/index.html +250 -0
  279. dbt/task/docs/serve.cpython-311-darwin.so +0 -0
  280. dbt/task/docs/serve.py +174 -0
  281. dbt/task/dvt_output.py +362 -0
  282. dbt/task/dvt_run.py +204 -0
  283. dbt/task/freshness.py +322 -0
  284. dbt/task/function.py +121 -0
  285. dbt/task/group_lookup.py +46 -0
  286. dbt/task/init.cpython-311-darwin.so +0 -0
  287. dbt/task/init.py +604 -0
  288. dbt/task/java.cpython-311-darwin.so +0 -0
  289. dbt/task/java.py +316 -0
  290. dbt/task/list.py +236 -0
  291. dbt/task/metadata.cpython-311-darwin.so +0 -0
  292. dbt/task/metadata.py +804 -0
  293. dbt/task/printer.py +175 -0
  294. dbt/task/profile.cpython-311-darwin.so +0 -0
  295. dbt/task/profile.py +1307 -0
  296. dbt/task/profile_serve.py +615 -0
  297. dbt/task/retract.py +438 -0
  298. dbt/task/retry.py +175 -0
  299. dbt/task/run.py +1387 -0
  300. dbt/task/run_operation.py +141 -0
  301. dbt/task/runnable.py +758 -0
  302. dbt/task/seed.py +103 -0
  303. dbt/task/show.py +149 -0
  304. dbt/task/snapshot.py +56 -0
  305. dbt/task/spark.cpython-311-darwin.so +0 -0
  306. dbt/task/spark.py +414 -0
  307. dbt/task/sql.py +110 -0
  308. dbt/task/target_sync.cpython-311-darwin.so +0 -0
  309. dbt/task/target_sync.py +766 -0
  310. dbt/task/test.py +464 -0
  311. dbt/tests/fixtures/__init__.py +1 -0
  312. dbt/tests/fixtures/project.py +620 -0
  313. dbt/tests/util.py +651 -0
  314. dbt/tracking.py +529 -0
  315. dbt/utils/__init__.py +3 -0
  316. dbt/utils/artifact_upload.py +151 -0
  317. dbt/utils/utils.py +408 -0
  318. dbt/version.py +270 -0
  319. dvt_cli/__init__.py +72 -0
  320. dvt_core-0.58.6.dist-info/METADATA +288 -0
  321. dvt_core-0.58.6.dist-info/RECORD +324 -0
  322. dvt_core-0.58.6.dist-info/WHEEL +5 -0
  323. dvt_core-0.58.6.dist-info/entry_points.txt +2 -0
  324. dvt_core-0.58.6.dist-info/top_level.txt +2 -0
dbt/task/docs/serve.py ADDED
@@ -0,0 +1,174 @@
1
+ """
2
+ DVT Docs Serve Task
3
+
4
+ v0.56.0: Enhanced with FastAPI backend and 3-tab web UI.
5
+
6
+ Serves documentation with:
7
+ - Traditional static HTML docs (backward compatible)
8
+ - REST API for catalog, profiling, lineage, and Spark status
9
+ - 3-tab web UI: Catalog, Profiling, Spark Monitor
10
+ """
11
+
12
+ import os
13
+ import shutil
14
+ import webbrowser
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ import click
19
+
20
+ from dbt.task.base import ConfiguredTask
21
+ from dbt.task.docs import DOCS_INDEX_FILE_PATH
22
+
23
+
24
+ class ServeTask(ConfiguredTask):
25
+ """
26
+ Serve documentation with optional enhanced API.
27
+
28
+ When FastAPI is available, serves:
29
+ - /api/catalog/* - Catalog nodes and search
30
+ - /api/profile/* - Profile results and alerts
31
+ - /api/lineage/* - Lineage graph and traversal
32
+ - /api/spark/* - Spark status (local only)
33
+ - /* - Static documentation files
34
+
35
+ Falls back to simple HTTP server when FastAPI is not installed.
36
+ """
37
+
38
+ def run(self):
39
+ port = self.args.port
40
+ host = self.args.host
41
+ project_root = Path(self.config.project_root)
42
+ target_path = Path(self.config.project_target_path)
43
+
44
+ # Check if FastAPI is available for enhanced serving
45
+ if self._fastapi_available():
46
+ self._run_fastapi_server(host, port, project_root, target_path)
47
+ else:
48
+ self._run_simple_server(host, port, target_path)
49
+
50
+ def _fastapi_available(self) -> bool:
51
+ """Check if FastAPI and uvicorn are installed."""
52
+ try:
53
+ import fastapi # noqa: F401
54
+ import uvicorn # noqa: F401
55
+ return True
56
+ except ImportError:
57
+ return False
58
+
59
+ def _run_fastapi_server(
60
+ self,
61
+ host: str,
62
+ port: int,
63
+ project_root: Path,
64
+ target_path: Path,
65
+ ):
66
+ """Run enhanced FastAPI server with REST API."""
67
+ from fastapi import FastAPI
68
+ from fastapi.staticfiles import StaticFiles
69
+ from fastapi.responses import RedirectResponse
70
+ import uvicorn
71
+
72
+ # Import API routers
73
+ from dbt.task.docs.api import (
74
+ catalog_router,
75
+ profile_router,
76
+ lineage_router,
77
+ spark_router,
78
+ )
79
+ from dbt.task.docs.api import catalog, profile, lineage, spark
80
+
81
+ # Initialize metadata store ONCE at startup
82
+ try:
83
+ from dbt.compute.metadata import ProjectMetadataStore
84
+ store = ProjectMetadataStore(project_root)
85
+ store.initialize() # Only called once here
86
+ click.echo(f" Metadata store initialized: {project_root}/.dvt/metadata_store.duckdb")
87
+ except Exception as e:
88
+ click.echo(f" Warning: Could not initialize metadata store: {e}")
89
+
90
+ # Set project root for all API modules (store will skip re-initialization)
91
+ catalog.set_project_root(project_root)
92
+ profile.set_project_root(project_root)
93
+ lineage.set_project_root(project_root)
94
+ spark.set_project_root(project_root)
95
+
96
+ # Create FastAPI app
97
+ app = FastAPI(
98
+ title="DVT Documentation",
99
+ description="DVT catalog, profiling, and lineage API",
100
+ version="0.56.0",
101
+ )
102
+
103
+ # Include API routers
104
+ app.include_router(catalog_router)
105
+ app.include_router(profile_router)
106
+ app.include_router(lineage_router)
107
+ app.include_router(spark_router)
108
+
109
+ # Prepare static files directory
110
+ os.chdir(target_path)
111
+ shutil.copyfile(DOCS_INDEX_FILE_PATH, "index.html")
112
+
113
+ # Root redirect to index.html
114
+ @app.get("/")
115
+ async def root():
116
+ return RedirectResponse(url="/index.html")
117
+
118
+ # Mount static files (must be last to not override API routes)
119
+ app.mount("/", StaticFiles(directory=str(target_path), html=True), name="static")
120
+
121
+ # Open browser if requested
122
+ if self.args.browser:
123
+ webbrowser.open_new_tab(f"http://localhost:{port}")
124
+
125
+ # Print server info
126
+ click.echo("")
127
+ click.echo("╔════════════════════════════════════════════════════════════════╗")
128
+ click.echo("║ DVT Documentation Server (Enhanced) ║")
129
+ click.echo("╠════════════════════════════════════════════════════════════════╣")
130
+ click.echo(f"║ Serving at: http://{host}:{port}".ljust(66) + "║")
131
+ click.echo("║ ║")
132
+ click.echo("║ Web UI: ║")
133
+ click.echo(f"║ • Documentation: http://localhost:{port}/".ljust(66) + "║")
134
+ click.echo(f"║ • API Docs: http://localhost:{port}/docs".ljust(66) + "║")
135
+ click.echo("║ ║")
136
+ click.echo("║ API Endpoints: ║")
137
+ click.echo("║ • /api/catalog/* - Catalog nodes and search ║")
138
+ click.echo("║ • /api/profile/* - Profile results and alerts ║")
139
+ click.echo("║ • /api/lineage/* - Lineage graph and traversal ║")
140
+ click.echo("║ • /api/spark/* - Spark status (local only) ║")
141
+ click.echo("╠════════════════════════════════════════════════════════════════╣")
142
+ click.echo("║ Press Ctrl+C to stop ║")
143
+ click.echo("╚════════════════════════════════════════════════════════════════╝")
144
+ click.echo("")
145
+
146
+ # Run server
147
+ uvicorn.run(app, host=host, port=port, log_level="warning")
148
+
149
+ def _run_simple_server(self, host: str, port: int, target_path: Path):
150
+ """Run simple HTTP server (fallback when FastAPI not installed)."""
151
+ import socketserver
152
+ from http.server import SimpleHTTPRequestHandler
153
+
154
+ os.chdir(target_path)
155
+ shutil.copyfile(DOCS_INDEX_FILE_PATH, "index.html")
156
+
157
+ if self.args.browser:
158
+ webbrowser.open_new_tab(f"http://localhost:{port}")
159
+
160
+ click.echo("")
161
+ click.echo("╔════════════════════════════════════════════════════════════════╗")
162
+ click.echo("║ DVT Documentation Server (Basic) ║")
163
+ click.echo("╠════════════════════════════════════════════════════════════════╣")
164
+ click.echo(f"║ Serving at: http://{host}:{port}".ljust(66) + "║")
165
+ click.echo("║ ║")
166
+ click.echo("║ Note: Install fastapi and uvicorn for enhanced features: ║")
167
+ click.echo("║ pip install fastapi uvicorn ║")
168
+ click.echo("╠════════════════════════════════════════════════════════════════╣")
169
+ click.echo("║ Press Ctrl+C to stop ║")
170
+ click.echo("╚════════════════════════════════════════════════════════════════╝")
171
+ click.echo("")
172
+
173
+ with socketserver.TCPServer((host, port), SimpleHTTPRequestHandler) as httpd:
174
+ httpd.serve_forever()
dbt/task/dvt_output.py ADDED
@@ -0,0 +1,362 @@
1
+ # =============================================================================
2
+ # DVT Rich Output Helpers
3
+ # =============================================================================
4
+ # Beautiful CLI output using Rich library for DVT commands.
5
+ #
6
+ # DVT v0.58.0: Unified output styling for all DVT commands
7
+ # DVT v0.58.1: Enhanced for dvt run integration
8
+ # =============================================================================
9
+
10
+ from __future__ import annotations
11
+
12
+ import time
13
+ from dataclasses import dataclass, field
14
+ from datetime import datetime
15
+ from typing import Any, Dict, List, Optional, Callable
16
+
17
+ # Try to import Rich - graceful fallback if not available
18
+ try:
19
+ from rich.console import Console
20
+ from rich.progress import (
21
+ Progress,
22
+ TextColumn,
23
+ BarColumn,
24
+ MofNCompleteColumn,
25
+ TimeElapsedColumn,
26
+ SpinnerColumn,
27
+ TaskProgressColumn,
28
+ )
29
+ from rich.table import Table
30
+ from rich.panel import Panel
31
+ from rich.live import Live
32
+ from rich import box
33
+ HAS_RICH = True
34
+ except ImportError:
35
+ HAS_RICH = False
36
+ Console = None
37
+ Progress = None
38
+
39
+
40
+ @dataclass
41
+ class DVTRunStats:
42
+ """Statistics for a DVT run execution."""
43
+ total: int = 0
44
+ passed: int = 0
45
+ failed: int = 0
46
+ skipped: int = 0
47
+ warned: int = 0
48
+ errored: int = 0
49
+ start_time: float = field(default_factory=time.time)
50
+ end_time: float = 0.0
51
+
52
+ @property
53
+ def duration_seconds(self) -> float:
54
+ return self.end_time - self.start_time if self.end_time else 0.0
55
+
56
+ @property
57
+ def success_rate(self) -> float:
58
+ if self.total == 0:
59
+ return 100.0
60
+ return (self.passed / self.total) * 100
61
+
62
+
63
+ class DVTProgressDisplay:
64
+ """
65
+ Rich progress display for DVT commands.
66
+
67
+ Provides a beautiful CLI UI with:
68
+ - Header panel with command info
69
+ - Progress bar with spinner and current task
70
+ - Per-model result lines
71
+ - Summary table at the end
72
+
73
+ DVT v0.58.1: Enhanced for dvt run integration with per-model progress.
74
+ """
75
+
76
+ def __init__(self, title: str = "DVT Run", subtitle: str = ""):
77
+ self.title = title
78
+ self.subtitle = subtitle
79
+ self._console = None
80
+ self._progress = None
81
+ self._task_id = None
82
+ self._use_rich = HAS_RICH
83
+ self.stats = DVTRunStats()
84
+ self._model_results: List[Dict[str, Any]] = []
85
+
86
+ if self._use_rich:
87
+ self._console = Console()
88
+
89
+ def print_header(self, mode: str = "", target: str = "", compute: str = ""):
90
+ """Print a beautiful header panel."""
91
+ if not self._use_rich:
92
+ print(f"\n{'=' * 60}")
93
+ print(f" {self.title}")
94
+ if self.subtitle:
95
+ print(f" {self.subtitle}")
96
+ if target:
97
+ print(f" Target: {target}")
98
+ if compute:
99
+ print(f" Compute: {compute}")
100
+ print(f"{'=' * 60}\n")
101
+ return
102
+
103
+ # Build info line
104
+ info_parts = []
105
+ if target:
106
+ info_parts.append(f"[bold cyan]Target:[/bold cyan] [yellow]{target}[/yellow]")
107
+ if compute:
108
+ info_parts.append(f"[bold cyan]Compute:[/bold cyan] [yellow]{compute}[/yellow]")
109
+ if mode:
110
+ info_parts.append(f"[bold cyan]Mode:[/bold cyan] [yellow]{mode}[/yellow]")
111
+
112
+ info_line = " | ".join(info_parts) if info_parts else ""
113
+
114
+ header_panel = Panel(
115
+ info_line or "[dim]Processing models...[/dim]",
116
+ title=f"[bold magenta]{self.title}[/bold magenta]",
117
+ subtitle=f"[dim]{self.subtitle}[/dim]" if self.subtitle else None,
118
+ border_style="magenta",
119
+ box=box.DOUBLE,
120
+ )
121
+ self._console.print()
122
+ self._console.print(header_panel)
123
+ self._console.print()
124
+
125
+ def start_progress(self, total: int, description: str = "Running models..."):
126
+ """Start the progress bar."""
127
+ self.stats.total = total
128
+ self.stats.start_time = time.time()
129
+
130
+ if not self._use_rich:
131
+ print(f"Starting: {description} ({total} total)")
132
+ return
133
+
134
+ self._progress = Progress(
135
+ SpinnerColumn(),
136
+ TextColumn("[bold blue]{task.description}"),
137
+ BarColumn(bar_width=40),
138
+ TaskProgressColumn(),
139
+ MofNCompleteColumn(),
140
+ TimeElapsedColumn(),
141
+ console=self._console,
142
+ )
143
+ self._progress.__enter__()
144
+ self._task_id = self._progress.add_task(f"[cyan]{description}", total=total)
145
+
146
+ def update_current(self, model_name: str, status: str = "running"):
147
+ """Update the current model being processed."""
148
+ if not self._use_rich or not self._progress:
149
+ if status == "running":
150
+ print(f" [{self.stats.passed + self.stats.errored + 1}/{self.stats.total}] {model_name}...")
151
+ return
152
+
153
+ # Color based on status
154
+ status_colors = {
155
+ "running": "cyan",
156
+ "success": "green",
157
+ "error": "red",
158
+ "skip": "yellow",
159
+ "warn": "yellow",
160
+ }
161
+ color = status_colors.get(status, "white")
162
+
163
+ self._progress.update(
164
+ self._task_id,
165
+ description=f"[{color}]{status.upper()}[/{color}] [bold]{model_name}[/bold]"
166
+ )
167
+
168
+ def advance(self, status: str = "success"):
169
+ """Advance the progress bar and update stats."""
170
+ if status == "success" or status == "pass":
171
+ self.stats.passed += 1
172
+ elif status == "error" or status == "fail":
173
+ self.stats.errored += 1
174
+ elif status == "skip":
175
+ self.stats.skipped += 1
176
+ elif status == "warn":
177
+ self.stats.warned += 1
178
+ else:
179
+ self.stats.passed += 1
180
+
181
+ if self._use_rich and self._progress:
182
+ self._progress.advance(self._task_id)
183
+
184
+ def stop_progress(self):
185
+ """Stop the progress bar."""
186
+ self.stats.end_time = time.time()
187
+ if self._use_rich and self._progress:
188
+ self._progress.__exit__(None, None, None)
189
+ self._progress = None
190
+
191
+ def print_model_result(
192
+ self,
193
+ model_name: str,
194
+ status: str,
195
+ duration_ms: float = 0,
196
+ message: str = "",
197
+ materialization: str = "",
198
+ execution_path: str = "",
199
+ ):
200
+ """Print a single model result line.
201
+
202
+ Args:
203
+ model_name: Name of the model
204
+ status: Execution status (success, error, skip, warn)
205
+ duration_ms: Execution time in milliseconds
206
+ message: Additional message (e.g., row count, error)
207
+ materialization: Materialization type (table, view, incremental)
208
+ execution_path: DVT execution path (PUSHDOWN or FEDERATION)
209
+ """
210
+ # Store result for summary
211
+ self._model_results.append({
212
+ "name": model_name,
213
+ "status": status,
214
+ "duration_ms": duration_ms,
215
+ "message": message,
216
+ "materialization": materialization,
217
+ "execution_path": execution_path,
218
+ })
219
+
220
+ # Determine execution path indicator
221
+ path_str = ""
222
+ if execution_path:
223
+ path_str = f"[{execution_path}]"
224
+
225
+ if not self._use_rich:
226
+ status_symbol = "OK" if status in ("success", "pass") else "FAIL" if status in ("error", "fail") else "SKIP"
227
+ mat_str = f" [{materialization}]" if materialization else ""
228
+ path_display = f" {path_str}" if path_str else ""
229
+ msg_display = f" {message}" if message else ""
230
+ print(f" {status_symbol} {model_name}{path_display}{mat_str} ({duration_ms:.0f}ms){msg_display}")
231
+ return
232
+
233
+ # Rich formatted output
234
+ status_styles = {
235
+ "success": ("[green]OK[/green]", "green"),
236
+ "pass": ("[green]OK[/green]", "green"),
237
+ "error": ("[red]FAIL[/red]", "red"),
238
+ "fail": ("[red]FAIL[/red]", "red"),
239
+ "skip": ("[yellow]SKIP[/yellow]", "yellow"),
240
+ "warn": ("[yellow]WARN[/yellow]", "yellow"),
241
+ }
242
+
243
+ # Execution path styling
244
+ path_styles = {
245
+ "PUSHDOWN": "[bold blue]PUSHDOWN[/bold blue]",
246
+ "FEDERATION": "[bold magenta]FEDERATION[/bold magenta]",
247
+ }
248
+
249
+ symbol, color = status_styles.get(status, ("[white]--[/white]", "white"))
250
+ mat_str = f" [dim][{materialization}][/dim]" if materialization else ""
251
+
252
+ # Build execution path display
253
+ path_display = ""
254
+ if execution_path:
255
+ styled_path = path_styles.get(execution_path, f"[dim]{execution_path}[/dim]")
256
+ path_display = f" [{styled_path}]"
257
+
258
+ # Build message display (e.g., row count, error details)
259
+ msg_str = ""
260
+ if message and execution_path != message: # Avoid duplicating execution_path
261
+ msg_str = f" [dim]{message}[/dim]"
262
+
263
+ self._console.print(
264
+ f" {symbol} [{color}]{model_name}[/{color}]{path_display}{mat_str} "
265
+ f"[dim]({duration_ms:.0f}ms)[/dim]{msg_str}"
266
+ )
267
+
268
+ def print_summary(self, additional_info: Dict[str, Any] = None):
269
+ """Print a beautiful summary panel."""
270
+ duration = self.stats.duration_seconds
271
+
272
+ if not self._use_rich:
273
+ print(f"\n{'=' * 60}")
274
+ print(f" SUMMARY")
275
+ print(f" Passed: {self.stats.passed}/{self.stats.total}")
276
+ if self.stats.errored:
277
+ print(f" Failed: {self.stats.errored}")
278
+ if self.stats.skipped:
279
+ print(f" Skipped: {self.stats.skipped}")
280
+ print(f" Duration: {duration:.2f}s")
281
+ print(f"{'=' * 60}\n")
282
+ return
283
+
284
+ # Create summary panel
285
+ self._console.print()
286
+
287
+ # Status indicator
288
+ if self.stats.errored > 0:
289
+ status_icon = "[bold red]FAILED[/bold red]"
290
+ status_text = f"[bold red]{self.stats.errored} of {self.stats.total} models failed[/bold red]"
291
+ border_color = "red"
292
+ elif self.stats.warned > 0:
293
+ status_icon = "[bold yellow]WARNING[/bold yellow]"
294
+ status_text = f"[bold yellow]{self.stats.warned} warnings[/bold yellow]"
295
+ border_color = "yellow"
296
+ else:
297
+ status_icon = "[bold green]SUCCESS[/bold green]"
298
+ status_text = f"[bold green]All {self.stats.passed} models completed successfully[/bold green]"
299
+ border_color = "green"
300
+
301
+ # Build summary content
302
+ lines = [
303
+ f"{status_text}",
304
+ "",
305
+ f"[bold]Passed:[/bold] {self.stats.passed}",
306
+ ]
307
+ if self.stats.errored:
308
+ lines.append(f"[bold red]Failed:[/bold red] {self.stats.errored}")
309
+ if self.stats.skipped:
310
+ lines.append(f"[bold yellow]Skipped:[/bold yellow] {self.stats.skipped}")
311
+ if self.stats.warned:
312
+ lines.append(f"[bold yellow]Warned:[/bold yellow] {self.stats.warned}")
313
+ lines.append("")
314
+ lines.append(f"[dim]Duration: {duration:.2f}s[/dim]")
315
+
316
+ if additional_info:
317
+ lines.append("")
318
+ for key, value in additional_info.items():
319
+ lines.append(f"[dim]{key}: {value}[/dim]")
320
+
321
+ summary_content = "\n".join(lines)
322
+
323
+ summary_panel = Panel(
324
+ summary_content,
325
+ title=f"[bold]{self.title} Complete[/bold]",
326
+ border_style=border_color,
327
+ box=box.ROUNDED,
328
+ )
329
+ self._console.print(summary_panel)
330
+ self._console.print()
331
+
332
+
333
+ def create_progress_callback(display: DVTProgressDisplay) -> Callable:
334
+ """Create a callback function for use with dbt's event system."""
335
+ def callback(event):
336
+ # This would integrate with dbt's event system
337
+ # For now, it's a placeholder
338
+ pass
339
+ return callback
340
+
341
+
342
+ def create_dvt_run_display(target: str = "", compute: str = "") -> DVTProgressDisplay:
343
+ """
344
+ Factory function to create a DVT run display.
345
+
346
+ Usage:
347
+ display = create_dvt_run_display(target="postgres", compute="spark-local")
348
+ display.print_header()
349
+ display.start_progress(total=10)
350
+ for model in models:
351
+ display.update_current(model.name, "running")
352
+ # ... execute model ...
353
+ display.print_model_result(model.name, "success", duration_ms=123, materialization="table")
354
+ display.advance("success")
355
+ display.stop_progress()
356
+ display.print_summary()
357
+ """
358
+ display = DVTProgressDisplay(
359
+ title="DVT Run",
360
+ subtitle="Executing models with DVT compute rules",
361
+ )
362
+ return display