Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Info boxes about locking panels (without actually doing it) #171

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion dp_wizard/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path
import logging

from shiny import App, ui, reactive, Inputs, Outputs, Session
from shiny import App, ui, reactive, Inputs, Outputs, Session, render

from dp_wizard.utils.argparse_helpers import get_cli_info, CLIInfo
from dp_wizard.app import analysis_panel, dataset_panel, results_panel, feedback_panel
Expand Down Expand Up @@ -37,6 +37,11 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
bin_counts = reactive.value({})
weights = reactive.value({})
epsilon = reactive.value(1.0)
current_panel = reactive.value(dataset_panel.dataset_panel_id)

@render.text
def current_panel_text():
return current_panel()

dataset_panel.dataset_server(
input,
Expand All @@ -45,6 +50,7 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
is_demo=cli_info.is_demo,
csv_path=csv_path,
contributions=contributions,
current_panel=current_panel,
)
analysis_panel.analysis_server(
input,
Expand All @@ -58,6 +64,7 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
bin_counts=bin_counts,
weights=weights,
epsilon=epsilon,
current_panel=current_panel,
)
results_panel.results_server(
input,
Expand All @@ -70,6 +77,7 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
bin_counts=bin_counts,
weights=weights,
epsilon=epsilon,
current_panel=current_panel,
)
feedback_panel.feedback_server(
input,
Expand Down
32 changes: 29 additions & 3 deletions dp_wizard/app/analysis_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
from dp_wizard.utils.dp_helper import confidence
from dp_wizard.app.components.outputs import output_code_sample, demo_tooltip
from dp_wizard.utils.code_generators import make_privacy_loss_block
from dp_wizard.app import results_panel
from dp_wizard.app.components.outputs import info_box

analysis_panel_id = "2_analysis_panel"


def analysis_ui():
return ui.nav_panel(
"Define Analysis",
ui.output_ui("analysis_panel_warning"),
ui.layout_columns(
ui.card(
ui.card_header("Columns"),
Expand Down Expand Up @@ -71,7 +76,7 @@ def analysis_ui():
),
ui.output_ui("columns_ui"),
ui.output_ui("download_results_button_ui"),
value="analysis_panel",
value=analysis_panel_id,
)


Expand All @@ -97,7 +102,27 @@ def analysis_server(
bin_counts: reactive.Value[dict[str, int]],
weights: reactive.Value[dict[str, str]],
epsilon: reactive.Value[float],
current_panel,
): # pragma: no cover
@render.ui
def analysis_panel_warning():
if current_panel() > analysis_panel_id:
return info_box(
"""
Once you've confirmed your analysis settings
they are locked. The privacy budget should be considered
a finite resource.
Comment on lines +112 to +114
Copy link
Member

@Shoeboxam Shoeboxam Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon further consideration, I've weakened the "we should lock once set" opinion I had, because I had a specific vision that may not be consistent with the intended use-case.

I think there are two modes:

  1. privacy unit and loss are configured externally and can never be changed
  2. privacy unit and loss are configured by analyst and can be changed, but with some cautions:

What to caution about:

  1. If there are existing releases, state that changing the privacy unit will re-run the release? Or maybe reset all stats? It should explain that changing the privacy unit is resetting the app in some way, or causing new releases to be run, and dropping the previous privacy spend. As we move forward, you (Chuck) may want to keep in mind that changing the input metric (from symmetric distance to user id distance, for example) could potentially make later queries invalid.
  2. The privacy unit has a significant impact on the integrity of the privacy guarantee. Please carefully consider if your new privacy unit is meaningful. While making your privacy unit more fine-grained can give the appearance of greater utility for the same privacy loss, it commensurately weakens the strength of the privacy guarantee.

"""
)
if current_panel() < analysis_panel_id:
return info_box(
"""
This form is locked until you've confirmed your
dataset and unit of privacy.
"""
)
return ""

@reactive.calc
def button_enabled():
column_ids_selected = input.columns_checkbox_group()
Expand Down Expand Up @@ -186,7 +211,8 @@ def privacy_loss_python():
@reactive.effect
@reactive.event(input.go_to_results)
def go_to_results():
ui.update_navs("top_level_nav", selected="results_panel")
current_panel.set(results_panel.results_panel_id)
ui.update_navs("top_level_nav", selected=results_panel.results_panel_id)

@render.ui
def download_results_button_ui():
Expand All @@ -198,5 +224,5 @@ def download_results_button_ui():
return button
return [
button,
"Select one or more columns before proceeding.",
info_box("Select one or more columns before proceeding."),
]
4 changes: 4 additions & 0 deletions dp_wizard/app/components/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def demo_tooltip(is_demo: bool, text: str): # pragma: no cover
)


def info_box(text): # pragma: no cover
return ui.div(text, class_="alert alert-info col-md-10 col-lg-8", role="alert")


def hide_if(condition: bool, el): # pragma: no cover
display = "none" if condition else "block"
return ui.div(el, style=f"display: {display};")
26 changes: 23 additions & 3 deletions dp_wizard/app/dataset_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
from dp_wizard.utils.argparse_helpers import get_cli_info
from dp_wizard.app.components.outputs import output_code_sample, demo_tooltip
from dp_wizard.utils.code_generators import make_privacy_unit_block
from dp_wizard.app import analysis_panel
from dp_wizard.app.components.outputs import info_box

dataset_panel_id = "1_dataset_panel"


def dataset_ui():
Expand All @@ -13,6 +17,7 @@ def dataset_ui():

return ui.nav_panel(
"Select Dataset",
ui.output_ui("dataset_panel_warning"),
# Doesn't seem to be possible to preset the actual value,
# but the placeholder string is a good substitute.
ui.input_file(
Expand All @@ -34,7 +39,7 @@ def dataset_ui():
ui.output_ui("python_tooltip_ui"),
output_code_sample("Unit of Privacy", "unit_of_privacy_python"),
ui.output_ui("define_analysis_button_ui"),
value="dataset_panel",
value=dataset_panel_id,
)


Expand All @@ -45,7 +50,21 @@ def dataset_server(
csv_path: reactive.Value[str],
contributions: reactive.Value[int],
is_demo: bool,
current_panel: reactive.Value[str],
): # pragma: no cover
@render.ui
def dataset_panel_warning():
if current_panel() > dataset_panel_id:
return info_box(
"""
Once you've confirmed your dataset and the unit of privacy
they are locked. The unit of privacy is a characteristic
of your dataset and shouldn't be tweaked just to improve
utility.
"""
)
return ""

@reactive.effect
@reactive.event(input.csv_path)
def _on_csv_path_change():
Expand Down Expand Up @@ -99,7 +118,7 @@ def define_analysis_button_ui():
return button
return [
button,
"Choose CSV and Contributions before proceeding.",
info_box("Choose CSV and Contributions before proceeding."),
]

@render.code
Expand All @@ -109,4 +128,5 @@ def unit_of_privacy_python():
@reactive.effect
@reactive.event(input.go_to_analysis)
def go_to_analysis():
ui.update_navs("top_level_nav", selected="analysis_panel")
current_panel.set(analysis_panel.analysis_panel_id)
ui.update_navs("top_level_nav", selected=analysis_panel.analysis_panel_id)
18 changes: 17 additions & 1 deletion dp_wizard/app/results_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
AnalysisPlanColumn,
)
from dp_wizard.utils.converters import convert_py_to_nb
from dp_wizard.app.components.outputs import info_box

results_panel_id = "3_results_panel"


def results_ui():
return ui.nav_panel(
"Download results",
ui.output_ui("results_panel_warning"),
ui.markdown("You can now make a differentially private release of your data."),
ui.download_button(
"download_script",
Expand All @@ -21,7 +25,7 @@ def results_ui():
"download_notebook",
"Download Notebook (.ipynb)",
),
value="results_panel",
value=results_panel_id,
)


Expand All @@ -36,7 +40,19 @@ def results_server(
bin_counts: reactive.Value[dict[str, int]],
weights: reactive.Value[dict[str, str]],
epsilon: reactive.Value[float],
current_panel: reactive.Value[str],
): # pragma: no cover
@render.ui
def results_panel_warning():
if current_panel() < results_panel_id:
return info_box(
"""
This tab is locked until you've confirmed your
analysis details.
"""
)
return ""

@reactive.calc
def analysis_plan() -> AnalysisPlan:
# weights().keys() will reflect the desired columns:
Expand Down
Loading