Skip to content

Commit

Permalink
Disable buttons until required inputs supplied (#126)
Browse files Browse the repository at this point in the history
* conditionally disable button

* test button disabled

* explain disabled

* apply the same logic on the analysis tab

* test download results button disabling

* use a reactive calc rather than events which set a value

* another reactive calc
  • Loading branch information
mccalluc authored Nov 5, 2024
1 parent 02ef2ad commit 14f06a1
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
25 changes: 21 additions & 4 deletions dp_creator_ii/app/analysis_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def analysis_ui():
log_slider("log_epsilon_slider", 0.1, 10.0),
ui.output_text("epsilon"),
output_code_sample("Privacy Loss", "privacy_loss_python"),
ui.input_action_button("go_to_results", "Download results"),
ui.output_ui("download_results_button_ui"),
value="analysis_panel",
)

Expand All @@ -41,6 +41,10 @@ def analysis_server(
contributions=None,
is_demo=None,
): # pragma: no cover
@reactive.calc
def button_enabled():
column_ids_selected = input.columns_checkbox_group()
return len(column_ids_selected) > 0

weights = reactive.value({})

Expand All @@ -67,9 +71,9 @@ def _update_checkbox_group():

@reactive.effect
@reactive.event(input.columns_checkbox_group)
def _update_weights():
column_ids_to_keep = input.columns_checkbox_group()
clear_column_weights(column_ids_to_keep)
def _on_column_set_change():
column_ids_selected = input.columns_checkbox_group()
clear_column_weights(column_ids_selected)

@render.ui
def columns_ui():
Expand Down Expand Up @@ -115,3 +119,16 @@ def privacy_loss_python():
@reactive.event(input.go_to_results)
def go_to_results():
ui.update_navs("top_level_nav", selected="results_panel")

@render.ui
def download_results_button_ui():
button = ui.input_action_button(
"go_to_results", "Download results", disabled=not button_enabled()
)

if button_enabled():
return button
return [
button,
"Select one or more columns before proceeding.",
]
23 changes: 22 additions & 1 deletion dp_creator_ii/app/dataset_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ def dataset_ui():
"contributions",
["Contributions", ui.output_ui("contributions_demo_tooltip_ui")],
cli_info.contributions,
min=1,
),
ui.output_ui("python_tooltip_ui"),
output_code_sample("Unit of Privacy", "unit_of_privacy_python"),
ui.input_action_button("go_to_analysis", "Define analysis"),
ui.output_ui("define_analysis_button_ui"),
value="dataset_panel",
)

Expand All @@ -52,6 +53,14 @@ def _on_csv_path_change():
def _on_contributions_change():
contributions.set(input.contributions())

@reactive.calc
def button_enabled():
contributions_is_set = input.contributions() is not None
csv_path_is_set = (
input.csv_path() is not None and len(input.csv_path()) > 0
) or is_demo
return contributions_is_set and csv_path_is_set

@render.ui
def choose_csv_demo_tooltip_ui():
return demo_tooltip(
Expand All @@ -78,6 +87,18 @@ def python_tooltip_ui():
"for the entire calculation.",
)

@render.ui
def define_analysis_button_ui():
button = ui.input_action_button(
"go_to_analysis", "Define analysis", disabled=not button_enabled()
)
if button_enabled():
return button
return [
button,
"Choose CSV and Contributions before proceeding.",
]

@render.code
def unit_of_privacy_python():
return make_privacy_unit_block(contributions())
Expand Down
15 changes: 13 additions & 2 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,17 @@ def expect_no_error():
expect_visible("dp.unit_of(contributions=42)")
expect_no_error()

# Button disabled until upload:
define_analysis_button = page.get_by_role("button", name="Define analysis")
assert define_analysis_button.is_disabled()

# Now upload:
csv_path = Path(__file__).parent / "fixtures" / "fake.csv"
page.get_by_label("Choose CSV file").set_input_files(csv_path.resolve())
expect_no_error()

# -- Define analysis --
page.get_by_role("button", name="Define analysis").click()
define_analysis_button.click()
expect_not_visible(pick_dataset_text)
expect_visible(perform_analysis_text)
expect_not_visible(download_results_text)
Expand All @@ -72,12 +77,18 @@ def expect_no_error():
expect_visible("Epsilon: 0.316")
page.locator(".irs-bar").click()
expect_visible("Epsilon: 0.158")

# Button disabled until column selected:
download_results_button = page.get_by_role("button", name="Download results")
assert download_results_button.is_disabled()

# Set column details:
page.get_by_label("grade").check()
page.get_by_label("Min").click()
page.get_by_label("Min").fill("0")
# TODO: All these recalculations cause timeouts:
# It is still rerendering the graph after hitting "Download results".
# https://github.com/opendp/dp-creator-ii/issues/116
# page.get_by_label("Max").click()
# page.get_by_label("Max").fill("100")
# page.get_by_label("Bins").click()
Expand All @@ -86,7 +97,7 @@ def expect_no_error():
expect_no_error()

# -- Download results --
page.get_by_role("button", name="Download results").click()
download_results_button.click()
expect_not_visible(pick_dataset_text)
expect_not_visible(perform_analysis_text)
expect_visible(download_results_text)
Expand Down

0 comments on commit 14f06a1

Please sign in to comment.