Skip to content

Commit

Permalink
Merge branch 'develop' into ahil-elastic
Browse files Browse the repository at this point in the history
  • Loading branch information
jlubken authored Apr 29, 2020
2 parents d65eb20 + 6eda172 commit 108c24d
Show file tree
Hide file tree
Showing 25 changed files with 360 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
python -m pytest
- name: Run App
run: |
PARAMETERS=./defaults/cypress.cfg streamlit run st_app.py &
PARAMETERS=./defaults/cypress.cfg ASSETS=./defaults/assets streamlit run st_app.py &
- name: Cypress
uses: cypress-io/github-action@v1
with:
Expand Down
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FROM python:3.7.7-slim-buster
ENV ASSETS=./defaults/assets/
ENV PARAMETERS=./defaults/webapp.cfg
ENV PORT=8000
WORKDIR /app
COPY README.md .
COPY setup.cfg .
Expand All @@ -11,5 +13,4 @@ COPY src src
COPY st_app.py st_app.py
RUN pip install -q .

CMD ["streamlit", "run", "st_app.py"]

CMD STREAMLIT_SERVER_PORT=$PORT streamlit run st_app.py
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: PARAMETERS=defaults/webapp.cfg STREAMLIT_SERVER_PORT=$PORT streamlit run st_app.py
web: PARAMETERS=./defaults/webapp.cfg ASSETS=./defaults/assets STREAMLIT_SERVER_PORT=$PORT streamlit run st_app.py
Binary file added defaults/assets/PPE_Calculator_for_COVID-19.xlsx
Binary file not shown.
Binary file added defaults/assets/PPE_Screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added defaults/assets/PPE_Screenshot_Interface_v0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added defaults/assets/PPE_Screenshot_Output_v0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 16 additions & 1 deletion docs/contributing/app-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ pip install streamlit
## Run the Streamlit Web App

```bash
ASSETS=./defaults/assets \
PARAMETERS=-./defaults/webapp.cfg streamlit run st_app.py
```

## Run the Command Line Interface

```bash
ASSETS=./defaults/assets \
PARAMETERS=./defaults/cli.cfg penn_chime
```

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

```bash
ASSETS=./defaults/assets \
PARAMETERS=./defaults/yours.cfg streamlit run st_app.py
```

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

```bash
STREAMLIT_SERVER_PORT=1234 PARAMETERS=./defaults/webapp.cfg streamlit run st_app.py
ASSETS=./defaults/assets \
STREAMLIT_SERVER_PORT=1234 \
PARAMETERS=./defaults/webapp.cfg streamlit run st_app.py
```

### Choosing a Different Language

If you want to run the application in another language, do the following. You can select Japanese as the language other than English.

```bash
ASSETS=./defaults/assets \
LANG=ja \
PARAMETERS=./defaults/webapp.cfg streamlit run st_app.py
```

## Project Layout
Expand Down
3 changes: 2 additions & 1 deletion docs/contributing/operations-support.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Contributing: Operations Support

*Coming soon*
- This repo is associated with Penn Medicine's Chime1.0 model, which uses a Streamlit app that is built on an SIR model
- Contributors are encouraged to consider supporting Penn Medicine's Chime2.0 model, which still uses a Streamlit app, but leverages an enhanced Bayesian SEIR model.
4 changes: 2 additions & 2 deletions heroku.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
build:
docker:
web: Dockerfile.dash
web: Dockerfile
config:
PORT: ${PORT}
PORT: ${PORT}
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Setup file for chime
"""
__version__ = "1.1.3" # update VERSION in constants.py
__version__ = "1.1.4" # update VERSION in constants.py
__author__ = "Predictive Healthcare @ Penn Medicine"

from setuptools import setup, find_namespace_packages
from setuptools import setup, find_packages


setup(
Expand All @@ -19,7 +19,7 @@
"Documentation": "https://codeforphilly.github.io/chime/",
},
package_dir={'': 'src'},
packages=find_namespace_packages(where='src', exclude=('tests')),
packages=find_packages(where='src', exclude=('tests')),
install_requires=[
"altair",
"black",
Expand Down
4 changes: 4 additions & 0 deletions src/penn_chime/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
from .model.parameters import Parameters
from .model.sir import Sir


def run(argv):
"""Eun cli."""
p = Parameters.create(os.environ, argv[1:])
m = Sir(p)

for df, name in (
(m.sim_sir_w_date_df, "sim_sir_w_date"),
(m.admits_df, "projected_admits"),
(m.census_df, "projected_census"),
(m.ppe_df, 'ppe_data')
):
df.to_csv(f"{p.current_date}_{name}.csv")


def main():
"""Main."""
run(sys.argv)
4 changes: 3 additions & 1 deletion src/penn_chime/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
re-run their reports
"""
CHANGE_DATE = date(year=2020, month=4, day=8)
VERSION = 'v1.1.3'
VERSION = 'v1.1.4'

DATE_FORMAT = "%b, %d" # see https://strftime.org
DOCS_URL = "https://code-for-philly.gitbook.io/chime"
Expand All @@ -17,3 +17,5 @@

