Skip to content

Commit b75e4aa

Browse files
sh-rpdjudjuu
andauthored
Dashboard Improvements (#2965)
* remove uneeded file * fix forwarding of pipelines dir to marimo app * disable state sync and display all schemas and remote state and schemas in pipeline overview * add support for multiple schemas * fix e2e tests, further updates pending * use dropdown instead of multiselect for schema selection add multi schema pipeline to fixtures * add last run info in pipeline overview add buttons to open pipeline folder and local data folder if present * fix loads browser to select correct schema * allow to start dashboard for a pipeline that is not there yet and add helpful error message in this case * nicer last run time formatting show pipeline error screen also when manually chnaing the pipeline name in the url * move buttons to top, add refresh buttons to sections * use raw query when constructing queries * lazy load remote state tab * fix traces and trace typing (mostly) * add exception traces to ui * add file watcher * remove test code * add source and resource state viewer to data panel * update existing unit tests * add unit test for new utils * make marimo dashboard the default app for pipeline show * update docs * update existing e2e tests for new yaml based rendering of state * move streamlit app down in sidebar * grammar fixes for dashboard strings * open duckdb in readme mode in datapanel in dashboard * remove old tests re-enable dashboard main command * add missing args to dashboard command * small fixes to e2e tests * add tests for exceptions * re-organize e2e tests into invidual tests * add basic schema selection checks * improve dashboard help and dashboard docs page * short some strings in testing to make selecting predictable * merge devel * typo --------- Co-authored-by: djudjuu <[email protected]>
1 parent 0e19b5f commit b75e4aa

File tree

35 files changed

+1053
-1322
lines changed

35 files changed

+1053
-1322
lines changed

.github/workflows/test_common.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ jobs:
154154
# Run pipeline dashboard e2e tests (does not pass with python 3.9, does not pass on windows (playwright does not work somehow), does not pass on python 3.13 (ibis not available))
155155
- name: Run dashboard e2e
156156
run: |
157-
marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines_dir _storage/.dlt/pipelines/ --with_test_identifiers true & pytest --browser chromium tests/e2e
157+
marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines-dir _storage/.dlt/pipelines/ --with_test_identifiers true & pytest --browser chromium tests/e2e
158158
if: matrix.python-version != '3.9' && matrix.os != 'windows-latest' && matrix.python-version != '3.14.0-beta.4'
159159

160160
matrix_job_required_check:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,4 @@ test-e2e-dashboard-headed:
167167
uv run pytest --headed --browser chromium tests/e2e
168168

169169
start-dlt-dashboard-e2e:
170-
uv run marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines_dir _storage/.dlt/pipelines --with_test_identifiers true
170+
uv run marimo run --headless dlt/helpers/dashboard/dlt_dashboard.py -- -- --pipelines-dir _storage/.dlt/pipelines --with_test_identifiers true

dlt/cli/command_wrappers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ def schema_command_wrapper(file_path: str, format_: str, remove_defaults: bool)
154154

155155

156156
@utils.track_command("dashboard", True)
157-
def dashboard_command_wrapper() -> None:
157+
def dashboard_command_wrapper(pipelines_dir: Optional[str], edit: bool) -> None:
158158
from dlt.helpers.dashboard.runner import run_dashboard
159159

160-
run_dashboard()
160+
run_dashboard(pipelines_dir=pipelines_dir, edit=edit)
161161

162162

163163
@utils.track_command("telemetry", False)

dlt/cli/pipeline_command.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ def pipeline_command(
5252
fmt.secho(_dir, fg="green")
5353
return
5454

55+
# we may open the dashboard for a pipeline without checking if it exists
56+
if operation == "show" and not command_kwargs.get("streamlit"):
57+
from dlt.common.utils import custom_environ
58+
from dlt.common.known_env import DLT_DATA_DIR
59+
60+
from dlt.helpers.dashboard.runner import run_dashboard
61+
62+
run_dashboard(pipeline_name, edit=command_kwargs.get("edit"), pipelines_dir=pipelines_dir)
63+
5564
try:
5665
if verbosity > 0:
5766
fmt.echo("Attaching to pipeline %s" % fmt.bold(pipeline_name))
@@ -117,39 +126,30 @@ def _display_pending_packages() -> Tuple[Sequence[str], Sequence[str]]:
117126
fmt.echo("Found pipeline %s in %s" % (fmt.bold(p.pipeline_name), fmt.bold(p.pipelines_dir)))
118127

119128
if operation == "show":
120-
if command_kwargs.get("dashboard"):
121-
from dlt.common.utils import custom_environ
122-
from dlt.common.known_env import DLT_DATA_DIR
123-
124-
from dlt.helpers.dashboard.runner import run_dashboard
125-
126-
with custom_environ({DLT_DATA_DIR: os.path.dirname(p.pipelines_dir)}):
127-
run_dashboard(pipeline_name, edit=command_kwargs.get("edit"))
128-
else:
129-
from dlt.common.runtime import signals
130-
from dlt.helpers.streamlit_app import index
131-
132-
with signals.delayed_signals():
133-
streamlit_cmd = [
134-
"streamlit",
135-
"run",
136-
index.__file__,
137-
"--client.showSidebarNavigation",
138-
"false",
139-
]
140-
141-
if hot_reload:
142-
streamlit_cmd.append("--server.runOnSave")
143-
streamlit_cmd.append("true")
144-
145-
streamlit_cmd.append("--")
146-
streamlit_cmd.append(pipeline_name)
147-
streamlit_cmd.append("--pipelines-dir")
148-
streamlit_cmd.append(p.pipelines_dir)
149-
150-
venv = Venv.restore_current()
151-
for line in iter_stdout(venv, *streamlit_cmd):
152-
fmt.echo(line)
129+
from dlt.common.runtime import signals
130+
from dlt.helpers.streamlit_app import index
131+
132+
with signals.delayed_signals():
133+
streamlit_cmd = [
134+
"streamlit",
135+
"run",
136+
index.__file__,
137+
"--client.showSidebarNavigation",
138+
"false",
139+
]
140+
141+
if hot_reload:
142+
streamlit_cmd.append("--server.runOnSave")
143+
streamlit_cmd.append("true")
144+
145+
streamlit_cmd.append("--")
146+
streamlit_cmd.append(pipeline_name)
147+
streamlit_cmd.append("--pipelines-dir")
148+
streamlit_cmd.append(p.pipelines_dir)
149+
150+
venv = Venv.restore_current()
151+
for line in iter_stdout(venv, *streamlit_cmd):
152+
fmt.echo(line)
153153

154154
if operation == "info":
155155
state: TSourceState = p.state # type: ignore

dlt/cli/plugins.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -206,30 +206,29 @@ def configure_parser(self, pipe_cmd: argparse.ArgumentParser) -> None:
206206
"Generates and launches Streamlit app with the loading status and dataset explorer"
207207
),
208208
description="""
209-
Generates and launches Streamlit (https://streamlit.io/) app with the loading status and dataset explorer.
209+
Launches the pipeline dashboard app with a comprehensive interface to inspect the pipeline state, schemas, and data in the destination.
210210
211-
This is a simple app that you can use to inspect the schemas and data in the destination as well as your pipeline state and loading status/stats. It should be executed from the same folder from which you ran the pipeline script to access destination credentials.
211+
This app should be executed from the same folder from which you ran the pipeline script to be able access destination credentials.
212212
213-
Requires `streamlit` to be installed in the current environment: `pip install streamlit`. Using --dashboard flag to launch pipeline dashboard preview instead of streamlit.
213+
If the --edit flag is used, will launch the editable version of the app if it exists in the current directory, or create this version and launch it in edit mode.
214+
215+
Requires `marimo` to be installed in the current environment: `pip install marimo`. Use the --streamlit flag to launch the legacy streamlit app.
214216
""",
215217
)
216218
show_cmd.add_argument(
217-
"--dashboard",
219+
"--streamlit",
218220
default=False,
219221
action="store_true",
220-
help=(
221-
"Launch pipeline dashboard instead of streamlit. Will launch editable version of"
222-
" app (created with the --edit flag) if it exists in the current directory."
223-
),
222+
help="Launch the legacy Streamlit dashboard instead of the new pipeline dashboard. ",
224223
)
225224
show_cmd.add_argument(
226225
"--edit",
227226
default=False,
228227
action="store_true",
229228
help=(
230229
"Creates editable version of pipeline dashboard in current directory if it does not"
231-
" exist there yet and launches it in edit mode. Only works when using the pipeline"
232-
" dashboard (--dashboard flag)."
230+
" exist there yet and launches it in edit mode. Will have no effect when using the"
231+
" streamlit flag."
233232
),
234233
)
235234
pipeline_subparsers.add_parser(
@@ -494,9 +493,18 @@ class DashboardCommand(SupportsCliCommand):
494493

495494
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
496495
self.parser = parser
496+
self.parser.add_argument(
497+
"--pipelines-dir", help="Pipelines working directory", default=None
498+
)
499+
self.parser.add_argument(
500+
"--edit",
501+
action="store_true",
502+
help="Eject Dashboard and start editable version",
503+
default=None,
504+
)
497505

498506
def execute(self, args: argparse.Namespace) -> None:
499-
dashboard_command_wrapper()
507+
dashboard_command_wrapper(pipelines_dir=args.pipelines_dir, edit=args.edit)
500508

501509

502510
class TelemetryCommand(SupportsCliCommand):
@@ -744,9 +752,9 @@ def plug_cli_schema() -> Type[SupportsCliCommand]:
744752

745753

746754
# TODO: define actual command and re-enable
747-
# @plugins.hookimpl(specname="plug_cli")
748-
# def plug_cli_dashboard() -> Type[SupportsCliCommand]:
749-
# return DashboardCommand
755+
@plugins.hookimpl(specname="plug_cli")
756+
def plug_cli_dashboard() -> Type[SupportsCliCommand]:
757+
return DashboardCommand
750758

751759

752760
@plugins.hookimpl(specname="plug_cli")

dlt/common/destination/dataset.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,12 @@ def __exit__(
449449
"""Context manager to keep the connection to the destination open between queries"""
450450
...
451451

452-
def ibis(self) -> IbisBackend:
452+
def ibis(self, read_only: bool = False) -> IbisBackend:
453453
"""Returns a connected ibis backend for the dataset. Not implemented for all destinations.
454454
455+
Args:
456+
read_only (bool): Whether to open the connection in read only mode. Only used for duckdb.
457+
455458
Returns:
456459
IbisBackend: The ibis backend for the dataset
457460
"""

dlt/dataset/dataset.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,14 @@ def __init__(
5959
self._opened_sql_client: SqlClientBase[Any] = None
6060
self._table_client: SupportsOpenTables = None
6161

62-
def ibis(self) -> IbisBackend:
63-
"""return a connected ibis backend"""
62+
def ibis(self, read_only: bool = False) -> IbisBackend:
63+
"""return a connected ibis backend, read-only flag is only used for duckdb"""
6464
from dlt.helpers.ibis import create_ibis_backend
6565

6666
return create_ibis_backend(
6767
self._destination,
6868
self._get_destination_client(self.schema),
69+
read_only=read_only,
6970
)
7071

7172
@property

dlt/dataset/relation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,10 @@ def cursor(self) -> Generator[SupportsDataAccess, Any, Any]:
219219
#
220220
# Query / Expression Management
221221
#
222-
def to_sql(self, pretty: bool = False) -> str:
222+
def to_sql(self, pretty: bool = False, _raw_query: bool = False) -> str:
223223
"""Returns an executable sql query string in the correct sql dialect for this relation"""
224224

225-
if self.__execute_raw_query:
225+
if self.__execute_raw_query or _raw_query:
226226
query = self._sqlglot_expression
227227
else:
228228
query = self._normalized_query

0 commit comments

Comments
 (0)