Skip to content

Commit

Permalink
Add --quiet option to the query CLI command (#1680)
Browse files Browse the repository at this point in the history
Resolves #1554
  • Loading branch information
plypaul authored Feb 23, 2025
1 parent 24204e7 commit 9042655
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20250221-153551.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Add `--quiet` option to the `query` CLI command.
time: 2025-02-21T15:35:51.935247-08:00
custom:
Author: plypaul
Issue: "1554"
38 changes: 28 additions & 10 deletions dbt-metricflow/dbt_metricflow/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ def tutorial(ctx: click.core.Context, cfg: CLIConfiguration, message: bool, clea
dbtMetricFlowTutorialHelper.run_tutorial(cfg=cfg, message=message, clean=clean, yes=yes)


def _click_echo(message: str, quiet: bool) -> None:
"""Helper method to call echo depending on whether `quiet` is set."""
if not quiet:
click.echo(message)


@cli.command()
@query_options
@click.option(
Expand Down Expand Up @@ -158,6 +164,12 @@ def tutorial(ctx: click.core.Context, cfg: CLIConfiguration, message: bool, clea
required=False,
help="Specify the name of the saved query to use for applicable parameters",
)
@click.option(
"--quiet",
required=False,
help="Minimize output to the console.",
is_flag=True,
)
@pass_config
@exception_handler
@error_if_not_in_dbt_project
Expand All @@ -178,12 +190,16 @@ def query(
decimals: int = DEFAULT_RESULT_DECIMAL_PLACES,
show_sql_descriptions: bool = False,
saved_query: Optional[str] = None,
quiet: bool = False,
) -> None:
"""Create a new query with MetricFlow and assembles a MetricFlowQueryResult."""
cfg.setup()
start = time.time()
spinner = Halo(text="Initiating query…", spinner="dots")
spinner.start()
spinner: Optional[Halo] = None
if not quiet:
spinner = Halo(text="Initiating query…", spinner="dots")
spinner.start()

mf_request = MetricFlowQueryRequest.create_with_random_request_id(
saved_query_name=saved_query,
metric_names=metrics,
Expand All @@ -203,7 +219,8 @@ def query(
else:
query_result = cfg.mf.query(mf_request=mf_request)

spinner.succeed(f"Success 🦄 - query completed after {time.time() - start:.2f} seconds")
if spinner is not None:
spinner.succeed(f"Success 🦄 - query completed after {time.time() - start:.2f} seconds")

if explain:
assert explain_result
Expand All @@ -213,7 +230,7 @@ def query(
else explain_result.sql_statement.sql
)
if show_dataflow_plan:
click.echo("🔎 Generated Dataflow Plan + SQL (remove --explain to see data):")
_click_echo("🔎 Generated Dataflow Plan + SQL (remove --explain to see data):", quiet=quiet)
click.echo(
textwrap.indent(
jinja2.Template(
Expand All @@ -230,16 +247,17 @@ def query(
)
click.echo("")
else:
click.echo(
_click_echo(
"🔎 SQL (remove --explain to see data or add --show-dataflow-plan to see the generated dataflow plan):"
"\n"
"\n",
quiet=quiet,
)
click.echo(sql)
if display_plans:
click.echo("Creating temporary directory for storing visualization output.")
_click_echo("Creating temporary directory for storing visualization output.", quiet=quiet)
temp_path = tempfile.mkdtemp()
svg_path = display_dag_as_svg(explain_result.dataflow_plan, temp_path)
click.echo("")
_click_echo("", quiet=quiet)
click.echo(f"Plan SVG saved to: {svg_path}")
exit()

Expand All @@ -248,14 +266,14 @@ def query(
# Show the data if returned successfully
if df is not None:
if df.row_count == 0:
click.echo("🕳 Successful MQL query returned an empty result set.")
_click_echo("🕳 Query returned an empty result set", quiet=quiet)
elif csv is not None:
# csv is a LazyFile that is file-like that works in this case.
csv_writer = csv_module.writer(csv)
csv_writer.writerow(df.column_names)
for row in df.rows:
csv_writer.writerow(row)
click.echo(f"🖨 Successfully written query output to {csv.name}")
_click_echo(f"🖨 Wrote query output to {csv.name}", quiet=quiet)
else:
click.echo(df.text_format(decimals))
if display_plans:
Expand Down
38 changes: 38 additions & 0 deletions tests_metricflow/cli/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations

from typing import Optional, Sequence

import click
from _pytest.capture import CaptureFixture
from _pytest.fixtures import FixtureRequest
from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration

from metricflow.protocols.sql_client import SqlClient
from tests_metricflow.fixtures.cli_fixtures import MetricFlowCliRunner
from tests_metricflow.snapshot_utils import assert_str_snapshot_equal


def run_and_check_cli_command(
request: FixtureRequest,
capsys: CaptureFixture,
mf_test_configuration: MetricFlowTestConfiguration,
cli_runner: MetricFlowCliRunner,
command: click.BaseCommand,
args: Sequence[str],
sql_client: Optional[SqlClient] = None,
assert_zero_exit_code: bool = True,
) -> None:
"""Helper to run a CLI command and check that the output matches the stored snapshot."""
# Needed to resolve `ValueError: I/O operation on closed file` when running CLI tests individually.
# See: https://github.com/pallets/click/issues/824
with capsys.disabled():
result = cli_runner.run(command, args=args)
assert_str_snapshot_equal(
request=request,
mf_test_configuration=mf_test_configuration,
snapshot_id="result",
snapshot_str=result.stdout,
sql_engine=sql_client.sql_engine_type if sql_client else None,
)
if assert_zero_exit_code:
assert result.exit_code == 0
57 changes: 57 additions & 0 deletions tests_metricflow/cli/test_cli_quiet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from __future__ import annotations

import logging

import pytest
from _pytest.capture import CaptureFixture
from _pytest.fixtures import FixtureRequest
from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration

from dbt_metricflow.cli.main import query
from metricflow.protocols.sql_client import SqlClient
from tests_metricflow.cli.conftest import run_and_check_cli_command
from tests_metricflow.fixtures.cli_fixtures import MetricFlowCliRunner

logger = logging.getLogger(__name__)


@pytest.mark.sql_engine_snapshot
@pytest.mark.duckdb_only
def test_query(
mf_test_configuration: MetricFlowTestConfiguration,
request: FixtureRequest,
capsys: CaptureFixture,
cli_runner: MetricFlowCliRunner,
sql_client: SqlClient,
) -> None:
"""Test that the `--quiet` flag only shows the table when running a query."""
run_and_check_cli_command(
request=request,
capsys=capsys,
mf_test_configuration=mf_test_configuration,
cli_runner=cli_runner,
command=query,
args=["--metrics", "bookings", "--group-by", "metric_time", "--order", "metric_time", "--quiet"],
sql_client=sql_client,
)


@pytest.mark.sql_engine_snapshot
@pytest.mark.duckdb_only
def test_explain(
mf_test_configuration: MetricFlowTestConfiguration,
request: FixtureRequest,
capsys: CaptureFixture,
cli_runner: MetricFlowCliRunner,
sql_client: SqlClient,
) -> None:
"""Test that the `--quiet` flag only shows the SQL when explaining a query."""
run_and_check_cli_command(
request=request,
capsys=capsys,
mf_test_configuration=mf_test_configuration,
cli_runner=cli_runner,
command=query,
args=["--metrics", "bookings", "--group-by", "metric_time", "--order", "metric_time", "--explain", "--quiet"],
sql_client=sql_client,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
test_name: test_explain
test_filename: test_cli_quiet.py
docstring:
Test that the `--quiet` flag only shows the SQL when explaining a query.
---
SELECT
metric_time__day
, SUM(bookings) AS bookings
FROM (
SELECT
DATE_TRUNC('day', ds) AS metric_time__day
, 1 AS bookings
FROM ***************************.fct_bookings bookings_source_src_10000
) subq_2
GROUP BY
metric_time__day
ORDER BY metric_time__day
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
test_name: test_query
test_filename: test_cli_quiet.py
docstring:
Test that the `--quiet` flag only shows the table when running a query.
---
metric_time__day bookings
------------------- ----------
2019-12-01T00:00:00 1
2019-12-18T00:00:00 10
2019-12-19T00:00:00 18
2019-12-20T00:00:00 2
2020-01-01T00:00:00 5
2020-01-02T00:00:00 9
2020-01-03T00:00:00 1

0 comments on commit 9042655

Please sign in to comment.