diff --git a/dp_creator_ii/__init__.py b/dp_creator_ii/__init__.py index 7504daf..8fbd9b5 100644 --- a/dp_creator_ii/__init__.py +++ b/dp_creator_ii/__init__.py @@ -1,7 +1,7 @@ """DP Creator II makes it easier to get started with Differential Privacy.""" import shiny -from dp_creator_ii.utils.argparse_helpers import get_csv_contrib_from_cli +from dp_creator_ii.utils.argparse_helpers import get_cli_info __version__ = "0.0.1" @@ -10,7 +10,7 @@ def main(): # pragma: no cover # We only call this here so "--help" is handled, # and to validate inputs before starting the server. - get_csv_contrib_from_cli() + get_cli_info() shiny.run_app( app="dp_creator_ii.app", diff --git a/dp_creator_ii/app/__init__.py b/dp_creator_ii/app/__init__.py index 43cf432..b91456d 100644 --- a/dp_creator_ii/app/__init__.py +++ b/dp_creator_ii/app/__init__.py @@ -3,7 +3,7 @@ from shiny import App, ui, reactive -from dp_creator_ii.utils.argparse_helpers import get_csv_contrib_from_cli +from dp_creator_ii.utils.argparse_helpers import get_cli_info from dp_creator_ii.app import analysis_panel, dataset_panel, results_panel @@ -25,29 +25,31 @@ def ctrl_c_reminder(): # pragma: no cover print("Session ended (Press CTRL+C to quit)") -def server(input, output, session): # pragma: no cover - (csv_path_from_cli, contributions_from_cli, is_demo) = get_csv_contrib_from_cli() - csv_path = reactive.value(csv_path_from_cli) - contributions = reactive.value(contributions_from_cli) - - dataset_panel.dataset_server( - input, - output, - session, - csv_path=csv_path, - contributions=contributions, - is_demo=is_demo, - ) - analysis_panel.analysis_server( - input, - output, - session, - csv_path=csv_path, - contributions=contributions, - is_demo=is_demo, - ) - results_panel.results_server(input, output, session) - session.on_ended(ctrl_c_reminder) - - -app = App(app_ui, server) +def make_server_from_cli_info(cli_info): + def server(input, output, session): # pragma: no cover + csv_path = reactive.value(cli_info.csv_path) + contributions = reactive.value(cli_info.contributions) + + dataset_panel.dataset_server( + input, + output, + session, + csv_path=csv_path, + contributions=contributions, + is_demo=cli_info.is_demo, + ) + analysis_panel.analysis_server( + input, + output, + session, + csv_path=csv_path, + contributions=contributions, + is_demo=cli_info.is_demo, + ) + results_panel.results_server(input, output, session) + session.on_ended(ctrl_c_reminder) + + return server + + +app = App(app_ui, make_server_from_cli_info(get_cli_info())) diff --git a/dp_creator_ii/app/dataset_panel.py b/dp_creator_ii/app/dataset_panel.py index 84e09c1..30922bb 100644 --- a/dp_creator_ii/app/dataset_panel.py +++ b/dp_creator_ii/app/dataset_panel.py @@ -2,14 +2,16 @@ from shiny import ui, reactive, render -from dp_creator_ii.utils.argparse_helpers import get_csv_contrib_from_cli +from dp_creator_ii.utils.argparse_helpers import get_cli_info from dp_creator_ii.app.components.outputs import output_code_sample, demo_tooltip from dp_creator_ii.utils.templates import make_privacy_unit_block def dataset_ui(): - (csv_path, contributions, is_demo) = get_csv_contrib_from_cli() - csv_placeholder = None if csv_path is None else Path(csv_path).name + cli_info = get_cli_info() + csv_placeholder = ( + None if cli_info.csv_path is None else Path(cli_info.csv_path).name + ) return ui.nav_panel( "Select Dataset", @@ -28,7 +30,7 @@ def dataset_ui(): ui.input_numeric( "contributions", ["Contributions", ui.output_ui("contributions_demo_tooltip_ui")], - contributions, + cli_info.contributions, ), ui.output_ui("python_tooltip_ui"), output_code_sample("Unit of Privacy", "unit_of_privacy_python"), diff --git a/dp_creator_ii/utils/argparse_helpers.py b/dp_creator_ii/utils/argparse_helpers.py index 3a1d362..4b6dd9d 100644 --- a/dp_creator_ii/utils/argparse_helpers.py +++ b/dp_creator_ii/utils/argparse_helpers.py @@ -4,6 +4,7 @@ import csv import random from warnings import warn +from collections import namedtuple def _existing_csv_type(arg): @@ -102,13 +103,18 @@ def _get_demo_csv_contrib(): } ) - return (csv_path, contributions, True) + return CLIInfo(csv_path=csv_path, contributions=contributions, is_demo=True) -def get_csv_contrib_from_cli(): # pragma: no cover +CLIInfo = namedtuple("CLIInfo", ["csv_path", "contributions", "is_demo"]) + + +def get_cli_info(): # pragma: no cover args = _get_args() if args.demo: if args.csv_path is not None: warn('"--demo" overrides "--csv" and "--contrib"') return _get_demo_csv_contrib() - return (args.csv_path, args.contributions, False) + return CLIInfo( + csv_path=args.csv_path, contributions=args.contributions, is_demo=False + ) diff --git a/tests/fixtures/default_app.py b/tests/fixtures/default_app.py new file mode 100644 index 0000000..d90f261 --- /dev/null +++ b/tests/fixtures/default_app.py @@ -0,0 +1,16 @@ +from shiny import App + + +from dp_creator_ii.app import app_ui, make_server_from_cli_info +from dp_creator_ii.utils.argparse_helpers import CLIInfo + +app = App( + app_ui, + make_server_from_cli_info( + CLIInfo( + csv_path=None, + contributions=None, + is_demo=False, + ) + ), +) diff --git a/tests/fixtures/demo_app.py b/tests/fixtures/demo_app.py new file mode 100644 index 0000000..169f7af --- /dev/null +++ b/tests/fixtures/demo_app.py @@ -0,0 +1,16 @@ +from shiny import App + +from dp_creator_ii.app import app_ui, make_server_from_cli_info +from dp_creator_ii.utils.argparse_helpers import CLIInfo + + +app = App( + app_ui, + make_server_from_cli_info( + CLIInfo( + csv_path=None, + contributions=None, + is_demo=True, + ) + ), +) diff --git a/tests/test_app.py b/tests/test_app.py index e80a2a8..9a75dd1 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -5,12 +5,23 @@ from shiny.pytest import create_app_fixture -app = create_app_fixture("../dp_creator_ii/app/__init__.py") +demo_app = create_app_fixture(Path(__file__).parent / "fixtures/demo_app.py") +default_app = create_app_fixture(Path(__file__).parent / "fixtures/default_app.py") +tooltip = "#choose_csv_demo_tooltip_ui svg" +for_the_demo = "For the demo, we'll imagine" # TODO: Why is incomplete coverage reported here? # https://github.com/opendp/dp-creator-ii/issues/18 -def test_app(page: Page, app: ShinyAppProc): # pragma: no cover +def test_demo_app(page: Page, demo_app: ShinyAppProc): # pragma: no cover + page.goto(demo_app.url) + expect(page).to_have_title("DP Creator II") + expect(page.get_by_text(for_the_demo)).not_to_be_visible() + page.locator(tooltip).hover() + expect(page.get_by_text(for_the_demo)).to_be_visible() + + +def test_default_app(page: Page, default_app: ShinyAppProc): # pragma: no cover pick_dataset_text = "How many rows of the CSV" perform_analysis_text = "Select numeric columns of interest" download_results_text = "You can now make a differentially private release" @@ -25,8 +36,9 @@ def expect_no_error(): expect(page.locator(".shiny-output-error")).not_to_be_attached() # -- Select dataset -- - page.goto(app.url) + page.goto(default_app.url) expect(page).to_have_title("DP Creator II") + expect(page.locator(tooltip)).to_have_count(0) expect_visible(pick_dataset_text) expect_not_visible(perform_analysis_text) expect_not_visible(download_results_text)