forked from wseis/qmra
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add user guide and improve zip export
- Loading branch information
Showing
16 changed files
with
312 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
from zipfile import ZipFile | ||
import base64 | ||
from django.db.models import QuerySet | ||
from django.template.loader import render_to_string | ||
|
||
from qmra.risk_assessment.models import RiskAssessment, RiskAssessmentResult, Inflow, Treatment | ||
import pandas as pd | ||
|
||
from qmra.risk_assessment.plots import risk_plots | ||
|
||
|
||
def inflows_as_df(inflows: QuerySet[Inflow]): | ||
dfs = [] | ||
for inflow in inflows.all(): | ||
dfs += [pd.DataFrame({ | ||
"Pathogen": [inflow.pathogen], | ||
"Minimum Concentration": [inflow.min], | ||
"Maximum Concentration": [inflow.max], | ||
})] | ||
return pd.concat(dfs) | ||
|
||
|
||
def treatments_as_df(treatments: QuerySet[Treatment]) -> pd.DataFrame: | ||
dfs = [] | ||
for t in treatments.all(): | ||
dfs += [pd.DataFrame({ | ||
"Treatment": [t.name] * 3, | ||
"Pathogen group": ["Viruses", "Bacteria", "Protozoa"], | ||
"Maximum LRV": [t.viruses_max, t.bacteria_max, t.protozoa_max], | ||
"Minimum LRV": [t.viruses_min, t.bacteria_min, t.protozoa_min] | ||
})] | ||
return pd.concat(dfs) | ||
|
||
|
||
def risk_assessment_result_as_df(pathogen: str, r: RiskAssessmentResult) -> pd.DataFrame: | ||
return pd.DataFrame({ | ||
("", "pathogen"): [pathogen] * 2, | ||
("", "stat"): ["Maximum LRV", "Minimum LRV"], | ||
("Infection prob.", "min"): [ | ||
r.infection_maximum_lrv_min, r.infection_minimum_lrv_min | ||
], | ||
("Infection prob.", "25%"): [ | ||
r.infection_maximum_lrv_q1, r.infection_minimum_lrv_q1 | ||
], | ||
("Infection prob.", "50%"): [ | ||
r.infection_maximum_lrv_median, r.infection_minimum_lrv_median | ||
], | ||
("Infection prob.", "75%"): [ | ||
r.infection_maximum_lrv_q3, r.infection_minimum_lrv_q3 | ||
], | ||
("Infection prob.", "max"): [ | ||
r.infection_maximum_lrv_max, r.infection_minimum_lrv_max | ||
], | ||
("DALYs pppy", "min"): [ | ||
r.dalys_maximum_lrv_min, r.dalys_minimum_lrv_min | ||
], | ||
("DALYs pppy", "25%"): [ | ||
r.dalys_maximum_lrv_q1, r.dalys_minimum_lrv_q1 | ||
], | ||
("DALYs pppy", "50%"): [ | ||
r.dalys_maximum_lrv_median, r.dalys_minimum_lrv_median | ||
], | ||
("DALYs pppy", "75%"): [ | ||
r.dalys_maximum_lrv_q3, r.dalys_minimum_lrv_q3 | ||
], | ||
("DALYs pppy", "max"): [ | ||
r.dalys_maximum_lrv_max, r.dalys_minimum_lrv_max | ||
], | ||
}) | ||
|
||
|
||
def results_as_df(results: dict[str, RiskAssessmentResult]) -> pd.DataFrame: | ||
dfs = [] | ||
for pathogen, r in results.items(): | ||
dfs += [risk_assessment_result_as_df(pathogen, r)] | ||
return pd.concat(dfs) | ||
|
||
|
||
def risk_assessment_as_zip(buffer, risk_assessment: RiskAssessment): | ||
inflows = inflows_as_df(risk_assessment.inflows) | ||
treatments = treatments_as_df(risk_assessment.treatments) | ||
results = results_as_df({r.pathogen: r for r in risk_assessment.results.all()}) | ||
plots = risk_plots(risk_assessment.results.all(), "png") | ||
report = render_to_string("assessment-result-export.html", | ||
context=dict(results=risk_assessment.results.all(), | ||
infection_risk=risk_assessment.infection_risk, | ||
risk_plot_data=base64.b64encode(plots[0]).decode("utf-8"), | ||
daly_plot_data=base64.b64encode(plots[1]).decode("utf-8"))) | ||
with ZipFile(buffer, mode="w") as archive: | ||
archive.mkdir("exposure-assessment") | ||
archive.mkdir("results-plots") | ||
archive.writestr("exposure-assessment/inflows.csv", inflows.to_csv(sep=",", decimal=".", index=False)) | ||
archive.writestr("exposure-assessment/treatments.csv", treatments.to_csv(sep=",", decimal=".", index=False)) | ||
archive.writestr(f"{risk_assessment.name}-result.csv", results.to_csv(sep=",", decimal=".", index=False)) | ||
archive.writestr(f"{risk_assessment.name}-report.html", report) | ||
archive.writestr("results-plots/infection-probability.png", plots[0]) | ||
archive.writestr("results-plots/dalys-pppy.png", plots[1]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
qmra/risk_assessment/templates/assessment-result-export.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
{% include "head.html" %} | ||
<body> | ||
<div class="container"> | ||
{% include "assessment-result.html" with results=results infection_risk=infection_risk risk_plot_data=risk_plot_data daly_plot_data=daly_plot_data %} | ||
</div> | ||
</body> | ||
<style type="text/css"> | ||
|
||
.max-risk { | ||
background: #FFECF4; | ||
color: #FF0532; | ||
} | ||
|
||
.min-risk { | ||
background: #FFDDB5; | ||
color: #ED5500; | ||
} | ||
|
||
.none-risk { | ||
background: #E2FBAC; | ||
color: #088B3C; | ||
} | ||
</style> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<script type="text/javascript"> | ||
const steps = [ | ||
{ | ||
title: "Name and description", | ||
content: "Before configuring your first QMRA model, give the risk assessment an informative name. It is also helpful to add some information on the scope and purpose of the risk assessment, for which you can use the provided text field. The text will be part of the final report, so you may adapt the level of detail to the target group of people, you want to share the report with. Don't worry, both can be adapted even after the model is configured.", | ||
target: "#name-and-description", | ||
order: 0 | ||
}, | ||
{ | ||
title: "Exposure", | ||
content: "A QMRA model always contain an exposure assessment. Within exposure assessment the frequency of exposure is defined as the number of exposure events. The ingested volume defines the magnitude of exposure per event.\nWhen you select an exposure name, default values for the number of events and the volume per event will be set. You can modify these values as you wish.", | ||
target: "#exposure-form-fieldset", | ||
order: 1 | ||
}, | ||
{ | ||
title: "Source water and inflows", | ||
content: "In this section you can define the type of source water and the concentrations of reference pathogens for your QMRA model. When you select a source water type, the app will fill default values for the reference pathogens Rotavirus, Cryptosporidium spp. and Campylobacter jejuni. Once again, you can modify these values as you wish.", | ||
target: "#inflow-content", | ||
order: 2 | ||
}, | ||
{ | ||
title: "Treatments", | ||
content: "The last step of a QMRA model is the configuration of the planned or implemented treatment processes. Each treatment is associated with a certain logremoval value (LRV) for viruses, bacteria, and protozoan parasites, respectively. When you add a treatment to your model, it is initialized with default LRVs collected from international guideline documents. Note, however, that the most reliable results may be achieved by providing locally obtained removal rates. Note also that treatments can be non-technical barriers or contain negative LRV for simulating recontamination.", | ||
target: "#treatment-content", | ||
order: 3 | ||
}, | ||
{ | ||
title: "Results", | ||
content: "Clicking on this tab will allow you to display the result of your risk assessment.", | ||
target: "#result-button", | ||
order: 4 | ||
}, | ||
{ | ||
title: "Save", | ||
content: "Once the parameters for the exposure and the inflows are set (an assessment can contain zero or more treatments), you can save your risk assessment. This will take you to your list of risk assessment where you can compare multiple scenarios to each other and export assessments as zip archives.", | ||
target: "#configurator-commands", | ||
order: 5 | ||
}, | ||
{ | ||
title: "References", | ||
content: "You can inspect the references for the default values of the elements you selected (exposure, source water, treatments) by clicking on this tab. Note that any change you made to the default values will not be reflected in this section.", | ||
target: "#references-button", | ||
order: 6 | ||
}, | ||
]; | ||
document.addEventListener("DOMContentLoaded", () => { | ||
const tg = new tourguide.TourGuideClient({ | ||
steps: steps | ||
}) | ||
tg.onAfterExit(() => { | ||
document.querySelector("#result-button").click(); | ||
}) | ||
document.querySelector("#start-user-guide").addEventListener("click", ev => { | ||
tg.start(); | ||
}) | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import io | ||
|
||
from assertpy import assert_that | ||
from django.test import TestCase | ||
import pandas as pd | ||
|
||
from qmra.risk_assessment import exports | ||
from qmra.risk_assessment.models import RiskAssessment, Inflow, Treatment, DefaultTreatments | ||
from qmra.risk_assessment.risk import assess_risk | ||
from qmra.user.models import User | ||
|
||
|
||
class TestResultExport(TestCase): | ||
|
||
def test_that(self): | ||
given_user = User.objects.create_user("test-user2", "[email protected]", "password") | ||
given_user.save() | ||
given_ra = RiskAssessment.objects.create( | ||
user=given_user, | ||
events_per_year=1, | ||
volume_per_event=2, | ||
) | ||
given_ra.save() | ||
given_inflows = [ | ||
Inflow.objects.create( | ||
risk_assessment=given_ra, | ||
pathogen="Rotavirus", | ||
min=0.1, max=0.2 | ||
), | ||
Inflow.objects.create( | ||
risk_assessment=given_ra, | ||
pathogen="Campylobacter jejuni", | ||
min=0.1, max=0.2 | ||
), | ||
Inflow( | ||
risk_assessment=given_ra, | ||
pathogen="Cryptosporidium parvum", | ||
min=0.1, max=0.2 | ||
), | ||
] | ||
given_treatments = [ | ||
Treatment.from_default(t, given_ra) | ||
for t in list(DefaultTreatments.data.values())[:3] | ||
] | ||
given_ra.inflows.set(given_inflows, bulk=False) | ||
given_ra.treatments.set(given_treatments, bulk=False) | ||
|
||
results = assess_risk(given_ra, given_inflows, given_treatments) | ||
given_ra = RiskAssessment.objects.get(pk=given_ra.id) | ||
with io.BytesIO() as buffer: | ||
exports.risk_assessment_as_zip(buffer, given_ra) | ||
buffer.seek(0) | ||
with open("test.zip", "wb") as f: | ||
f.write(buffer.getvalue()) | ||
assert_that(buffer).is_not_none() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.