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

CSV report #182

Merged
merged 8 commits into from
Dec 2, 2024
Merged
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
51 changes: 40 additions & 11 deletions dp_wizard/app/results_panel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from pathlib import Path

from shiny import ui, render, reactive, Inputs, Outputs, Session
from faicons import icon_svg
from htmltools.tags import table, tr, td

from dp_wizard.utils.code_generators import (
NotebookGenerator,
Expand All @@ -14,21 +16,35 @@
wait_message = "Please wait."


def td_button(name: str, ext: str, icon: str):
function_name = f'download_{name.lower().replace(" ", "_")}'
return (
td(
ui.download_button(
function_name,
[
icon_svg(icon, margin_right="0.5em"),
f"Download {name} ({ext})",
],
width="20em",
)
),
)


def results_ui():
return ui.nav_panel(
"Download results",
ui.markdown("You can now make a differentially private release of your data."),
ui.download_button(
"download_report",
"Download Report (.txt)",
),
ui.download_button(
"download_script",
"Download Script (.py)",
),
ui.download_button(
"download_notebook",
"Download Notebook (.ipynb)",
table(
tr(
td_button("Notebook", ".ipynb", "book"),
td_button("Script", ".py", "python"),
),
tr(
td_button("Report", ".txt", "file-lines"),
td_button("Table", ".csv", "file-csv"),
),
),
value="results_panel",
)
Expand Down Expand Up @@ -106,3 +122,16 @@ async def download_report():
Path(__file__).parent.parent / "tmp" / "report.txt"
).read_text()
yield report_txt

@render.download(
filename="dp-wizard-report.csv",
media_type="text/plain",
)
async def download_table():
with ui.Progress() as progress:
progress.set(message=wait_message)
notebook_nb() # Evaluate just for the side effect of creating report.
report_csv = (
Path(__file__).parent.parent / "tmp" / "report.csv"
).read_text()
yield report_csv
1 change: 1 addition & 0 deletions dp_wizard/tmp/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
demo.csv
report.txt
report.csv
15 changes: 7 additions & 8 deletions dp_wizard/utils/code_generators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,34 @@ def _make_context(self):
return str(self._make_partial_context().fill_values(CSV_PATH=self.csv_path))

def _make_extra_blocks(self):
identifiers = [name_to_identifier(name) for name in self.columns.keys()]
outputs_expression = (
"{"
+ ",".join(
str(
Template("report_kv")
.fill_values(
IDENTIFIER=id,
NAME=name,
CONFIDENCE=confidence,
)
.fill_expressions(
IDENTIFIER_HISTOGRAM=f"{id}_histogram",
IDENTIFIER_ACCURACY=f"{id}_accuracy",
IDENTIFIER_HISTOGRAM=f"{name_to_identifier(name)}_histogram",
IDENTIFIER_ACCURACY=f"{name_to_identifier(name)}_accuracy",
)
)
for id in identifiers
for name in self.columns.keys()
)
+ "}"
)
tmp_path = Path(__file__).parent.parent.parent / "tmp"
reports_block = str(
Template("reports")
.fill_expressions(
OUTPUTS=outputs_expression,
)
.fill_values(
CSV_PATH=self.csv_path,
REPORT_PATH=str(
Path(__file__).parent.parent.parent / "tmp" / "report.txt"
),
TXT_REPORT_PATH=str(tmp_path / "report.txt"),
CSV_REPORT_PATH=str(tmp_path / "report.csv"),
)
)
return {"REPORTS_BLOCK": reports_block}
Expand Down
2 changes: 1 addition & 1 deletion dp_wizard/utils/code_generators/no-tests/_report_kv.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
IDENTIFIER: {
NAME: {
"histogram": dict(zip(*df_to_columns(IDENTIFIER_HISTOGRAM))),
"accuracy": IDENTIFIER_ACCURACY,
"confidence": CONFIDENCE,
Expand Down
41 changes: 40 additions & 1 deletion dp_wizard/utils/code_generators/no-tests/_reports.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
from yaml import dump
from pathlib import Path
import csv


# https://stackoverflow.com/a/6027615/10727889
def flatten_dict(dictionary, parent_key=""):
"""
Walk tree to return flat dictionary.
>>> from pprint import pp
>>> pp(flatten_dict({
... "inputs": {
... "data": "fake.csv"
... },
... "outputs": {
... "a column": {
... "(0, 1]": 24,
... "(1, 2]": 42,
... }
... }
... }))
{'inputs: data': 'fake.csv',
'outputs: a column: (0, 1]': 24,
'outputs: a column: (1, 2]': 42}
"""
separator = ": "
items = []
for key, value in dictionary.items():
new_key = parent_key + separator + key if parent_key else key
if isinstance(value, dict):
items.extend(flatten_dict(value, new_key).items())
else:
items.append((new_key, value))
return dict(items)


report = {
"inputs": {
Expand All @@ -9,4 +42,10 @@
}

print(dump(report))
Path(REPORT_PATH).write_text(dump(report))
Path(TXT_REPORT_PATH).write_text(dump(report))

flat_report = flatten_dict(report)
with Path(CSV_REPORT_PATH).open(mode="w", newline="") as handle:
writer = csv.writer(handle)
for kv_pair in flat_report.items():
writer.writerow(kv_pair)
19 changes: 14 additions & 5 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,23 @@ def expect_no_error():
expect_visible(download_results_text)
expect_no_error()

# Notebook:
with page.expect_download() as report_download_info:
page.get_by_text("Download report").click()
# Text Report:
with page.expect_download() as text_report_download_info:
page.get_by_text("Download report (.txt)").click()
expect_no_error()

report_download = text_report_download_info.value
report = report_download.path().read_text()
assert "confidence: 0.95" in report

# CSV Report:
with page.expect_download() as csv_report_download_info:
page.get_by_text("Download table (.csv)").click()
expect_no_error()

report_download = report_download_info.value
report_download = csv_report_download_info.value
report = report_download.path().read_text()
assert "inputs:" in report
assert "outputs: grade: confidence,0.95" in report

# Script:
with page.expect_download() as script_download_info:
Expand Down
Loading