FLOAT_INPUT_MIN = 0.0001
FLOAT_INPUT_STEP = 0.1

PREFIT_ADDITIONAL_DAYS = 300
6 changes: 6 additions & 0 deletions src/penn_chime/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ en:
app-new-admissions-text: "Projected number of **daily** COVID-19 admissions."
app-admitted-patients-title: "Admitted Patients (Census)"
app-admitted-patients-text: "Projected **census** of COVID-19 patients, accounting for arrivals and discharges."
app-PPE-title: "Personal Protective Equipment (PPE) Calculator"
app-PPE-screenshot: "Show a screenshot of the tool"
app-PPE-documentation: |+
Refer to our <a href="{link_to_docs}">user documentation for instructions on how to use the tool</a>.
app-SIR-title: "Susceptible, Infected, and Recovered"
app-SIR-text: "The number of susceptible, infected, and recovered individuals in the hospital catchment region at any given moment"
charts-date: "Date"
Expand Down Expand Up @@ -81,6 +85,8 @@ en:
presentation-copyright: "© 2020, The Trustees of the University of Pennsylvania"
presentation-download: |+
<a download="{filename}" href="data:file/csv;base64,{csv}">Download {filename}</a>
presentation-excel-download: |+
Download the PPE Calculator here: <a download="{filename}" href="data:file/xlsx;base64,{excel}">{filename}</a>.
admits_hospitalized: "Hospitalized Admissions"
admits_icu: "ICU Admissions"
admits_ventilated: "Ventilated Admissions"
Expand Down
6 changes: 6 additions & 0 deletions src/penn_chime/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ ja:
app-new-admissions-text: "**日毎の**COVID-19関連新規入院患者数予測"
app-admitted-patients-title: "累計入院患者数"
app-admitted-patients-text: "入院と退院を考慮したCOVID-19患者の予測**累計患者数**"
app-PPE-title: "個人用防護具 (PPE) 計算機"
app-PPE-screenshot: "ツールのスクリーンショットを表示"
app-PPE-documentation: |+
ツールの使い方については<a href="{link_to_docs}">ユーザドキュメントの説明</a>を確認してください。
app-SIR-title: "感受性保持者、感染者と回復者"
app-SIR-text: "与えられた時間範囲での、病院の担当範囲領域における感受性保持者、感染者と回復者の人数"
charts-date: "日付"
Expand Down Expand Up @@ -79,6 +83,8 @@ ja:
presentation-copyright: "© 2020, The Trustees of the University of Pennsylvania"
presentation-download: |+
<a download="{filename}" href="data:file/csv;base64,{csv}">{filename} をダウンロード</a>
presentation-excel-download: |+
個人用防護具計算機のダウンロードはこちら: <a download="{filename}" href="data:file/xlsx;base64,{excel}">{filename}</a>.
admits_hospitalized: "新規入院患者数"
admits_icu: "新規集中治療患者数"
admits_ventilated: "新規人工呼吸患者数"
Expand Down
Empty file.
41 changes: 31 additions & 10 deletions src/penn_chime/model/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def validate(string):
"ventilated": ValDisposition,
"hospitalized": ValDisposition,
"icu": ValDisposition,
"use_log_scale": OptionalValue
}


Expand All @@ -161,16 +162,17 @@ def validate(string):
"relative_contact_rate": "Social distancing reduction rate: 0.0 - 1.0",
"ventilated_days": "Average days on ventilator",
"ventilated_rate": "Ventilated Rate: 0.0 - 1.0",
"use_log_scale": "Flag to use logarithmic scale on charts instead of linear scale."
}


ARGS = (
(
"parameters",
str,
None,
None,
False,
None, # Min value
None, # Max value
False, # Whether it is required or optional.
),
(
"current_hospitalized",
Expand Down Expand Up @@ -298,15 +300,24 @@ def validate(string):
1.0,
True,
),
(
"use_log_scale",
bool,
None,
None,
False
)
)


def to_cli(name):
return "--" + name.replace('_', '-')


class Parameters:
"""Parameters."""
"""
Object containing all of the parameters that can be adjusted by the user, either from the command line or using
the side bar of the web app.
"""

@classmethod
def parser(cls):
Expand All @@ -315,11 +326,20 @@ def parser(cls):

for name, cast, min_value, max_value, required in ARGS:
arg = to_cli(name)
parser.add_argument(
arg,
type=validator(arg, cast, min_value, max_value, required),
help=HELP.get(name),
)
if cast == bool:
# This argument is a command-line flag and does not need validation.
parser.add_argument(
arg,
action='store_true',
help=HELP.get(name),
)
else:
# Use a custom validator for any arguments that take in values.
parser.add_argument(
arg,
type=validator(arg, cast, min_value, max_value, required),
help=HELP.get(name),
)
return parser

@classmethod
Expand Down Expand Up @@ -396,6 +416,7 @@ def __init__(self, **kwargs):
self.relative_contact_rate = None
self.recovered = None
self.ventilated = None
self.use_log_scale = False

