From 6874656a69c66d92e245d0f57418c03408583f51 Mon Sep 17 00:00:00 2001 From: Chuck McCallum Date: Thu, 26 Sep 2024 14:44:31 -0400 Subject: [PATCH 1/4] make an app subdirectory --- .coveragerc | 2 +- dp_creator_ii/__init__.py | 2 +- dp_creator_ii/{app.py => app/__init__.py} | 0 dp_creator_ii/tests/test_app.py | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename dp_creator_ii/{app.py => app/__init__.py} (100%) diff --git a/.coveragerc b/.coveragerc index 1f2be51..3342379 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,7 +4,7 @@ source = . omit = # TODO - app.py + dp_creator_ii/app/* # More strict: Check transitions between lines, not just individual lines. # TODO: branch = True diff --git a/dp_creator_ii/__init__.py b/dp_creator_ii/__init__.py index be09255..0a084e7 100644 --- a/dp_creator_ii/__init__.py +++ b/dp_creator_ii/__init__.py @@ -42,7 +42,7 @@ def main(): # pragma: no cover # Just setting variables in a plain python module doesn't work: # The new thread started for the server doesn't see changes. - Path("config.json").write_text( + Path("app/config.json").write_text( json.dumps( { "csv_path": str(args.csv_path), diff --git a/dp_creator_ii/app.py b/dp_creator_ii/app/__init__.py similarity index 100% rename from dp_creator_ii/app.py rename to dp_creator_ii/app/__init__.py diff --git a/dp_creator_ii/tests/test_app.py b/dp_creator_ii/tests/test_app.py index ec1ea58..684af42 100644 --- a/dp_creator_ii/tests/test_app.py +++ b/dp_creator_ii/tests/test_app.py @@ -3,7 +3,7 @@ from shiny.pytest import create_app_fixture -app = create_app_fixture("../app.py") +app = create_app_fixture("../app/__init__.py") # TODO: Why is incomplete coverage reported here? From d1c2456e62e94c4b6284a60a7e9c6ee8f7a9e005 Mon Sep 17 00:00:00 2001 From: Chuck McCallum Date: Thu, 26 Sep 2024 15:00:58 -0400 Subject: [PATCH 2/4] move UI to three separate files --- dp_creator_ii/app/__init__.py | 42 +++-------------------------- dp_creator_ii/app/analysis_panel.py | 10 +++++++ dp_creator_ii/app/dataset_panel.py | 12 +++++++++ dp_creator_ii/app/results_panel.py | 17 ++++++++++++ 4 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 dp_creator_ii/app/analysis_panel.py create mode 100644 dp_creator_ii/app/dataset_panel.py create mode 100644 dp_creator_ii/app/results_panel.py diff --git a/dp_creator_ii/app/__init__.py b/dp_creator_ii/app/__init__.py index e3bb1ec..ece1e8c 100644 --- a/dp_creator_ii/app/__init__.py +++ b/dp_creator_ii/app/__init__.py @@ -6,48 +6,14 @@ from dp_creator_ii.template import make_notebook_py, make_script_py from dp_creator_ii.converters import convert_py_to_nb - -def dataset_panel(): - return ui.nav_panel( - "Select Dataset", - "TODO: Pick dataset", - ui.output_text("csv_path_text"), - ui.output_text("unit_of_privacy_text"), - ui.input_action_button("go_to_analysis", "Perform analysis"), - value="dataset_panel", - ) - - -def analysis_panel(): - return ui.nav_panel( - "Perform Analysis", - "TODO: Define analysis", - ui.input_action_button("go_to_results", "Download results"), - value="analysis_panel", - ) - - -def results_panel(): - return ui.nav_panel( - "Download Results", - "TODO: Download Results", - ui.download_button("download_script", "Download script"), - # TODO: Notebook code is badly formatted - # ui.download_button( - # "download_notebook_unexecuted", "Download notebook (unexecuted)" - # ), - # ui.download_button( - # "download_notebook_executed", "Download notebook (executed)" - # ) - value="results_panel", - ) +from dp_creator_ii.app import analysis_panel, dataset_panel, results_panel app_ui = ui.page_bootstrap( ui.navset_tab( - dataset_panel(), - analysis_panel(), - results_panel(), + dataset_panel.dataset_ui(), + analysis_panel.analysis_ui(), + results_panel.results_ui(), id="top_level_nav", ), title="DP Creator II", diff --git a/dp_creator_ii/app/analysis_panel.py b/dp_creator_ii/app/analysis_panel.py new file mode 100644 index 0000000..0406e79 --- /dev/null +++ b/dp_creator_ii/app/analysis_panel.py @@ -0,0 +1,10 @@ +from shiny import App, ui, reactive, render + + +def analysis_ui(): + return ui.nav_panel( + "Perform Analysis", + "TODO: Define analysis", + ui.input_action_button("go_to_results", "Download results"), + value="analysis_panel", + ) diff --git a/dp_creator_ii/app/dataset_panel.py b/dp_creator_ii/app/dataset_panel.py new file mode 100644 index 0000000..a6360c5 --- /dev/null +++ b/dp_creator_ii/app/dataset_panel.py @@ -0,0 +1,12 @@ +from shiny import App, ui, module, reactive, render + + +def dataset_ui(): + return ui.nav_panel( + "Select Dataset", + "TODO: Pick dataset", + ui.output_text("csv_path_text"), + ui.output_text("unit_of_privacy_text"), + ui.input_action_button("go_to_analysis", "Perform analysis"), + value="dataset_panel", + ) diff --git a/dp_creator_ii/app/results_panel.py b/dp_creator_ii/app/results_panel.py new file mode 100644 index 0000000..eae1536 --- /dev/null +++ b/dp_creator_ii/app/results_panel.py @@ -0,0 +1,17 @@ +from shiny import App, ui, reactive, render + + +def results_ui(): + return ui.nav_panel( + "Download Results", + "TODO: Download Results", + ui.download_button("download_script", "Download script"), + # TODO: Notebook code is badly formatted + # ui.download_button( + # "download_notebook_unexecuted", "Download notebook (unexecuted)" + # ), + # ui.download_button( + # "download_notebook_executed", "Download notebook (executed)" + # ) + value="results_panel", + ) From 95c745cd5877b9aab4f093d266fd469356b54a85 Mon Sep 17 00:00:00 2001 From: Chuck McCallum Date: Thu, 26 Sep 2024 15:02:33 -0400 Subject: [PATCH 3/4] move UI to three separate files --- dp_creator_ii/app/analysis_panel.py | 2 +- dp_creator_ii/app/dataset_panel.py | 2 +- dp_creator_ii/app/results_panel.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dp_creator_ii/app/analysis_panel.py b/dp_creator_ii/app/analysis_panel.py index 0406e79..08f278f 100644 --- a/dp_creator_ii/app/analysis_panel.py +++ b/dp_creator_ii/app/analysis_panel.py @@ -1,4 +1,4 @@ -from shiny import App, ui, reactive, render +from shiny import ui def analysis_ui(): diff --git a/dp_creator_ii/app/dataset_panel.py b/dp_creator_ii/app/dataset_panel.py index a6360c5..e53ed2b 100644 --- a/dp_creator_ii/app/dataset_panel.py +++ b/dp_creator_ii/app/dataset_panel.py @@ -1,4 +1,4 @@ -from shiny import App, ui, module, reactive, render +from shiny import ui def dataset_ui(): diff --git a/dp_creator_ii/app/results_panel.py b/dp_creator_ii/app/results_panel.py index eae1536..c2eb1a2 100644 --- a/dp_creator_ii/app/results_panel.py +++ b/dp_creator_ii/app/results_panel.py @@ -1,4 +1,4 @@ -from shiny import App, ui, reactive, render +from shiny import ui def results_ui(): From 1b4f9fcef3bf6d3552157675b890d1ca556d6607 Mon Sep 17 00:00:00 2001 From: Chuck McCallum Date: Thu, 26 Sep 2024 15:44:33 -0400 Subject: [PATCH 4/4] split server into functions --- dp_creator_ii/app/__init__.py | 75 ++--------------------------- dp_creator_ii/app/analysis_panel.py | 9 +++- dp_creator_ii/app/dataset_panel.py | 26 +++++++++- dp_creator_ii/app/results_panel.py | 47 +++++++++++++++++- 4 files changed, 83 insertions(+), 74 deletions(-) diff --git a/dp_creator_ii/app/__init__.py b/dp_creator_ii/app/__init__.py index ece1e8c..f636d97 100644 --- a/dp_creator_ii/app/__init__.py +++ b/dp_creator_ii/app/__init__.py @@ -1,10 +1,4 @@ -import json -from pathlib import Path - -from shiny import App, ui, reactive, render - -from dp_creator_ii.template import make_notebook_py, make_script_py -from dp_creator_ii.converters import convert_py_to_nb +from shiny import App, ui from dp_creator_ii.app import analysis_panel, dataset_panel, results_panel @@ -21,70 +15,9 @@ def server(input, output, session): - config_path = Path(__file__).parent / "config.json" - config = json.loads(config_path.read_text()) - config_path.unlink() - - csv_path = reactive.value(config["csv_path"]) - unit_of_privacy = reactive.value(config["unit_of_privacy"]) - - @render.text - def csv_path_text(): - return str(csv_path.get()) - - @render.text - def unit_of_privacy_text(): - return str(unit_of_privacy.get()) - - @reactive.effect - @reactive.event(input.go_to_analysis) - def go_to_analysis(): - ui.update_navs("top_level_nav", selected="analysis_panel") - - @reactive.effect - @reactive.event(input.go_to_results) - def go_to_results(): - ui.update_navs("top_level_nav", selected="results_panel") - - @render.download( - filename="dp-creator-script.py", - media_type="text/x-python", - ) - async def download_script(): - script_py = make_script_py( - unit=1, - loss=1, - weights=[1], - ) - yield script_py - - @render.download( - filename="dp-creator-notebook.ipynb", - media_type="application/x-ipynb+json", - ) - async def download_notebook_unexecuted(): - notebook_py = make_notebook_py( - csv_path="todo.csv", - unit=1, - loss=1, - weights=[1], - ) - notebook_nb = convert_py_to_nb(notebook_py) - yield notebook_nb - - @render.download( - filename="dp-creator-notebook-executed.ipynb", - media_type="application/x-ipynb+json", - ) - async def download_notebook_executed(): - notebook_py = make_notebook_py( - csv_path="todo.csv", - unit=1, - loss=1, - weights=[1], - ) - notebook_nb = convert_py_to_nb(notebook_py, execute=True) - yield notebook_nb + dataset_panel.dataset_server(input, output, session) + analysis_panel.analysis_server(input, output, session) + results_panel.results_server(input, output, session) app = App(app_ui, server) diff --git a/dp_creator_ii/app/analysis_panel.py b/dp_creator_ii/app/analysis_panel.py index 08f278f..ff89a81 100644 --- a/dp_creator_ii/app/analysis_panel.py +++ b/dp_creator_ii/app/analysis_panel.py @@ -1,4 +1,4 @@ -from shiny import ui +from shiny import ui, reactive def analysis_ui(): @@ -8,3 +8,10 @@ def analysis_ui(): ui.input_action_button("go_to_results", "Download results"), value="analysis_panel", ) + + +def analysis_server(input, output, session): + @reactive.effect + @reactive.event(input.go_to_results) + def go_to_results(): + ui.update_navs("top_level_nav", selected="results_panel") diff --git a/dp_creator_ii/app/dataset_panel.py b/dp_creator_ii/app/dataset_panel.py index e53ed2b..8e83995 100644 --- a/dp_creator_ii/app/dataset_panel.py +++ b/dp_creator_ii/app/dataset_panel.py @@ -1,4 +1,6 @@ -from shiny import ui +from pathlib import Path +import json +from shiny import ui, reactive, render def dataset_ui(): @@ -10,3 +12,25 @@ def dataset_ui(): ui.input_action_button("go_to_analysis", "Perform analysis"), value="dataset_panel", ) + + +def dataset_server(input, output, session): + config_path = Path(__file__).parent / "config.json" + config = json.loads(config_path.read_text()) + config_path.unlink() + + csv_path = reactive.value(config["csv_path"]) + unit_of_privacy = reactive.value(config["unit_of_privacy"]) + + @render.text + def csv_path_text(): + return str(csv_path.get()) + + @render.text + def unit_of_privacy_text(): + return str(unit_of_privacy.get()) + + @reactive.effect + @reactive.event(input.go_to_analysis) + def go_to_analysis(): + ui.update_navs("top_level_nav", selected="analysis_panel") diff --git a/dp_creator_ii/app/results_panel.py b/dp_creator_ii/app/results_panel.py index c2eb1a2..aa72abf 100644 --- a/dp_creator_ii/app/results_panel.py +++ b/dp_creator_ii/app/results_panel.py @@ -1,4 +1,7 @@ -from shiny import ui +from shiny import ui, render + +from dp_creator_ii.template import make_notebook_py, make_script_py +from dp_creator_ii.converters import convert_py_to_nb def results_ui(): @@ -15,3 +18,45 @@ def results_ui(): # ) value="results_panel", ) + + +def results_server(input, output, session): + @render.download( + filename="dp-creator-script.py", + media_type="text/x-python", + ) + async def download_script(): + script_py = make_script_py( + unit=1, + loss=1, + weights=[1], + ) + yield script_py + + @render.download( + filename="dp-creator-notebook.ipynb", + media_type="application/x-ipynb+json", + ) + async def download_notebook_unexecuted(): + notebook_py = make_notebook_py( + csv_path="todo.csv", + unit=1, + loss=1, + weights=[1], + ) + notebook_nb = convert_py_to_nb(notebook_py) + yield notebook_nb + + @render.download( + filename="dp-creator-notebook-executed.ipynb", + media_type="application/x-ipynb+json", + ) + async def download_notebook_executed(): + notebook_py = make_notebook_py( + csv_path="todo.csv", + unit=1, + loss=1, + weights=[1], + ) + notebook_nb = convert_py_to_nb(notebook_py, execute=True) + yield notebook_nb