Skip to content

Deploy to shinyapps #411

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

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
run: playwright install chromium

- name: Test
run: ./ci.sh
run: scripts/ci.sh

- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
Expand Down
16 changes: 8 additions & 8 deletions README-PYPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Building on what we've learned from [DP Creator](https://github.com/opendp/dpcre
- Interactive visualization of privacy budget choices
- UI development in Python with [Shiny](https://shiny.posit.co/py/)

DP Wizard guides the user through the application of differential privacy.
After selecting a local CSV, users are prompted to describe the analysis they need.
Output options include:
You can run DP Wizard locally and upload your own CSV,
or use the [cloud deployment](https://01966942-7eab-da99-0887-a7c483756aa8.share.connect.posit.cloud/) and only provide column names to protect your private data.
In either case, you'll be prompted to describe the analysis you need, and then be able to download outputs including:

- A Jupyter notebook which demonstrates how to use [OpenDP](https://docs.opendp.org/).
- A plain Python script.
Expand All @@ -25,16 +25,16 @@ You can check your current version with `python --version`.
The exact upgrade process will depend on your environment and operating system.

```
usage: dp-wizard [-h] [--demo | --no_uploads]
usage: dp-wizard [-h] [--demo | --cloud]

DP Wizard makes it easier to get started with Differential Privacy.

options:
-h, --help show this help message and exit
--demo Use generated fake CSV for a quick demo
--no_uploads Prompt for column names instead of CSV upload
-h, --help show this help message and exit
--demo Use generated fake CSV for a quick demo
--cloud Prompt for column names instead of CSV upload

Unless you have set "--demo" or "--no_uploads", you will specify a CSV
Unless you have set "--demo" or "--cloud", you will specify a CSV
inside the application.

Provide a "Public CSV" if you have a public data set, and are curious how
Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Building on what we've learned from [DP Creator](https://github.com/opendp/dpcre
- Interactive visualization of privacy budget choices
- UI development in Python with [Shiny](https://shiny.posit.co/py/)

DP Wizard guides the user through the application of differential privacy.
After selecting a local CSV, users are prompted to describe the analysis they need.
Output options include:
You can run DP Wizard locally and upload your own CSV,
or use the [cloud deployment](https://01966942-7eab-da99-0887-a7c483756aa8.share.connect.posit.cloud/) and only provide column names to protect your private data.
In either case, you'll be prompted to describe the analysis you need, and then be able to download outputs including:

- A Jupyter notebook which demonstrates how to use [OpenDP](https://docs.opendp.org/).
- A plain Python script.
Expand All @@ -25,16 +25,16 @@ You can check your current version with `python --version`.
The exact upgrade process will depend on your environment and operating system.

```
usage: dp-wizard [-h] [--demo | --no_uploads]
usage: dp-wizard [-h] [--demo | --cloud]

DP Wizard makes it easier to get started with Differential Privacy.

options:
-h, --help show this help message and exit
--demo Use generated fake CSV for a quick demo
--no_uploads Prompt for column names instead of CSV upload
-h, --help show this help message and exit
--demo Use generated fake CSV for a quick demo
--cloud Prompt for column names instead of CSV upload

Unless you have set "--demo" or "--no_uploads", you will specify a CSV
Unless you have set "--demo" or "--cloud", you will specify a CSV
inside the application.

Provide a "Public CSV" if you have a public data set, and are curious how
Expand Down Expand Up @@ -94,7 +94,7 @@ Your browser should open and connect you to the application.

Tests should pass, and code coverage should be complete (except blocks we explicitly ignore):
```shell
$ ./ci.sh
$ scripts/ci.sh
```

We're using [Playwright](https://playwright.dev/python/) for end-to-end tests. You can use it to [generate test code](https://playwright.dev/python/docs/codegen-intro) just by interacting with the app in a browser:
Expand All @@ -118,7 +118,7 @@ If Playwright fails in CI, we can still see what went wrong:

- Make sure you're up to date, and have the git-ignored credentials file `.pypirc`.
- Make one last feature branch:
- Run `changelog.py` to update the `CHANGELOG.md`.
- Run `scripts/changelog.py` to update the `CHANGELOG.md`.
- Then bump `dp_wizard/VERSION`, and add the new number at the top of the `CHANGELOG.md`.
- Push to github; open PR, with version number in name; merge PR.
- `flit publish --pypirc .pypirc`
Expand All @@ -127,11 +127,14 @@ This project is configured so there are two different install possibilities from
- `pip install dp_wizard` does not aggressively pin dependency versions. It is preferred if you're using `dp_wizard` as a library.
- `pip install dp_wizard[app]` pins all dependencies, and will work more reliably for application users.

The cloud deployment is [configured](https://connect.posit.cloud/mccalluc/content/01966942-7eab-da99-0887-a7c483756aa8/edit) to update on pushes to the `cloud-deployment` branch.
If you are on `main`, with no local changes, run `script/deploy.sh`.

### Conventions

Branch names should be of the form `NNNN-short-description`, where `NNNN` is the issue number being addressed.

Add developer-only dependencies in `requirements-dev.in`; Add other dependencies in `requirements.in`. After an edit to either file run `dependencies.py` to install the new dependency locally and update `pyproject.toml`.
Add developer-only dependencies in `requirements-dev.in`; Add other dependencies in `requirements.in`. After an edit to either file run `scripts/requirements.py` to install the new dependency locally and update `pyproject.toml`.

A Github [project board](https://github.com/orgs/opendp/projects/10/views/2) provides an overview of the issues and PRs.
When PRs are [Ready for Review](https://github.com/orgs/opendp/projects/10/views/2?filterQuery=status%3A%22Ready+for+Review%22) they should be flagged as such so reviewers can find them.
Expand Down
2 changes: 1 addition & 1 deletion dp_wizard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def main(): # pragma: no cover
not_first_run_path.touch()

shiny.run_app(
app="dp_wizard.app",
app="dp_wizard.app_local",
launch_browser=True,
reload=True,
)
12 changes: 12 additions & 0 deletions dp_wizard/app_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from shiny import App

from dp_wizard.shiny import app_ui, make_server_from_cli_info
from dp_wizard.utils.argparse_helpers import CLIInfo


app = App(
app_ui,
make_server_from_cli_info(
CLIInfo(is_demo=False, in_cloud=True),
),
)
12 changes: 12 additions & 0 deletions dp_wizard/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from shiny import App

from dp_wizard.shiny import app_ui, make_server_from_cli_info
from dp_wizard.utils.argparse_helpers import CLIInfo


app = App(
app_ui,
make_server_from_cli_info(
CLIInfo(is_demo=True, in_cloud=False),
),
)
10 changes: 10 additions & 0 deletions dp_wizard/local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from shiny import App

from dp_wizard.shiny import app_ui, make_server_from_cli_info
from dp_wizard.utils.argparse_helpers import get_cli_info


app = App(
app_ui,
make_server_from_cli_info(get_cli_info()),
)
21 changes: 21 additions & 0 deletions dp_wizard/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# After making changes here, run scripts/requirements.py.

# OpenDP:
opendp[polars]==0.13.0

# Conversion:
jupytext
jupyter-client
pyyaml
nbconvert
ipykernel
black
# May also require:
# python -m ipykernel install --name kernel_name --user

# Shiny:
shiny
faicons

# Visualization:
matplotlib
13 changes: 5 additions & 8 deletions dp_wizard/app/__init__.py → dp_wizard/shiny/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import csv
import random

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

from dp_wizard.utils.argparse_helpers import get_cli_info, CLIInfo
from dp_wizard.utils.argparse_helpers import CLIInfo
from dp_wizard.utils.csv_helper import read_csv_names
from dp_wizard.app import (
from dp_wizard.shiny import (
about_panel,
analysis_panel,
dataset_panel,
Expand Down Expand Up @@ -122,7 +122,7 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
output,
session,
is_demo=cli_info.is_demo,
no_uploads=cli_info.no_uploads,
in_cloud=cli_info.in_cloud,
initial_public_csv_path="",
initial_private_csv_path=str(initial_private_csv_path),
public_csv_path=public_csv_path,
Expand Down Expand Up @@ -150,7 +150,7 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
input,
output,
session,
no_uploads=cli_info.no_uploads,
in_cloud=cli_info.in_cloud,
public_csv_path=public_csv_path,
private_csv_path=private_csv_path,
contributions=contributions,
Expand All @@ -170,6 +170,3 @@ def server(input: Inputs, output: Outputs, session: Session): # pragma: no cove
session.on_ended(ctrl_c_reminder)

return server


app = App(app_ui, make_server_from_cli_info(get_cli_info()))
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from htmltools import tags
from shiny import ui, reactive, Inputs, Outputs, Session

from dp_wizard.app.components.outputs import nav_button
from dp_wizard.shiny.components.outputs import nav_button


def _run(cmd):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from htmltools import tags
from shiny import ui, reactive, render, Inputs, Outputs, Session

from dp_wizard.app.components.inputs import log_slider
from dp_wizard.app.components.column_module import column_ui, column_server
from dp_wizard.shiny.components.inputs import log_slider
from dp_wizard.shiny.components.column_module import column_ui, column_server
from dp_wizard.utils.csv_helper import (
id_names_dict_from_names,
id_labels_dict_from_names,
get_csv_row_count,
)
from dp_wizard.app.components.outputs import (
from dp_wizard.shiny.components.outputs import (
output_code_sample,
demo_tooltip,
nav_button,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from dp_wizard.utils.dp_helper import make_accuracy_histogram
from dp_wizard.utils.shared import plot_bars
from dp_wizard.utils.code_generators import make_column_config_block
from dp_wizard.app.components.outputs import (
from dp_wizard.shiny.components.outputs import (
output_code_sample,
demo_tooltip,
info_md_box,
Expand Down
File renamed without changes.
53 changes: 35 additions & 18 deletions dp_wizard/app/dataset_panel.py → dp_wizard/shiny/dataset_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
PUBLIC_PRIVATE_TEXT,
)
from dp_wizard.utils.csv_helper import get_csv_names_mismatch
from dp_wizard.app.components.outputs import (
from dp_wizard.shiny.components.outputs import (
output_code_sample,
demo_tooltip,
hide_if,
Expand Down Expand Up @@ -50,7 +50,7 @@ def dataset_server(
output: Outputs,
session: Session,
is_demo: bool,
no_uploads: bool,
in_cloud: bool,
initial_public_csv_path: str,
initial_private_csv_path: str,
public_csv_path: reactive.Value[str],
Expand Down Expand Up @@ -96,22 +96,39 @@ def csv_column_mismatch_calc() -> Optional[tuple[set, set]]:

@render.ui
def csv_or_columns_ui():
if no_uploads:
return ui.card(
ui.card_header("CSV Columns"),
ui.markdown(
"""
When run locally, DP Wizard allows you to specify a private CSV,
but for the safety of your data, in the cloud DP Wizard only
accepts column names. After defining your analysis,
you can download a notebook to run locally.
if in_cloud:
return [
ui.card(
ui.card_header("Welcome!"),
ui.markdown(
"""
# DP Wizard, from OpenDP

DP Wizard makes it easier to get started with
differential privacy: You configure a basic analysis
interactively, and then download code which
demonstrates how to use the
[OpenDP library](https://docs.opendp.org/).

Provide the names of columns you'll use in your analysis,
one per line, with no extra punctuation.
"""
When [installed and run
locally](https://pypi.org/project/dp_wizard/),
DP Wizard allows you to specify a private CSV,
but for the safety of your data, in the cloud
DP Wizard only accepts column names.
"""
),
),
ui.input_text_area("column_names", "CSV Column Names", rows=5),
)
ui.card(
ui.card_header("CSV Columns"),
ui.markdown(
"""
Provide the names of columns you'll use in your analysis,
one per line, with no extra punctuation.
"""
),
ui.input_text_area("column_names", "CSV Column Names", rows=5),
),
]
return (
ui.card(
ui.card_header("Input CSVs"),
Expand Down Expand Up @@ -207,7 +224,7 @@ def button_enabled():
return (
contributions_valid()
and len(column_names()) > 0
and (no_uploads or not csv_column_mismatch_calc())
and (in_cloud or not csv_column_mismatch_calc())
)

@reactive.calc
Expand Down Expand Up @@ -242,7 +259,7 @@ def define_analysis_button_ui():
button,
(
"Specify columns and the unit of privacy before proceeding."
if no_uploads
if in_cloud
else "Specify CSV and the unit of privacy before proceeding."
),
]
Expand Down
File renamed without changes.
Loading
Loading