Skip to content

Commit 94eb9ce

Browse files
Merge pull request #573 from CodeForPhilly/ppe
- Adds Personal Protective Equipment (PPE) Model with downloadable tool
2 parents bc8f45b + d1333db commit 94eb9ce

File tree

16 files changed

+101
-10
lines changed

16 files changed

+101
-10
lines changed

.github/workflows/pythonapp.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
python -m pytest
4444
- name: Run App
4545
run: |
46-
PARAMETERS=./defaults/cypress.cfg streamlit run st_app.py &
46+
PARAMETERS=./defaults/cypress.cfg ASSETS=./defaults/assets streamlit run st_app.py &
4747
- name: Cypress
4848
uses: cypress-io/github-action@v1
4949
with:

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
FROM python:3.7.7-slim-buster
2+
ENV ASSETS=./defaults/assets/
23
ENV PARAMETERS=./defaults/webapp.cfg
34
ENV PORT=8000
45
WORKDIR /app
@@ -11,4 +12,5 @@ COPY defaults defaults
1112
COPY src src
1213
COPY st_app.py st_app.py
1314
RUN pip install -q .
15+
1416
CMD STREAMLIT_SERVER_PORT=$PORT streamlit run st_app.py

Procfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
web: PARAMETERS=defaults/webapp.cfg STREAMLIT_SERVER_PORT=$PORT streamlit run st_app.py
1+
web: PARAMETERS=./defaults/webapp.cfg ASSETS=./defaults/assets STREAMLIT_SERVER_PORT=$PORT streamlit run st_app.py
Binary file not shown.

defaults/assets/PPE_Screenshot.jpg

253 KB
Loading
Loading
1.01 MB
Loading

docs/contributing/app-dev.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ pip install streamlit
4646
## Run the Streamlit Web App
4747

4848
```bash
49+
ASSETS=./defaults/assets \
4950
PARAMETERS=-./defaults/webapp.cfg streamlit run st_app.py
5051
```
5152

5253
## Run the Command Line Interface
5354

5455
```bash
56+
ASSETS=./defaults/assets \
5557
PARAMETERS=./defaults/cli.cfg penn_chime
5658
```
5759

@@ -66,6 +68,7 @@ penn_chime --help
6668
If you want a different set of default parameters, you may use your own configuration file.
6769

6870
```bash
71+
ASSETS=./defaults/assets \
6972
PARAMETERS=./defaults/yours.cfg streamlit run st_app.py
7073
```
7174

@@ -77,7 +80,9 @@ Be sure to include `--mitigation-date` in the file if social distancing was impl
7780
If you need to run the application on a different port than the default (8000), you can set an environment variable.
7881

7982
```bash
80-
STREAMLIT_SERVER_PORT=1234 PARAMETERS=./defaults/webapp.cfg streamlit run st_app.py
83+
ASSETS=./defaults/assets \
84+
STREAMLIT_SERVER_PORT=1234 \
85+
PARAMETERS=./defaults/webapp.cfg streamlit run st_app.py
8186
```
8287

8388
## Project Layout

heroku.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ build:
22
docker:
33
web: Dockerfile
44
config:
5-
PORT: ${PORT}
5+
PORT: ${PORT}

src/penn_chime/cli.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@
66
from .model.parameters import Parameters
77
from .model.sir import Sir
88

9+
910
def run(argv):
11+
"""Eun cli."""
1012
p = Parameters.create(os.environ, argv[1:])
1113
m = Sir(p)
1214

1315
for df, name in (
1416
(m.sim_sir_w_date_df, "sim_sir_w_date"),
1517
(m.admits_df, "projected_admits"),
1618
(m.census_df, "projected_census"),
19+
(m.ppe_df, 'ppe_data')
1720
):
1821
df.to_csv(f"{p.current_date}_{name}.csv")
1922

23+
2024
def main():
2125
"""Main."""
2226
run(sys.argv)

src/penn_chime/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ en:
33
app-new-admissions-text: "Projected number of **daily** COVID-19 admissions."
44
app-admitted-patients-title: "Admitted Patients (Census)"
55
app-admitted-patients-text: "Projected **census** of COVID-19 patients, accounting for arrivals and discharges."
6+
app-PPE-title: "Personal Protective Equipment (PPE) Calculator"
67
app-SIR-title: "Susceptible, Infected, and Recovered"
78
app-SIR-text: "The number of susceptible, infected, and recovered individuals in the hospital catchment region at any given moment"
89
charts-date: "Date"

