diff --git a/README-PYPI.md b/README-PYPI.md
index dd40d1e9..a3d57701 100644
--- a/README-PYPI.md
+++ b/README-PYPI.md
@@ -8,12 +8,10 @@ DP Wizard demonstrates how to calculate DP statistics or create a synthetic data
(If differential privacy is new to you, [these slides](https://opendp.github.io/dp-wizard/) provide some background, and explain how DP Wizard works.)
-You can run DP Wizard locally and upload your own CSV,
-or use the [cloud deployment](https://mccalluc-dp-wizard.share.connect.posit.cloud/) and only provide column names to protect your private data.
-In either case, you'll be prompted to describe your privacy budget and the analysis you need.
-With that information, DP Wizard provides:
-
-- A Jupyter notebook which demonstrates how to use the [OpenDP Library](https://docs.opendp.org/).
+After selecting a local CSV, you'll be prompted to describe the analysis you need.
+Output options include:
+- A Jupyter notebook which demonstrates how to use
+the [OpenDP Library](https://docs.opendp.org/).
- A plain Python script.
- Text and CSV reports.
@@ -39,7 +37,7 @@ The exact upgrade process will depend on your environment and operating system.
Install with `pip install 'dp_wizard[app]'` and you can start DP Wizard from the command line.
```
-usage: dp-wizard [-h] [--sample | --cloud]
+usage: dp-wizard [-h] [--sample]
DP Wizard makes it easier to get started with Differential Privacy.
@@ -47,9 +45,8 @@ options:
-h, --help show this help message and exit
--sample Generate a sample CSV: See how DP Wizard works without providing
your own data
- --cloud Prompt for column names instead of CSV upload
-Unless you have set "--sample" or "--cloud", you will specify a CSV
+Unless you have set "--sample", you will specify a CSV
inside the application.
Provide a "Private CSV" if you only have a private data set, and want to
diff --git a/README.md b/README.md
index a9bd88bb..1923eff5 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,10 @@ DP Wizard demonstrates how to calculate DP statistics or create a synthetic data
(If differential privacy is new to you, [these slides](https://opendp.github.io/dp-wizard/) provide some background, and explain how DP Wizard works.)
-You can run DP Wizard locally and upload your own CSV,
-or use the [cloud deployment](https://mccalluc-dp-wizard.share.connect.posit.cloud/) and only provide column names to protect your private data.
-In either case, you'll be prompted to describe your privacy budget and the analysis you need.
-With that information, DP Wizard provides:
-
-- A Jupyter notebook which demonstrates how to use the [OpenDP Library](https://docs.opendp.org/).
+After selecting a local CSV, you'll be prompted to describe the analysis you need.
+Output options include:
+- A Jupyter notebook which demonstrates how to use
+the [OpenDP Library](https://docs.opendp.org/).
- A plain Python script.
- Text and CSV reports.
@@ -39,7 +37,7 @@ The exact upgrade process will depend on your environment and operating system.
Install with `pip install 'dp_wizard[app]'` and you can start DP Wizard from the command line.
```
-usage: dp-wizard [-h] [--sample | --cloud]
+usage: dp-wizard [-h] [--sample]
DP Wizard makes it easier to get started with Differential Privacy.
@@ -47,9 +45,8 @@ options:
-h, --help show this help message and exit
--sample Generate a sample CSV: See how DP Wizard works without providing
your own data
- --cloud Prompt for column names instead of CSV upload
-Unless you have set "--sample" or "--cloud", you will specify a CSV
+Unless you have set "--sample", you will specify a CSV
inside the application.
Provide a "Private CSV" if you only have a private data set, and want to
diff --git a/docs/index.html b/docs/index.html
index 8fd8ccc4..4c1657c8 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -645,7 +645,7 @@
-**[`pip install 'dp_wizard[app]'`](https://pypi.org/project/dp_wizard/) `dp_wizard --cloud` (requires Python>=3.10)**
+**[`pip install 'dp_wizard[app]'`](https://pypi.org/project/dp_wizard/) `dp_wizard --sample` (requires Python>=3.10)**
|
diff --git a/dp_wizard/shiny/__init__.py b/dp_wizard/shiny/__init__.py
index 4deeecbd..7c4f1f75 100644
--- a/dp_wizard/shiny/__init__.py
+++ b/dp_wizard/shiny/__init__.py
@@ -111,7 +111,6 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
state = AppState(
# CLI options:
is_sample_csv=cli_info.is_sample_csv,
- in_cloud=cli_info.is_cloud_mode,
qa_mode=cli_info.is_qa_mode,
# Reactive bools:
is_tutorial_mode=reactive.value(cli_info.get_is_tutorial_mode()),
diff --git a/dp_wizard/shiny/components/summaries.py b/dp_wizard/shiny/components/summaries.py
index b7d230ba..9f268993 100644
--- a/dp_wizard/shiny/components/summaries.py
+++ b/dp_wizard/shiny/components/summaries.py
@@ -19,8 +19,6 @@ def dataset_summary(state: AppState): # pragma: no cover
sources.append("Private CSV")
if state.public_csv_path():
sources.append("Public CSV")
- if state.in_cloud:
- sources.append("Field List")
contributions = state.contributions()
entity = state.contributions_entity()
diff --git a/dp_wizard/shiny/panels/analysis_panel/__init__.py b/dp_wizard/shiny/panels/analysis_panel/__init__.py
index dd289068..8337ff39 100644
--- a/dp_wizard/shiny/panels/analysis_panel/__init__.py
+++ b/dp_wizard/shiny/panels/analysis_panel/__init__.py
@@ -147,7 +147,6 @@ def analysis_server(
): # pragma: no cover
# CLI options:
is_sample_csv = state.is_sample_csv
- # in_cloud = state.in_cloud
# Reactive bools:
is_tutorial_mode = state.is_tutorial_mode
diff --git a/dp_wizard/shiny/panels/dataset_panel/__init__.py b/dp_wizard/shiny/panels/dataset_panel/__init__.py
index 04dc14d2..4b21c7ad 100644
--- a/dp_wizard/shiny/panels/dataset_panel/__init__.py
+++ b/dp_wizard/shiny/panels/dataset_panel/__init__.py
@@ -118,7 +118,6 @@ def dataset_server(
): # pragma: no cover
# CLI options:
is_sample_csv = state.is_sample_csv
- in_cloud = state.in_cloud
# Reactive bools:
is_tutorial_mode = state.is_tutorial_mode
@@ -169,12 +168,6 @@ def _on_private_csv_path_change():
private_csv_path.set(path)
csv_info.set(CsvInfo(Path(path)))
- @reactive.effect
- @reactive.event(input.all_column_names)
- def _on_column_names_change():
- # Only used when the user is supplying column names in cloud mode.
- csv_info.set(infer_csv_info(input.all_column_names()))
-
@reactive.calc
def csv_column_mismatch_calc() -> Optional[tuple[set, set]]:
public = public_csv_path()
@@ -223,7 +216,6 @@ def welcome_ui():
@render.ui
def csv_or_columns_ui():
return data_source.csv_or_columns_ui(
- in_cloud=in_cloud,
is_tutorial_mode=is_tutorial_mode,
csv_info=csv_info,
)
@@ -359,7 +351,7 @@ def set_is_dataset_selected():
and not info.get_is_error()
and len(info.get_all_column_names()) > 0
and not get_row_count_errors(max_rows())
- and (in_cloud or not csv_column_mismatch_calc())
+ and not csv_column_mismatch_calc()
)
@reactive.calc
@@ -388,26 +380,13 @@ def contributions_validation_ui():
@render.ui
def python_tutorial_ui():
- cloud_extra_markdown = (
- """
- Because this instance of DP Wizard is running in the cloud,
- we don't allow private data to be uploaded.
- When run locally, DP Wizard can also run an analysis
- on your data and return results,
- and not just an unexecuted notebook.
- """
- if in_cloud
- else ""
- )
return tutorial_box(
is_tutorial_mode(),
- f"""
+ """
Along the way, code samples demonstrate
how the information you provide is used in the
OpenDP Library, and at the end you can download
a notebook for the entire calculation.
-
- {cloud_extra_markdown}
""",
responsive=False,
)
@@ -465,7 +444,7 @@ def define_analysis_button_ui():
return [
button,
f"""
- Specify {'columns' if in_cloud else 'CSV'}, unit of privacy,
+ Specify CSV, unit of privacy,
and maximum row count before proceeding.
""",
]
diff --git a/dp_wizard/shiny/panels/dataset_panel/data_source.py b/dp_wizard/shiny/panels/dataset_panel/data_source.py
index 26a92462..fe9a7d9d 100644
--- a/dp_wizard/shiny/panels/dataset_panel/data_source.py
+++ b/dp_wizard/shiny/panels/dataset_panel/data_source.py
@@ -19,57 +19,21 @@
def csv_or_columns_ui(
- in_cloud: bool,
is_tutorial_mode: reactive.Value[bool],
csv_info: reactive.Value[CsvInfo],
): # pragma: no cover
- if in_cloud:
- content = [
- ui.markdown(
- """
- Provide the names of columns you'll use in your analysis,
- one per line, with a sample value for each. For example:
-
- ```
- name: Chuck
- age: 48
- ```
- """
- ),
- tutorial_box(
- is_tutorial_mode(),
- """
- When [installed and run
- locally](https://pypi.org/project/dp_wizard/),
- DP Wizard allows you to specify a private and public CSV,
- but for the safety of your data, in the cloud
- DP Wizard only accepts column names.
-
- If you don't have other ideas, we can imagine
- a CSV of student quiz grades: Enter `student_id`,
- `quiz_id`, `grade`, and `class_year_str` below,
- each on a separate line.
- """,
- responsive=False,
- ),
- ui.input_text_area("all_column_names", "CSV Column Names", rows=5),
- ]
- else:
- content = [
- ui.markdown(
- f"""
+ return [
+ ui.markdown(
+ f"""
Choose **Private CSV** {PRIVATE_TEXT}
Choose **Public CSV** {PUBLIC_TEXT}
Choose both **Private CSV** and **Public CSV** {PUBLIC_PRIVATE_TEXT}
- """
- ),
- ui.output_ui("input_files_ui"),
- ui.output_ui("csv_message_ui"),
- ]
-
- content += [
+ """
+ ),
+ ui.output_ui("input_files_ui"),
+ ui.output_ui("csv_message_ui"),
code_sample(
"Context",
Template(
@@ -98,7 +62,6 @@ def csv_or_columns_ui(
),
ui.output_ui("python_tutorial_ui"),
]
- return content
def input_files_ui(
diff --git a/dp_wizard/shiny/panels/results_panel/__init__.py b/dp_wizard/shiny/panels/results_panel/__init__.py
index 200e356a..171f3a91 100644
--- a/dp_wizard/shiny/panels/results_panel/__init__.py
+++ b/dp_wizard/shiny/panels/results_panel/__init__.py
@@ -99,7 +99,6 @@ def results_server(
): # pragma: no cover
# CLI options:
# is_sample_csv = state.is_sample_csv
- in_cloud = state.in_cloud
qa_mode = state.qa_mode
# Reactive bools:
@@ -209,47 +208,35 @@ def download_results_ui():
]
if product() == Product.SYNTHETIC_DATA:
downloads.append("Contingency Table")
- return (
- ui.markdown(
- """
- When [installed and run
- locally](https://pypi.org/project/dp_wizard/),
- there are more download options because DP Wizard
- can read your private CSV and release differentially
- private statistics.
+ return [
+ tutorial_box(
+ is_tutorial_mode(),
"""
- )
- if in_cloud
- else [
- tutorial_box(
- is_tutorial_mode(),
- """
Now you can download a notebook for your analysis.
The Jupyter notebook could be used locally or on Colab,
but the HTML version can be viewed in the brower.
""",
- responsive=False,
- ),
- download_button(
- "Package",
- primary=True,
- disabled=disabled,
- ),
- ui.br(),
- "Contains:",
- ui.tags.ul(
- *[
- ui.tags.li(
- download_link(
- download,
- disabled=disabled,
- )
+ responsive=False,
+ ),
+ download_button(
+ "Package",
+ primary=True,
+ disabled=disabled,
+ ),
+ ui.br(),
+ "Contains:",
+ ui.tags.ul(
+ *[
+ ui.tags.li(
+ download_link(
+ download,
+ disabled=disabled,
)
- for download in downloads
- ]
- ),
- ]
- )
+ )
+ for download in downloads
+ ]
+ ),
+ ]
@render.ui
def download_code_ui():
@@ -257,18 +244,11 @@ def download_code_ui():
return [
tutorial_box(
is_tutorial_mode(),
- (
- """
- In the cloud, DP Wizard only provides unexecuted
- notebooks and scripts.
- """
- if in_cloud
- else """
- Alternatively, you can download a script or unexecuted
- notebook that demonstrates the steps of your analysis,
- but does not contain any data or analysis results.
- """
- ),
+ """
+ Alternatively, you can download a script or unexecuted
+ notebook that demonstrates the steps of your analysis,
+ but does not contain any data or analysis results.
+ """,
responsive=False,
),
download_button("Notebook (unexecuted)", disabled=disabled),
diff --git a/dp_wizard/types.py b/dp_wizard/types.py
index 32e919b2..a1e1352a 100644
--- a/dp_wizard/types.py
+++ b/dp_wizard/types.py
@@ -212,7 +212,6 @@ def get_is_error(self) -> bool:
class AppState:
# CLI options:
is_sample_csv: bool
- in_cloud: bool
qa_mode: bool
# Reactive bools:
diff --git a/dp_wizard/utils/argparse_helpers.py b/dp_wizard/utils/argparse_helpers.py
index 67a7e775..647f1a85 100644
--- a/dp_wizard/utils/argparse_helpers.py
+++ b/dp_wizard/utils/argparse_helpers.py
@@ -20,8 +20,7 @@ def _get_arg_parser() -> argparse.ArgumentParser:
description="DP Wizard makes it easier to get started with "
"Differential Privacy.",
epilog=f"""
-Unless you have set "--sample" or "--cloud", you will specify a CSV
-inside the application.
+Unless you have set "--sample", you will provide a CSV inside the application.
Provide a "Private CSV" {PRIVATE_TEXT}
@@ -37,18 +36,13 @@ def _get_arg_parser() -> argparse.ArgumentParser:
help="Generate a sample CSV: "
"See how DP Wizard works without providing your own data",
)
- group.add_argument(
- "--cloud",
- action="store_true",
- help="Prompt for column names instead of CSV upload",
- )
return parser
def _get_args() -> argparse.Namespace:
"""
>>> _get_args()
- Namespace(sample=False, cloud=False)
+ Namespace(sample=False)
"""
arg_parser = _get_arg_parser()
@@ -65,15 +59,12 @@ def _get_args() -> argparse.Namespace:
class CLIInfo(NamedTuple):
is_sample_csv: bool
- is_cloud_mode: bool
is_qa_mode: bool
def get_is_tutorial_mode(self) -> bool:
- return self.is_sample_csv or self.is_cloud_mode # pragma: no cover
+ return self.is_sample_csv # pragma: no cover
def get_cli_info() -> CLIInfo: # pragma: no cover
args = _get_args()
- return CLIInfo(
- is_sample_csv=args.sample, is_cloud_mode=args.cloud, is_qa_mode=False
- )
+ return CLIInfo(is_sample_csv=args.sample, is_qa_mode=False)
diff --git a/tests/apps/app_cloud.py b/tests/apps/app_cloud.py
deleted file mode 100644
index b103258a..00000000
--- a/tests/apps/app_cloud.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from dp_wizard.shiny import make_app
-from dp_wizard.utils.argparse_helpers import CLIInfo
-
-app = make_app(
- CLIInfo(
- is_sample_csv=False,
- is_cloud_mode=True,
- is_qa_mode=False,
- )
-)
diff --git a/tests/apps/app_qa.py b/tests/apps/app_qa.py
index a6bcf864..6b92d134 100644
--- a/tests/apps/app_qa.py
+++ b/tests/apps/app_qa.py
@@ -4,7 +4,6 @@
app = make_app(
CLIInfo(
is_sample_csv=True,
- is_cloud_mode=False,
is_qa_mode=True,
)
)
diff --git a/tests/apps/app_sample.py b/tests/apps/app_sample.py
index dea73ec3..116402b3 100644
--- a/tests/apps/app_sample.py
+++ b/tests/apps/app_sample.py
@@ -4,7 +4,6 @@
app = make_app(
CLIInfo(
is_sample_csv=True,
- is_cloud_mode=False,
is_qa_mode=False,
)
)
diff --git a/tests/test_app.py b/tests/test_app.py
index 2b6bec7a..3209ffb0 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -23,48 +23,9 @@
test_apps = Path(__file__).parent / "apps"
sample_app = create_app_fixture(test_apps / "app_sample.py")
-cloud_app = create_app_fixture(test_apps / "app_cloud.py")
qa_app = create_app_fixture(test_apps / "app_qa.py")
-def test_cloud_app(page: Page, cloud_app: ShinyAppProc): # pragma: no cover
- page.goto(cloud_app.url)
-
- page.locator("#max_rows").fill("10000")
- expect(page).to_have_title("DP Wizard")
- expect(page.get_by_text("Choose Public CSV")).not_to_be_visible()
- page.get_by_label("CSV Column Names").fill("a_column:1\nb_column:2")
-
- page.get_by_role("button", name="Define Analysis").click()
- page.locator(".selectize-input").nth(0).click()
- page.get_by_text("1: a_column").click()
- page.get_by_label("Lower").fill("0")
- page.get_by_label("Upper").fill("10")
-
- expect(
- page.get_by_text("Select one or more columns before proceeding.")
- ).not_to_be_visible()
- page.locator(".selectize-input").nth(0).click()
- page.get_by_text("2: b_column").click()
- page.get_by_text("Select one or more columns before proceeding.")
- page.get_by_text("2: b_column×").click()
-
- page.get_by_role("button", name="Download Results").click()
- with page.expect_download() as download_info:
- page.get_by_role("link", name="Notebook (unexecuted").click()
-
- download_path = download_info.value.path()
-
- # Try to execute the downloaded file:
- # Based on https://nbconvert.readthedocs.io/en/latest/execute_api.html#example
- nb = nbformat.read(download_path.open(), as_version=4)
- ep = ExecutePreprocessor(timeout=600, kernel_name="python3")
- ep.preprocess(nb)
-
- # Clean up file in CWD that is created by notebook execution.
- Path(PLACEHOLDER_CSV_NAME).unlink()
-
-
def test_qa_app(page: Page, qa_app: ShinyAppProc): # pragma: no cover
page.goto(qa_app.url)
|