diff --git a/dp_wizard/shiny/panels/results_panel/__init__.py b/dp_wizard/shiny/panels/results_panel/__init__.py index 54671326..716c6542 100644 --- a/dp_wizard/shiny/panels/results_panel/__init__.py +++ b/dp_wizard/shiny/panels/results_panel/__init__.py @@ -318,6 +318,7 @@ def package_zip(): # from a clean slate, rather than rely on the side effect # of a reactive.calc. (zip_root_dir / f"{stem}.txt").write_text(report_txt()) + (zip_root_dir / f"{stem}.txt").write_text(report_html()) (zip_root_dir / f"{stem}.csv").write_text(table_csv()) base_name = f"{tmp_dir}/{stem}" @@ -392,6 +393,11 @@ def report_txt(): notebook_nb() # Evaluate just for the side effect of creating report. return (_target_path / "report.txt").read_text() + @reactive.calc + def report_html(): + notebook_nb() # Evaluate just for the side effect of creating report. + return (_target_path / "report.html").read_text() + @reactive.calc def table_csv(): notebook_nb() # Evaluate just for the side effect of creating report. diff --git a/dp_wizard/utils/code_generators/no-tests/_stats_reports.py b/dp_wizard/utils/code_generators/no-tests/_stats_reports.py index 8c2e8947..ce039563 100644 --- a/dp_wizard/utils/code_generators/no-tests/_stats_reports.py +++ b/dp_wizard/utils/code_generators/no-tests/_stats_reports.py @@ -1,4 +1,5 @@ import csv +import re from pathlib import Path from yaml import dump @@ -45,10 +46,30 @@ def flatten_dict(dictionary, parent_key=""): "outputs": OUTPUTS, } -Path(TXT_REPORT_PATH).write_text(dump(report)) +target_path = Path(TARGET_PATH) +(target_path / "report.txt").write_text(dump(report)) flat_report = flatten_dict(report) -with Path(CSV_REPORT_PATH).open(mode="w", newline="") as handle: +with (target_path / "report.csv").open(mode="w", newline="") as handle: writer = csv.writer(handle) for kv_pair in flat_report.items(): writer.writerow(kv_pair) + + +def png_name(name): + return re.sub(r"\W+", "-", name) + ".png" + + +for name, figure in figures.items(): + figure.savefig(target_path / png_name(name)) + + +imgs = [f"{name}" for name in figures.keys()] +html = f""" + + +Figures: +{"\n".join(imgs)} + +""" +(target_path / "report.html").write_text(html) diff --git a/dp_wizard/utils/code_generators/no-tests/_synth_reports.py b/dp_wizard/utils/code_generators/no-tests/_synth_reports.py index 006fe342..984da702 100644 --- a/dp_wizard/utils/code_generators/no-tests/_synth_reports.py +++ b/dp_wizard/utils/code_generators/no-tests/_synth_reports.py @@ -13,6 +13,9 @@ "outputs": OUTPUTS, } -Path(TXT_REPORT_PATH).write_text(dump(report)) +target_path = Path(TARGET_PATH) +(target_path / "report.txt").write_text(dump(report)) -synthetic_data.write_csv(CSV_REPORT_PATH) +synthetic_data.write_csv(target_path / "report.csv") + +# TODO: Use figures diff --git a/dp_wizard/utils/code_generators/notebook_generator.py b/dp_wizard/utils/code_generators/notebook_generator.py index ab63e3f3..6b6b7962 100644 --- a/dp_wizard/utils/code_generators/notebook_generator.py +++ b/dp_wizard/utils/code_generators/notebook_generator.py @@ -91,8 +91,10 @@ def template(synthetic_data): .fill_values( CSV_PATH=self.analysis_plan.get_absolute_csv_path(), EPSILON=self.analysis_plan.epsilon, - TXT_REPORT_PATH=str(target_path / "report.txt"), - CSV_REPORT_PATH=str(target_path / "report.csv"), + TARGET_PATH=target_path, + # TXT_REPORT_PATH=str(target_path / "report.txt"), + # CSV_REPORT_PATH=str(target_path / "report.csv"), + # FIGURES_PATH=str(target_path), ) .finish() ) diff --git a/dp_wizard/utils/shared.py b/dp_wizard/utils/shared.py index ea83f42c..deeafd16 100644 --- a/dp_wizard/utils/shared.py +++ b/dp_wizard/utils/shared.py @@ -63,6 +63,9 @@ def df_to_columns(df: DataFrame): return transposed if transposed else (tuple(), tuple()) +figures = {} + + def plot_bars( df: DataFrame, error: float, cutoff: float, title: str ): # pragma: no cover @@ -75,7 +78,8 @@ def plot_bars( plt.rcParams["figure.figsize"] = (12, 4) bins, values = df_to_columns(df) - _figure, axes = plt.subplots() + figure, axes = plt.subplots() + figures[title] = figure # pyright: ignore[reportUndefinedVariable] bar_colors = ["blue" if v > cutoff else "lightblue" for v in values] axes.bar(bins, values, color=bar_colors, yerr=error) axes.set_xticks(bins, bins, rotation=45)