passed_and_default_parameters = {}
for key, value in kwargs.items():
Expand Down
14 changes: 14 additions & 0 deletions src/penn_chime/model/ppe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Personal Protective Equipment."""

import os
from typing import Dict


class PPE:

def __init__(self, env: Dict[str, str]):
"""__init__."""
self.assets = assets = env['ASSETS']
self.filename = filename = "PPE_Calculator_for_COVID-19.xlsx"
self.src = os.path.join(assets, filename)
self.screenshot = os.path.join(assets, 'PPE_Screenshot.jpg')
51 changes: 36 additions & 15 deletions src/penn_chime/model/sir.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import numpy as np
import pandas as pd

from ..constants import PREFIT_ADDITIONAL_DAYS
from .parameters import Parameters


Expand Down Expand Up @@ -68,31 +69,42 @@ def __init__(self, p: Parameters):

if p.mitigation_date is None:
self.i_day = 0 # seed to the full length
raw = self.run_projection(p, [(self.beta, p.n_days)])
raw = self.run_projection(p, [
(self.beta, p.n_days + PREFIT_ADDITIONAL_DAYS)])
self.i_day = i_day = int(get_argmin_ds(raw["census_hospitalized"], p.current_hospitalized))

self.raw = self.run_projection(p, self.gen_policy(p))
self.raw = self.run_projection(p, self.get_policies(p))

logger.info('Set i_day = %s', i_day)
else:
projections = {}
best_i_day = -1
best_i_day_loss = float('inf')
for i_day in range(p.n_days):
self.i_day = i_day
raw = self.run_projection(p, self.gen_policy(p))
for self.i_day in range(p.n_days + PREFIT_ADDITIONAL_DAYS):
mitigation_day = -(p.current_date - p.mitigation_date).days
if mitigation_day < -self.i_day:
mitigation_day = -self.i_day

total_days = self.i_day + p.n_days + PREFIT_ADDITIONAL_DAYS
pre_mitigation_days = self.i_day + mitigation_day
post_mitigation_days = total_days - pre_mitigation_days

raw = self.run_projection(p, [
(self.beta, pre_mitigation_days),
(self.beta_t, post_mitigation_days),
]
)

# Don't fit against results that put the peak before the present day
if raw["census_hospitalized"].argmax() < i_day:
if raw["census_hospitalized"].argmax() < self.i_day:
continue

loss = get_loss(raw["census_hospitalized"][i_day], p.current_hospitalized)
loss = get_loss(raw["census_hospitalized"][self.i_day], p.current_hospitalized)
if loss < best_i_day_loss:
best_i_day_loss = loss
best_i_day = i_day
self.raw = raw
best_i_day = self.i_day

self.i_day = best_i_day
self.raw = self.run_projection(p, self.get_policies(p))

logger.info(
'Estimated date_first_hospitalized: %s; current_date: %s; i_day: %s',
Expand Down Expand Up @@ -127,7 +139,7 @@ def __init__(self, p: Parameters):
intrinsic_growth_rate = get_growth_rate(p.doubling_time)
self.beta = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, 0.0)
self.beta_t = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, p.relative_contact_rate)
self.raw = self.run_projection(p, self.gen_policy(p))
self.raw = self.run_projection(p, self.get_policies(p))

self.population = p.population
else:
Expand Down Expand Up @@ -162,6 +174,15 @@ def __init__(self, p: Parameters):
'census_icu': self.raw['census_icu'],
'census_ventilated': self.raw['census_ventilated'],
})
self.ppe_df = pd.DataFrame(data={
'day': self.raw['day'],
'date': self.raw['date'],
'census_hospitalized': self.raw['census_hospitalized'],
'census_icu': self.raw['census_icu'],
'census_ventilated': self.raw['census_ventilated'],
'admits_hospitalized': self.raw['admits_hospitalized'],
})
self.ppe_df = self.ppe_df[self.ppe_df['day']>=0]

logger.info('len(np.arange(-i_day, n_days+1)): %s', len(np.arange(-self.i_day, p.n_days+1)))
logger.info('len(raw_df): %s', len(self.raw_df))
Expand Down Expand Up @@ -195,9 +216,9 @@ def get_argmin_doubling_time(self, p: Parameters, dts):
intrinsic_growth_rate = get_growth_rate(i_dt)
self.beta = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, 0.0)
self.beta_t = get_beta(intrinsic_growth_rate, self.gamma, self.susceptible, p.relative_contact_rate)
raw = self.run_projection(p, self.gen_policy(p))

raw = self.run_projection(p, self.get_policies(p))

# Skip values the would put the fit past peak
peak_admits_day = raw["admits_hospitalized"].argmax()
if peak_admits_day < 0:
Expand All @@ -210,7 +231,7 @@ def get_argmin_doubling_time(self, p: Parameters, dts):
min_loss = pd.Series(losses).argmin()
return min_loss

def gen_policy(self, p: Parameters) -> Sequence[Tuple[float, int]]:
def get_policies(self, p: Parameters) -> Sequence[Tuple[float, int]]:
if p.mitigation_date is not None:
mitigation_day = -(p.current_date - p.mitigation_date).days
else:
Expand Down
Loading

0 comments on commit 108c24d

Please sign in to comment.