src/penn_chime/model/ppe.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Personal Protective Equipment."""
2+
3+
import os
4+
from typing import Dict
5+
6+
7+
class PPE:
8+
9+
def __init__(self, env: Dict[str, str]):
10+
"""__init__."""
11+
self.assets = assets = env['ASSETS']
12+
self.filename = filename = "PPE_Calculator_for_COVID-19.xlsx"
13+
self.src = os.path.join(assets, filename)
14+
self.screenshot = os.path.join(assets, 'PPE_Screenshot.jpg')

src/penn_chime/model/sir.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,15 @@ def __init__(self, p: Parameters):
174174
'census_icu': self.raw['census_icu'],
175175
'census_ventilated': self.raw['census_ventilated'],
176176
})
177+
self.ppe_df = pd.DataFrame(data={
178+
'day': self.raw['day'],
179+
'date': self.raw['date'],
180+
'census_hospitalized': self.raw['census_hospitalized'],
181+
'census_icu': self.raw['census_icu'],
182+
'census_ventilated': self.raw['census_ventilated'],
183+
'admits_hospitalized': self.raw['admits_hospitalized'],
184+
})
185+
self.ppe_df = self.ppe_df[self.ppe_df['day']>=0]
177186

178187
logger.info('len(np.arange(-i_day, n_days+1)): %s', len(np.arange(-self.i_day, p.n_days+1)))
179188
logger.info('len(raw_df): %s', len(self.raw_df))

src/penn_chime/utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
def dataframe_to_base64(df: pd.DataFrame) -> str:
9-
"""Converts a dataframe to a base64-encoded CSV representation of that data.
9+
"""Converts a dataframe into csv base64-encoded data.
1010
1111
This is useful for building datauris for use to download the data in the browser.
1212
@@ -16,3 +16,9 @@ def dataframe_to_base64(df: pd.DataFrame) -> str:
1616
csv = df.to_csv(index=False)
1717
b64 = b64encode(csv.encode()).decode()
1818
return b64
19+
20+
21+
def excel_to_base64(filename: str) -> str:
22+
"""Converts an excel document into base64-encoded data."""
23+
with open(filename, 'rb') as fin:
24+
return b64encode(fin.read()).decode()

src/penn_chime/view/st_app.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import os
44

5-
import altair as alt # type: ignore
6-
import streamlit as st # type: ignore
7-
import i18n # type: ignore
5+
import altair as alt
6+
import streamlit as st
7+
import i18n
88

99
i18n.set('filename_format', '{locale}.{format}')
1010
i18n.set('locale', 'en')
@@ -13,13 +13,15 @@
1313

1414
from ..model.parameters import Parameters
1515
from ..model.sir import Sir
16+
from ..model.ppe import PPE
1617
from .charts import (
1718
build_admits_chart,
1819
build_census_chart,
1920
build_sim_sir_w_date_chart,
2021
)
2122
from .st_display import (
2223
display_download_link,
24+
display_excel_download_link,
2325
display_footer,
2426
display_header,
2527
display_sidebar,
@@ -35,6 +37,8 @@ def main():
3537
st.markdown(hide_menu_style, unsafe_allow_html=True)
3638

3739
d = Parameters.create(os.environ, [])
40+
ppe = PPE(os.environ)
41+
3842
p = display_sidebar(st, d)
3943
m = Sir(p)
4044

@@ -62,6 +66,29 @@ def main():
6266
df=m.census_df,
6367
)
6468

69+
st.subheader(i18n.t("app-PPE-title"))
70+
display_excel_download_link(st, ppe.filename, ppe.src)
71+
display_download_link(
72+
st,
73+
p,
74+
filename=f"{p.current_date}_projected_census_for_ppe_calculator.csv",
75+
df=m.ppe_df,
76+
)
77+
78+
if st.checkbox("Show a screenshot of the tool"):
79+
st.image(
80+
image=ppe.screenshot,
81+
width=600,
82+
format='JPEG',
83+
)
84+
st.markdown("""
85+
Refer to our <a href="{link_to_docs}">user documentation for instructions on how to use the tool</a>.
86+
""".format(
87+
link_to_docs="https://code-for-philly.gitbook.io/chime/ppe-calculator",
88+
),
89+
unsafe_allow_html=True
90+
)
91+
6592
st.subheader(i18n.t("app-SIR-title"))
6693
st.markdown(i18n.t("app-SIR-text"))
6794
sim_sir_w_date_chart = build_sim_sir_w_date_chart(alt=alt, sim_sir_w_date_floor_df=m.sim_sir_w_date_floor_df)

src/penn_chime/view/st_display.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import os
44
import json
55

6+
from logging import INFO, basicConfig, getLogger
67
import pandas as pd
78
import i18n
9+
from sys import stdout
810

911
from ..constants import (
1012
CHANGE_DATE,
@@ -15,9 +17,21 @@
1517
VERSION,
1618
)
1719
from ..model.parameters import Parameters, Disposition
18-
from ..utils import dataframe_to_base64
20+
from ..utils import (
21+
dataframe_to_base64,
22+
excel_to_base64,
23+
)
1924
from .spreadsheet import spreadsheet
2025

26+
27+
basicConfig(
28+
level=INFO,
29+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
30+
stream=stdout,
31+
)
32+
logger = getLogger(__name__)
33+
34+
2135
hide_menu_style = """
2236
<style>
2337
#MainMenu {visibility: hidden;}
@@ -448,7 +462,6 @@ def display_footer(st):
448462
)
449463
st.markdown(i18n.t("presentation-copyright"))
450464

451-
452465
def display_download_link(st, p, filename: str, df: pd.DataFrame):
453466
csv = dataframe_to_base64(df.rename(p.labels, axis=1))
454467
st.markdown(
@@ -457,3 +470,13 @@ def display_download_link(st, p, filename: str, df: pd.DataFrame):
457470
),
458471
unsafe_allow_html=True,
459472
)
473+
474+
def display_excel_download_link(st, filename: str, src: str):
475+
excel = excel_to_base64(src)
476+
st.markdown("""
477+
Download the PPE Calculator here: <a download="{filename}" href="data:file/xlsx;base64,{excel}">{filename}</a>.
478+
""".format(
479+
excel=excel, filename=filename
480+
),
481+
unsafe_allow_html=True,
482+
)

0 commit comments

Comments
 (0)