Skip to content

Commit 8493d33

Browse files
Merge branch 'develop' into develop
2 parents 5b0ed2f + 051d404 commit 8493d33

File tree

9 files changed

+160
-51
lines changed

9 files changed

+160
-51
lines changed

docs/CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ As of March 21, help is especially wanted from contributors with experience in K
3030
- Request review from the relevant maintainer(s).
3131
- Check your pull request periodically to see if any changes have been requested or any merge conflicts have arisen.
3232
- If a merge conflict arises, rebase against the latest `develop` branch and force-push the new branch as early as you can. You may need to do this more than once before your changes get merged. Do your best to keep your branch in a mergeable state until it is finished being reviewed and accepted.
33+
- If your change affects the results calculated or presented, update `change_date` in `src/penn_chime/parameters.py` when the pull request is ready for merge, so users can see when results have last changed
3334
- Note: Frequent contributors with write access can submit pull requests from a new branch in the `CodeforPhilly/chime` repository.
3435

3536
## Review & Release

e2e/cypress/integration/tests/actions.spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ context('Actions', () => {
88
it('All + elements are clickable', () => {
99
cy.get('.step-up').click( { multiple: true } )
1010

11+
// This gets the "first" input from the sidebar. From clicking step up,
12+
// the number of days to project should increase from default 60 to 70.
1113
cy.get('input.st-al')
12-
.should('has.value', '7')
14+
.should('has.value', '70')
1315
})
1416
})

src/app.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
import streamlit as st # type: ignore
55

66
from penn_chime.presentation import (
7-
# additional_projections_chart,
87
display_header,
98
display_sidebar,
10-
display_n_days_slider,
119
draw_census_table,
1210
draw_projected_admissions_table,
1311
draw_raw_sir_simulation_table,
@@ -19,11 +17,10 @@
1917
)
2018
from penn_chime.settings import DEFAULTS
2119
from penn_chime.models import sim_sir_df, build_admissions_df, build_census_df
22-
from penn_chime.charts import (
23-
additional_projections_chart,
24-
admitted_patients_chart,
25-
new_admissions_chart,
26-
)
20+
from penn_chime.charts import (additional_projections_chart,
21+
admitted_patients_chart,
22+
new_admissions_chart,
23+
chart_descriptions)
2724

2825
# This is somewhat dangerous:
2926
# Hide the main menu with "Rerun", "run on Save", "clear cache", and "record a screencast"
@@ -39,10 +36,6 @@
3936
notes = "The total size of the susceptible population will be the entire catchment area for Penn Medicine entities (HUP, PAH, PMC, CCH)"
4037
show_more_info_about_this_tool(st=st, parameters=p, inputs=DEFAULTS, notes=notes)
4138

42-
# PRESENTATION
43-
# Two more combination variable initialization / input element creation
44-
as_date = st.checkbox(label="Present result as dates instead of days", value=False)
45-
display_n_days_slider(st, p, DEFAULTS)
4639

4740
# begin format data
4841
admissions_df = build_admissions_df(p=p) # p.n_days, *p.dispositions)
@@ -51,30 +44,34 @@
5144

5245
st.subheader("New Admissions")
5346
st.markdown("Projected number of **daily** COVID-19 admissions at Penn hospitals")
47+
new_admit_chart = new_admissions_chart(alt, admissions_df, parameters=p)
5448
st.altair_chart(
55-
new_admissions_chart(alt, admissions_df, parameters=p, as_date=as_date),
56-
use_container_width=True,
49+
new_admit_chart, use_container_width=True
5750
)
51+
52+
st.markdown(chart_descriptions(new_admit_chart))
53+
5854
if st.checkbox("Show Projected Admissions in tabular form"):
59-
draw_projected_admissions_table(st, admissions_df, as_date=as_date)
55+
draw_projected_admissions_table(st, admissions_df, as_date=p.as_date)
6056
st.subheader("Admitted Patients (Census)")
6157
st.markdown(
6258
"Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals"
6359
)
60+
census_chart = admitted_patients_chart(alt=alt, census=census_df, parameters=p)
6461
st.altair_chart(
65-
admitted_patients_chart(alt=alt, census=census_df, parameters=p, as_date=as_date),
66-
use_container_width=True,
62+
census_chart, use_container_width=True
6763
)
64+
st.markdown(chart_descriptions(census_chart, suffix=" Census"))
6865
if st.checkbox("Show Projected Census in tabular form"):
69-
draw_census_table(st, census_df, as_date=as_date)
66+
draw_census_table(st, census_df, as_date=p.as_date)
7067
st.markdown(
7168
"""**Click the checkbox below to view additional data generated by this simulation**"""
7269
)
7370
if st.checkbox("Show Additional Projections"):
7471
show_additional_projections(
75-
st, alt, additional_projections_chart, parameters=p, as_date=as_date
72+
st, alt, additional_projections_chart, parameters=p
7673
)
7774
if st.checkbox("Show Raw SIR Simulation Data"):
78-
draw_raw_sir_simulation_table(st, parameters=p, as_date=as_date)
75+
draw_raw_sir_simulation_table(st, parameters=p)
7976
write_definitions(st)
8077
write_footer(st)

src/cli.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from pandas import DataFrame
1010

1111
from penn_chime.parameters import Parameters
12-
from penn_chime.utils import build_admissions_df, build_census_df, RateLos
13-
12+
from penn_chime.utils import RateLos
13+
from penn_chime.models import build_admissions_df, build_census_df
1414

1515
class FromFile(Action):
1616
"""From File."""
@@ -123,8 +123,9 @@ def main():
123123
"Recovered": p.recovered_v,
124124
}
125125
)
126-
admits_df = build_admissions_df(p.n_days, *p.dispositions)
127-
census_df = build_census_df(admits_df, *p.lengths_of_stay)
126+
127+
admits_df = build_admissions_df(p)
128+
census_df = build_census_df(admits_df, p)
128129

129130
prefix = a.prefix
130131
for df, name in (

src/penn_chime/charts.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2+
from math import ceil
3+
import datetime
14
from altair import Chart # type: ignore
25
import pandas as pd # type: ignore
36
import numpy as np # type: ignore
@@ -7,11 +10,12 @@
710

811

912
def new_admissions_chart(
10-
alt, projection_admits: pd.DataFrame, parameters: Parameters, as_date: bool = False,
13+
alt, projection_admits: pd.DataFrame, parameters: Parameters
1114
) -> Chart:
1215
"""docstring"""
1316
plot_projection_days = parameters.n_days - 10
1417
max_y_axis = parameters.max_y_axis
18+
as_date = parameters.as_date
1519

1620
y_scale = alt.Scale()
1721

@@ -45,12 +49,13 @@ def new_admissions_chart(
4549

4650

4751
def admitted_patients_chart(
48-
alt, census: pd.DataFrame, parameters: Parameters, as_date: bool = False
52+
alt, census: pd.DataFrame, parameters: Parameters
4953
) -> Chart:
5054
"""docstring"""
5155

5256
plot_projection_days = parameters.n_days - 10
5357
max_y_axis = parameters.max_y_axis
58+
as_date = parameters.as_date
5459
if as_date:
5560
census = add_date_column(census)
5661
x_kwargs = {"shorthand": "date:T", "title": "Date"}
@@ -84,10 +89,16 @@ def admitted_patients_chart(
8489

8590

8691
def additional_projections_chart(
87-
alt, i: np.ndarray, r: np.ndarray, as_date: bool = False, max_y_axis: int = None
92+
alt, parameters: Parameters
8893
) -> Chart:
94+
i = parameters.infected_v
95+
r = parameters.recovered_v
8996
dat = pd.DataFrame({"Infected": i, "Recovered": r})
9097
dat["day"] = dat.index
98+
99+
as_date = parameters.as_date
100+
max_y_axis = parameters.max_y_axis
101+
91102
if as_date:
92103
dat = add_date_column(dat)
93104
x_kwargs = {"shorthand": "date:T", "title": "Date"}
@@ -112,3 +123,43 @@ def additional_projections_chart(
112123
)
113124
.interactive()
114125
)
126+
127+
128+
def chart_descriptions(chart: Chart, suffix: str = ""):
129+
"""
130+
131+
:param chart: Chart: The alt chart to be used in finding max points
132+
:param suffix: str: The assumption is that the charts have similar column names.
133+
The census chart adds " Census" to the column names.
134+
Make sure to include a space or underscore as appropriate
135+
:return: str: Returns a multi-line string description of the results
136+
"""
137+
messages = []
138+
139+
cols = ["Hospitalized", "ICU", "Ventilated"]
140+
asterisk = False
141+
day = "date" if "date" in chart.data.columns else "day"
142+
143+
for col in cols:
144+
if chart.data[col].idxmax() + 1 == len(chart.data):
145+
asterisk = True
146+
147+
on = chart.data[day][chart.data[col].idxmax()]
148+
if day == "date":
149+
on = datetime.datetime.strftime(on, "%b %d") # todo: bring this to an optional arg / i18n
150+
else:
151+
on += 1 # 0 index issue
152+
153+
messages.append(
154+
"{}{} peaks at {:,} on day {}{}".format(
155+
col,
156+
suffix,
157+
ceil(chart.data[col].max()),
158+
on,
159+
"*" if asterisk else "",
160+
)
161+
)
162+
163+
if asterisk:
164+
messages.append("_* The max is at the upper bound of the data, and therefore may not be the actual max_")
165+
return "\n\n".join(messages)

src/penn_chime/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
"""Models."""
1+
"""Models.
2+
3+
Changes affecting results or their presentation should also update
4+
parameters.py `change_date`, so users can see when results have last
5+
changed
6+
"""
27

38
from typing import Generator, Tuple
49

src/penn_chime/parameters.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
"""Parameters."""
1+
"""Parameters.
2+
3+
Changes affecting results or their presentation should also update
4+
`change_date`, so users can see when results have last changed
5+
"""
26

37
from numpy import log2 # type: ignore
48

@@ -23,7 +27,8 @@ def __init__(
2327
icu: RateLos,
2428
ventilated: RateLos,
2529
max_y_axis: int = None,
26-
n_days: int = None
30+
n_days: int = None,
31+
as_date: bool = False
2732
):
2833
self.current_hospitalized = current_hospitalized
2934
self.doubling_time = doubling_time
@@ -38,6 +43,7 @@ def __init__(
3843
self.ventilated = ventilated
3944

4045
self.max_y_axis = max_y_axis
46+
self.as_date = as_date
4147

4248
self.rates = tuple(each.rate for each in (hospitalized, icu, ventilated))
4349
self.lengths_of_stay = tuple(
@@ -128,3 +134,11 @@ def n_days(self, n_days: int):
128134
i_icu_v,
129135
i_ventilated_v,
130136
)
137+
138+
def change_date(self):
139+
"""
140+
This reflects a date from which previously-run reports will no
141+
longer match current results, indicating when users should
142+
re-run their reports
143+
"""
144+
return "March 23 2020"

src/penn_chime/presentation.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def display_header(st, p):
4545
unsafe_allow_html=True,
4646
)
4747
st.markdown(
48-
"""**IMPORTANT NOTICE**: Admissions and Census calculations were previously **undercounting**. Please update your reports. See more about this issue [here](https://github.com/CodeForPhilly/chime/pull/190)."""
48+
"""**IMPORTANT NOTICE**: Admissions and Census calculations were previously **undercounting**. Please update your reports generated before """ + p.change_date() + """. See more about changes [here](https://github.com/CodeForPhilly/chime/labels/models)."""
4949
)
5050
st.markdown(
5151
"""*This tool was developed by the [Predictive Healthcare team](http://predictivehealthcare.pennmedicine.org/) at
@@ -93,6 +93,14 @@ def display_sidebar(st, d: Constants) -> Parameters:
9393
if d.known_infected < 1:
9494
raise ValueError("Known cases must be larger than one to enable predictions.")
9595

96+
n_days = st.sidebar.number_input(
97+
"Number of days to project",
98+
min_value=30,
99+
value=d.n_days,
100+
step=10,
101+
format="%i",
102+
)
103+
96104
current_hospitalized = st.sidebar.number_input(
97105
"Currently Hospitalized COVID-19 Patients",
98106
min_value=0,
@@ -204,6 +212,8 @@ def display_sidebar(st, d: Constants) -> Parameters:
204212
format="%i",
205213
)
206214

215+
as_date = st.sidebar.checkbox(label="Present result as dates instead of days", value=False)
216+
207217
max_y_axis_set = st.sidebar.checkbox("Set the Y-axis on graphs to a static value")
208218
max_y_axis = None
209219
if max_y_axis_set:
@@ -212,6 +222,7 @@ def display_sidebar(st, d: Constants) -> Parameters:
212222
)
213223

214224
return Parameters(
225+
n_days=n_days,
215226
current_hospitalized=current_hospitalized,
216227
doubling_time=doubling_time,
217228
known_infected=known_infected,
@@ -222,18 +233,7 @@ def display_sidebar(st, d: Constants) -> Parameters:
222233
icu=RateLos(icu_rate, icu_los),
223234
ventilated=RateLos(ventilated_rate, ventilated_los),
224235
max_y_axis=max_y_axis,
225-
)
226-
227-
228-
def display_n_days_slider(st, p: Parameters, d: Constants):
229-
"""Display n_days_slider."""
230-
p.n_days = st.slider(
231-
"Number of days to project",
232-
min_value=30,
233-
max_value=200,
234-
value=d.n_days,
235-
step=1,
236-
format="%i",
236+
as_date=as_date,
237237
)
238238

239239

@@ -368,7 +368,7 @@ def write_footer(st):
368368

369369

370370
def show_additional_projections(
371-
st, alt, charting_func, parameters, as_date: bool = False,
371+
st, alt, charting_func, parameters
372372
):
373373
st.subheader(
374374
"The number of infected and recovered individuals in the hospital catchment region at any given moment"
@@ -377,10 +377,7 @@ def show_additional_projections(
377377
st.altair_chart(
378378
charting_func(
379379
alt,
380-
parameters.infected_v,
381-
parameters.recovered_v,
382-
as_date=as_date,
383-
max_y_axis=parameters.max_y_axis,
380+
parameters=parameters
384381
),
385382
use_container_width=True,
386383
)
@@ -403,7 +400,6 @@ def draw_projected_admissions_table(
403400
admits_table = add_date_column(
404401
admits_table, drop_day_column=True, date_format=DATE_FORMAT
405402
)
406-
407403
st.table(admits_table)
408404
return None
409405

@@ -423,7 +419,8 @@ def draw_census_table(st, census_df: pd.DataFrame, as_date: bool = False):
423419
return None
424420

425421

426-
def draw_raw_sir_simulation_table(st, parameters, as_date: bool = False):
422+
def draw_raw_sir_simulation_table(st, parameters):
423+
as_date = parameters.as_date
427424
days = np.arange(0, parameters.n_days + 1)
428425
data_list = [
429426
days,

0 commit comments

Comments
 (0)