diff --git a/.gitignore b/.gitignore index 6823cfc9..c8c3a87b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Editors .vscode/ +.idea/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/Pipfile b/Pipfile index 89e80402..ed939580 100644 --- a/Pipfile +++ b/Pipfile @@ -9,7 +9,9 @@ verify_ssl = true streamlit = "*" pandas = "*" numpy = "*" +python-i18n = "*" altair = "*" +pyyaml = "*" [requires] python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock index 0ee34d51..55856c4e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ef0a22a90dfc91be4e5b17baca660f77ec37da86f1ace30e9b46df5f60562a13" + "sha256": "e4a1f7d9acc2423650df12ac13cf168ba087f6ad37c765a6071f535dad838525" }, "pipfile-spec": 6, "requires": { @@ -24,6 +24,14 @@ "index": "pypi", "version": "==4.0.1" }, + "appnope": { + "hashes": [ + "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", + "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" + ], + "markers": "platform_system == 'Darwin'", + "version": "==0.1.0" + }, "astor": { "hashes": [ "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", @@ -67,17 +75,17 @@ }, "boto3": { "hashes": [ - "sha256:172f07391ad6bd261336cddaf988944b2d8e528a7c036d82c7841676cbc935ec", - "sha256:e3cf4a0e1a6d79d2761b6ab050cb897a4e753f625524d15c01540f9522a11874" + "sha256:0080ea1cce4c87d9f1e4e47fed4b994451b0022fcf6e120d1fb653eb3874506d", + "sha256:a3a89a254b068dbcc23298dea11d15b9e6e19f825290ebe43fcf4ccbbe423994" ], - "version": "==1.12.22" + "version": "==1.12.23" }, "botocore": { "hashes": [ - "sha256:ac6b6ae659b797f7bb106a7bee5ff1c7de9ece85e542a334a1cd618f9f2782cc", - "sha256:b8b399828d6dfe8ee879c22aa313591c183b78a8733df437ba84d9f39cd1b219" + "sha256:5b6bf68bd484fbf83e27f56a0a92e38cee223b689121c1184a0292311b88f888", + "sha256:e4fc86574043b20267f863b9168bd1c9ed50a53f0f7222ce8c99f64355174515" ], - "version": "==1.15.22" + "version": "==1.15.23" }, "certifi": { "hashes": [ @@ -298,9 +306,14 @@ "numpy": { "hashes": [ "sha256:1598a6de323508cfeed6b7cd6c4efb43324f4692e20d1f76e1feec7f59013448", + "sha256:1b0ece94018ae21163d1f651b527156e1f03943b986188dd81bc7e066eae9d1c", "sha256:2e40be731ad618cb4974d5ba60d373cdf4f1b8dcbf1dcf4d9dff5e212baf69c5", + "sha256:4ba59db1fcc27ea31368af524dcf874d9277f21fd2e1f7f1e2e0c75ee61419ed", + "sha256:59ca9c6592da581a03d42cc4e270732552243dc45e87248aa8d636d53812f6a5", + "sha256:5e0feb76849ca3e83dd396254e47c7dba65b3fa9ed3df67c2556293ae3e16de3", "sha256:6d205249a0293e62bbb3898c4c2e1ff8a22f98375a34775a259a0523111a8f6c", "sha256:6fcc5a3990e269f86d388f165a089259893851437b904f422d301cdce4ff25c8", + "sha256:82847f2765835c8e5308f136bc34018d09b49037ec23ecc42b246424c767056b", "sha256:87902e5c03355335fc5992a74ba0247a70d937f326d852fc613b7f53516c0963", "sha256:9ab21d1cb156a620d3999dd92f7d1c86824c622873841d6b080ca5495fa10fef", "sha256:a1baa1dc8ecd88fb2d2a651671a84b9938461e8a8eed13e2f0a812a94084d1fa", @@ -308,9 +321,11 @@ "sha256:a35af656a7ba1d3decdd4fae5322b87277de8ac98b7d9da657d9e212ece76a61", "sha256:b1fe1a6f3a6f355f6c29789b5927f8bd4f134a4bd9a781099a7c4f66af8850f5", "sha256:b5ad0adb51b2dee7d0ee75a69e9871e2ddfb061c73ea8bc439376298141f77f5", + "sha256:ba3c7a2814ec8a176bb71f91478293d633c08582119e713a0c5351c0f77698da", "sha256:cd77d58fb2acf57c1d1ee2835567cd70e6f1835e32090538f17f8a3a99e5e34b", "sha256:cdb3a70285e8220875e4d2bc394e49b4988bdb1298ffa4e0bd81b2f613be397c", - "sha256:deb529c40c3f1e38d53d5ae6cd077c21f1d49e13afc7936f7f868455e16b64a0" + "sha256:deb529c40c3f1e38d53d5ae6cd077c21f1d49e13afc7936f7f868455e16b64a0", + "sha256:e7894793e6e8540dbeac77c87b489e331947813511108ae097f1715c018b8f3d" ], "index": "pypi", "version": "==1.18.2" @@ -467,6 +482,14 @@ ], "version": "==2.8.0" }, + "python-i18n": { + "hashes": [ + "sha256:095cb67a449f9caaddb4ca034255dcb442b1d1aca874294eda537bb5ea919892", + "sha256:85954be9c16b53221b42ebdf5164d6e6fef80f2dd0e388c7a468687330bba1d3" + ], + "index": "pypi", + "version": "==0.3.7" + }, "pytz": { "hashes": [ "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", @@ -474,6 +497,23 @@ ], "version": "==2019.3" }, + "pyyaml": { + "hashes": [ + "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", + "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", + "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", + "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", + "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", + "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", + "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", + "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", + "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", + "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", + "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" + ], + "index": "pypi", + "version": "==5.3" + }, "pyzmq": { "hashes": [ "sha256:0bbc1728fe4314b4ca46249c33873a390559edac7c217ec7001b5e0c34a8fb7f", diff --git a/app.py b/app.py index fb5d4967..e5db0184 100644 --- a/app.py +++ b/app.py @@ -4,6 +4,7 @@ import streamlit as st import numpy as np import altair as alt +import i18n hide_menu_style = """ """ st.markdown(hide_menu_style, unsafe_allow_html=True) +i18n.set('filename_format', '{locale}.{format}') +i18n.set('locale', 'en') +i18n.set('fallback', 'en') +i18n.load_path.append('./locales') delaware = 564696 chester = 519293 @@ -23,42 +28,41 @@ # Widgets current_hosp = st.sidebar.number_input( - "Currently Hospitalized COVID-19 Patients", value=known_cases, step=1, format="%i" + i18n.t("Currently Hospitalized COVID-19 Patients"), value=known_cases, step=1, format="%i" ) - doubling_time = st.sidebar.number_input( - "Doubling time before social distancing (days)", value=6, step=1, format="%i" + i18n.t("Doubling time before social distancing (days)"), value=6, step=1, format="%i" ) relative_contact_rate = st.sidebar.number_input( - "Social distancing (% reduction in social contact)", 0, 100, value=0, step=5, format="%i" + i18n.t("Social distancing (% reduction in social contact)"), 0, 100, value=0, step=5, format="%i" )/100.0 hosp_rate = ( - st.sidebar.number_input("Hospitalization %(total infections)", 0.0, 100.0, value=5.0, step=1.0, format="%f") + st.sidebar.number_input(i18n.t("Hospitalization %(total infections)"), 0.0, 100.0, value=5.0, step=1.0, format="%f") / 100.0 ) icu_rate = ( - st.sidebar.number_input("ICU %(total infections)", 0.0, 100.0, value=2.0, step=1.0, format="%f") / 100.0 + st.sidebar.number_input(i18n.t("ICU %(total infections)"), 0.0, 100.0, value=2.0, step=1.0, format="%f") / 100.0 ) vent_rate = ( - st.sidebar.number_input("Ventilated %(total infections)", 0.0, 100.0, value=1.0, step=1.0, format="%f") + st.sidebar.number_input(i18n.t("Ventilated %(total infections)"), 0.0, 100.0, value=1.0, step=1.0, format="%f") / 100.0 ) -hosp_los = st.sidebar.number_input("Hospital Length of Stay", value=7, step=1, format="%i") -icu_los = st.sidebar.number_input("ICU Length of Stay", value=9, step=1, format="%i") -vent_los = st.sidebar.number_input("Vent Length of Stay", value=10, step=1, format="%i") +hosp_los = st.sidebar.number_input(i18n.t("Hospital Length of Stay"), value=7, step=1, format="%i") +icu_los = st.sidebar.number_input(i18n.t("ICU Length of Stay"), value=9, step=1, format="%i") +vent_los = st.sidebar.number_input(i18n.t("Vent Length of Stay"), value=10, step=1, format="%i") Penn_market_share = ( st.sidebar.number_input( - "Hospital Market Share (%)", 0.0, 100.0, value=15.0, step=1.0, format="%f" + i18n.t("Hospital Market Share (%)"), 0.0, 100.0, value=15.0, step=1.0, format="%f" ) / 100.0 ) S = st.sidebar.number_input( - "Regional Population", value=S_default, step=100000, format="%i" + i18n.t("Regional Population"), value=S_default, step=100000, format="%i" ) initial_infections = st.sidebar.number_input( - "Currently Known Regional Infections (only used to compute detection rate - does not change projections)", value=known_infections, step=10, format="%i" + i18n.t("Currently Known Regional Infections (only used to compute detection rate - does not change projections)"), value=known_infections, step=10, format="%i" ) total_infections = current_hosp / Penn_market_share / hosp_rate @@ -82,32 +86,11 @@ doubling_time_t = 1/np.log2(beta*S - gamma +1) # doubling time after distancing def head(): - st.markdown(""" - - -
- """, unsafe_allow_html=True) - st.markdown( - """*This tool was developed by the [Predictive Healthcare team](http://predictivehealthcare.pennmedicine.org/) at - Penn Medicine. For questions and comments please see our - [contact page](http://predictivehealthcare.pennmedicine.org/contact/). Code can be found on [Github](https://github.com/pennsignals/chime). - Join our [Slack channel](https://codeforphilly.org/chat?channel=covid19-chime-penn) if you would like to get involved!*""") + st.markdown(i18n.t("Penn Medicine - COVID-19 Hospital Impact Model for Epidemics"), unsafe_allow_html=True) + st.markdown(i18n.t("This tool was developed by...")) st.markdown( - """The estimated number of currently infected individuals is **{total_infections:.0f}**. The **{initial_infections}** - confirmed cases in the region imply a **{detection_prob:.0%}** rate of detection. This is based on current inputs for - Hospitalizations (**{current_hosp}**), Hospitalization rate (**{hosp_rate:.0%}**), Region size (**{S}**), - and Hospital market share (**{Penn_market_share:.0%}**). - -An initial doubling time of **{doubling_time}** days and a recovery time of **{recovery_days}** days imply an $R_0$ of -**{r_naught:.2f}**. - -**Mitigation**: A **{relative_contact_rate:.0%}** reduction in social contact after the onset of the outbreak reduces the doubling time to **{doubling_time_t:.1f}** days, implying an effective $R_t$ of **${r_t:.2f}$**. -""".format( + i18n.t("The estimated number of currently infected...").format( total_infections=total_infections, initial_infections=initial_infections, detection_prob=detection_prob, @@ -132,67 +115,25 @@ def show_more_info_about_this_tool(): """a lot of streamlit writing to screen.""" st.subheader( - "[Discrete-time SIR modeling](https://mathworld.wolfram.com/SIRModel.html) of infections/recovery" + i18n.t("Discrete-time SIR modeling") ) st.markdown( - """The model consists of individuals who are either _Susceptible_ ($S$), _Infected_ ($I$), or _Recovered_ ($R$). - -The epidemic proceeds via a growth and decline process. This is the core model of infectious disease spread and has been in use in epidemiology for many years.""" + i18n.t("The model consists of individuals who are either...") ) - st.markdown("""The dynamics are given by the following 3 equations.""") + st.markdown(i18n.t("The dynamics are given by the following 3 equations.")) st.latex("S_{t+1} = (-\\beta S_t I_t) + S_t") st.latex("I_{t+1} = (\\beta S_t I_t - \\gamma I_t) + I_t") st.latex("R_{t+1} = (\\gamma I_t) + R_t") st.markdown( - """To project the expected impact to Penn Medicine, we estimate the terms of the model. - -To do this, we use a combination of estimates from other locations, informed estimates based on logical reasoning, and best guesses from the American Hospital Association. - - -### Parameters - -The model's parameters, $\\beta$ and $\\gamma$, determine the virulence of the epidemic. - -$$\\beta$$ can be interpreted as the _effective contact rate_: -""") + i18n.t("To project the expected impact to Penn Medicine...") + ) st.latex("\\beta = \\tau \\times c") - st.markdown( -"""which is the transmissibility ($\\tau$) multiplied by the average number of people exposed ($$c$$). The transmissibility is the basic virulence of the pathogen. The number of people exposed $c$ is the parameter that can be changed through social distancing. - - -$\\gamma$ is the inverse of the mean recovery time, in days. I.e.: if $\\gamma = 1/{recovery_days}$, then the average infection will clear in {recovery_days} days. - -An important descriptive parameter is the _basic reproduction number_, or $R_0$. This represents the average number of people who will be infected by any given infected person. When $R_0$ is greater than 1, it means that a disease will grow. Higher $R_0$'s imply more rapid growth. It is defined as """.format(recovery_days=int(recovery_days) , c='c')) + st.markdown(i18n.t("which is the transmissibility multiplied...").format(recovery_days=int(recovery_days) , c='c')) st.latex("R_0 = \\beta /\\gamma") - - st.markdown(""" - -$R_0$ gets bigger when - -- there are more contacts between people -- when the pathogen is more virulent -- when people have the pathogen for longer periods of time - -A doubling time of {doubling_time} days and a recovery time of {recovery_days} days imply an $R_0$ of {r_naught:.2f}. - -#### Effect of social distancing - -After the beginning of the outbreak, actions to reduce social contact will lower the parameter $c$. If this happens at -time $t$, then the number of people infected by any given infected person is $R_t$, which will be lower than $R_0$. - -A {relative_contact_rate:.0%} reduction in social contact would increase the time it takes for the outbreak to double, -to {doubling_time_t:.2f} days from {doubling_time:.2f} days, with a $R_t$ of {r_t:.2f}. - -#### Using the model - -We need to express the two parameters $\\beta$ and $\\gamma$ in terms of quantities we can estimate. - -- $\\gamma$: the CDC is recommending 14 days of self-quarantine, we'll use $\\gamma = 1/{recovery_days}$. -- To estimate $$\\beta$$ directly, we'd need to know transmissibility and social contact rates. since we don't know these things, we can extract it from known _doubling times_. The AHA says to expect a doubling time $T_d$ of 7-10 days. That means an early-phase rate of growth can be computed by using the doubling time formula: -""".format(doubling_time=doubling_time, + st.markdown(i18n.t("$R_0$ gets bigger when...").format(doubling_time=doubling_time, recovery_days=recovery_days, r_naught=r_naught, relative_contact_rate=relative_contact_rate, @@ -202,19 +143,7 @@ def show_more_info_about_this_tool(): st.latex("g = 2^{1/T_d} - 1") st.markdown( - """ -- Since the rate of new infections in the SIR model is $g = \\beta S - \\gamma$, and we've already computed $\\gamma$, $\\beta$ becomes a function of the initial population size of susceptible individuals. -$$\\beta = (g + \\gamma)$$. - - -### Initial Conditions - -- The total size of the susceptible population will be the entire catchment area for Penn Medicine entities (HUP, PAH, PMC, CCH) - - Delaware = {delaware} - - Chester = {chester} - - Montgomery = {montgomery} - - Bucks = {bucks} - - Philly = {philly}""".format( + i18n.t("Since the rate of new infections in the SIR model...").format( delaware=delaware, chester=chester, montgomery=montgomery, @@ -224,7 +153,7 @@ def show_more_info_about_this_tool(): ) return None -if st.checkbox("Show more info about this tool"): +if st.checkbox(i18n.t("Show more info about this tool")): show_more_info_about_this_tool() # The SIR model, one time step @@ -261,7 +190,7 @@ def sim_sir(S, I, R, beta, gamma, n_days, beta_decay=None): return s, i, r -n_days = st.slider("Number of days to project", 30, 200, 60, 1, "%i") +n_days = st.slider(i18n.t("Number of days to project"), 30, 200, 60, 1, "%i") beta_decay = 0.0 s, i, r = sim_sir(S, I, R, beta, gamma, n_days, beta_decay=beta_decay) @@ -277,8 +206,8 @@ def sim_sir(S, I, R, beta, gamma, n_days, beta_decay=None): projection = pd.DataFrame.from_dict(data_dict) -st.subheader("New Admissions") -st.markdown("Projected number of **daily** COVID-19 admissions at Penn hospitals") +st.subheader(i18n.t("New Admissions")) +st.markdown(i18n.t("Projected number of **daily** COVID-19 admissions at Penn hospitals")) # New cases projection_admits = projection.iloc[:-1, :] - projection.shift(1) @@ -290,15 +219,15 @@ def sim_sir(S, I, R, beta, gamma, n_days, beta_decay=None): def new_admissions_chart(projection_admits: pd.DataFrame, plot_projection_days: int) -> alt.Chart: """docstring""" - projection_admits = projection_admits.rename(columns={"hosp": "Hospitalized", "icu": "ICU", "vent": "Ventilated"}) + projection_admits = projection_admits.rename(columns={"hosp": i18n.t("Hospitalized"), "icu": i18n.t("ICU"), "vent": i18n.t("Ventilated")}) return ( alt .Chart(projection_admits.head(plot_projection_days)) - .transform_fold(fold=["Hospitalized", "ICU", "Ventilated"]) + .transform_fold(fold=[i18n.t("Hospitalized"), i18n.t("ICU"), i18n.t("Ventilated")]) .mark_line(point=True) .encode( - x=alt.X("day", title="Days from today"), - y=alt.Y("value:Q", title="Daily admissions"), + x=alt.X("day", title=i18n.t("Days from today")), + y=alt.Y("value:Q", title=i18n.t("Daily admissions")), color="key:N", tooltip=["day", "key:N"] ) @@ -309,17 +238,16 @@ def new_admissions_chart(projection_admits: pd.DataFrame, plot_projection_days: -if st.checkbox("Show Projected Admissions in tabular form"): +if st.checkbox(i18n.t("Show Projected Admissions in tabular form")): admits_table = projection_admits[np.mod(projection_admits.index, 7) == 0].copy() admits_table["day"] = admits_table.index admits_table.index = range(admits_table.shape[0]) admits_table = admits_table.fillna(0).astype(int) - st.dataframe(admits_table) -st.subheader("Admitted Patients (Census)") +st.subheader(i18n.t("Admitted Patients (Census)")) st.markdown( - "Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals" + i18n.t("Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals") ) def _census_table(projection_admits, hosp_los, icu_los, vent_los) -> pd.DataFrame: @@ -355,16 +283,16 @@ def _census_table(projection_admits, hosp_los, icu_los, vent_los) -> pd.DataFram def admitted_patients_chart(census: pd.DataFrame) -> alt.Chart: """docstring""" - census = census.rename(columns={"hosp": "Hospital Census", "icu": "ICU Census", "vent": "Ventilated Census"}) + census = census.rename(columns={"hosp": i18n.t("Hospital Census"), "icu": i18n.t("ICU Census"), "vent": i18n.t("Ventilated Census")}) return ( alt .Chart(census) - .transform_fold(fold=["Hospital Census", "ICU Census", "Ventilated Census"]) + .transform_fold(fold=[i18n.t("Hospital Census"), i18n.t("ICU Census"), i18n.t("Ventilated Census")]) .mark_line(point=True) .encode( - x=alt.X("day", title="Days from today"), - y=alt.Y("value:Q", title="Census"), + x=alt.X("day", title=i18n.t("Days from today")), + y=alt.Y("value:Q", title=i18n.t("Census")), color="key:N", tooltip=["day", "key:N"] ) @@ -373,20 +301,20 @@ def admitted_patients_chart(census: pd.DataFrame) -> alt.Chart: st.altair_chart(admitted_patients_chart(census_table), use_container_width=True) -if st.checkbox("Show Projected Census in tabular form"): +if st.checkbox(i18n.t("Show Projected Census in tabular form")): st.dataframe(census_table) def additional_projections_chart(i: np.ndarray, r: np.ndarray) -> alt.Chart: - dat = pd.DataFrame({"Infected": i, "Recovered": r}) + dat = pd.DataFrame({i18n.t("Infected"): i, i18n.t("Recovered"): r}) return ( alt .Chart(dat.reset_index()) - .transform_fold(fold=["Infected", "Recovered"]) + .transform_fold(fold=[i18n.t("Infected"), i18n.t("Recovered")]) .mark_line() .encode( - x=alt.X("index", title="Days from today"), - y=alt.Y("value:Q", title="Case Volume"), + x=alt.X("index", title=i18n.t("Days from today")), + y=alt.Y("value:Q", title=i18n.t("Case Volume")), tooltip=["key:N", "value:Q"], color="key:N" ) @@ -394,17 +322,17 @@ def additional_projections_chart(i: np.ndarray, r: np.ndarray) -> alt.Chart: ) st.markdown( - """**Click the checkbox below to view additional data generated by this simulation**""" + i18n.t("**Click the checkbox below to view additional data generated by this simulation**") ) def show_additional_projections(): st.subheader( - "The number of infected and recovered individuals in the hospital catchment region at any given moment" + i18n.t("The number of infected and recovered individuals in the hospital catchment region at any given moment") ) st.altair_chart(additional_projections_chart(i, r), use_container_width=True) - if st.checkbox("Show Raw SIR Similation Data"): + if st.checkbox(i18n.t("Show Raw SIR Similation Data")): # Show data days = np.array(range(0, n_days + 1)) data_list = [days, s, i, r] @@ -415,31 +343,19 @@ def show_additional_projections(): st.dataframe(infect_table) -if st.checkbox("Show Additional Projections"): +if st.checkbox(i18n.t("Show Additional Projections")): show_additional_projections() # Definitions and footer -st.subheader("Guidance on Selecting Inputs") +st.subheader(i18n.t("Guidance on Selecting Inputs")) st.markdown( - """* **Hospitalized COVID-19 Patients:** The number of patients currently hospitalized with COVID-19 **at your hospital(s)**. This number is used in conjunction with Hospital Market Share and Hospitalization % to estimate the total number of infected individuals in your region. -* **Doubling Time (days):** This parameter drives the rate of new cases during the early phases of the outbreak. The American Hospital Association currently projects doubling rates between 7 and 10 days. This is the doubling time you expect under status quo conditions. To account for reduced contact and other public health interventions, modify the _Social distancing_ input. -* **Social distancing (% reduction in person-to-person physical contact):** This parameter allows users to explore how reduction in interpersonal contact & transmission (hand-washing) might slow the rate of new infections. It is your estimate of how much social contact reduction is being achieved in your region relative to the status quo. While it is unclear how much any given policy might affect social contact (eg. school closures or remote work), this parameter lets you see how projections change with percentage reductions in social contact. -* **Hospitalization %(total infections):** Percentage of **all** infected cases which will need hospitalization. -* **ICU %(total infections):** Percentage of **all** infected cases which will need to be treated in an ICU. -* **Ventilated %(total infections):** Percentage of **all** infected cases which will need mechanical ventilation. -* **Hospital Length of Stay:** Average number of days of treatment needed for hospitalized COVID-19 patients. -* **ICU Length of Stay:** Average number of days of ICU treatment needed for ICU COVID-19 patients. -* **Vent Length of Stay:** Average number of days of ventilation needed for ventilated COVID-19 patients. -* **Hospital Market Share (%):** The proportion of patients in the region that are likely to come to your hospital (as opposed to other hospitals in the region) when they get sick. One way to estimate this is to look at all of the hospitals in your region and add up all of the beds. The number of beds at your hospital divided by the total number of beds in the region times 100 will give you a reasonable starting estimate. -* **Regional Population:** Total population size of the catchment region of your hospital(s). -* **Currently Known Regional Infections**: The number of infections reported in your hospital's catchment region. This is only used to compute detection rate - **it will not change projections**. This input is used to estimate the detection rate of infected individuals. - """ + i18n.t("**Hospitalized COVID-19 Patients:**...") ) -st.subheader("References & Acknowledgements") +st.subheader(i18n.t("References & Acknowledgements")) st.markdown( """* AHA Webinar, Feb 26, James Lawler, MD, an associate professor University of Nebraska Medical Center, What Healthcare Leaders Need To Know: Preparing for the COVID-19 * We would like to recognize the valuable assistance in consultation and review of model assumptions by Michael Z. Levy, PhD, Associate Professor of Epidemiology, Department of Biostatistics, Epidemiology and Informatics at the Perelman School of Medicine diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 00000000..8a9a6f1a --- /dev/null +++ b/locales/en.yml @@ -0,0 +1,135 @@ +en: + "Currently Known Regional Infections (only used to compute detection rate - does not change projections)": "Currently Known Regional Infections (only used to compute detection rate - does not change projections)" + "Currently Hospitalized COVID-19 Patients": "Currently Hospitalized COVID-19 Patients" + "Doubling time before social distancing (days)": "Doubling time before social distancing (days)" + "Social distancing (% reduction in social contact)": "Social distancing (% reduction in social contact)" + "Hospitalization %(total infections)": "Hospitalization %(total infections)" + "ICU %(total infections)": "ICU %(total infections)" + "Ventilated %(total infections)": "Ventilated %(total infections)" + "Hospital Length of Stay": "Hospital Length of Stay" + "ICU Length of Stay": "ICU Length of Stay" + "Vent Length of Stay": "Vent Length of Stay" + "Hospital Market Share (%)": "Hospital Market Share (%)" + "Regional Population": "Regional Population" + "Penn Medicine - COVID-19 Hospital Impact Model for Epidemics": |+ + + + + "This tool was developed by...": |+ + *This tool was developed by the [Predictive Healthcare team](http://predictivehealthcare.pennmedicine.org/) at + Penn Medicine. For questions and comments please see our + [contact page](http://predictivehealthcare.pennmedicine.org/contact/). Code can be found on [Github](https://github.com/pennsignals/chime). + Join our [Slack channel](https://codeforphilly.org/chat?channel=covid19-chime-penn) if you would like to get involved!* + "The estimated number of currently infected...": |+ + The estimated number of currently infected individuals is **{total_infections:.0f}**. The **{initial_infections}** + confirmed cases in the region imply a **{detection_prob:.0%}** rate of detection. This is based on current inputs for + Hospitalizations (**{current_hosp}**), Hospitalization rate (**{hosp_rate:.0%}**), Region size (**{S}**), + and Hospital market share (**{Penn_market_share:.0%}**). + + An initial doubling time of **{doubling_time}** days and a recovery time of **{recovery_days}** days imply an $R_0$ of + **{r_naught:.2f}**. + + **Mitigation**: A **{relative_contact_rate:.0%}** reduction in social contact after the onset of the outbreak reduces the doubling time to **{doubling_time_t:.1f}** days, implying an effective $R_t$ of **${r_t:.2f}$**. + "Show more info about this tool": "Show more info about this tool" + "Discrete-time SIR modeling": "[Discrete-time SIR modeling](https://mathworld.wolfram.com/SIRModel.html) of infections/recovery" + "The model consists of individuals who are either...": |+ + The model consists of individuals who are either _Susceptible_ ($S$), _Infected_ ($I$), or _Recovered_ ($R$). + + The epidemic proceeds via a growth and decline process. This is the core model of infectious disease spread and has been in use in epidemiology for many years. + "The dynamics are given by the following 3 equations.": "The dynamics are given by the following 3 equations." + "To project the expected impact to Penn Medicine...": |+ + To project the expected impact to Penn Medicine, we estimate the terms of the model. + + To do this, we use a combination of estimates from other locations, informed estimates based on logical reasoning, and best guesses from the American Hospital Association. + + + ### Parameters + + The model's parameters, $\beta$ and $\gamma$, determine the virulence of the epidemic. + + $$\beta$$ can be interpreted as the _effective contact rate_: + "which is the transmissibility multiplied...": |+ + which is the transmissibility ($\tau$) multiplied by the average number of people exposed ($$c$$). The transmissibility is the basic virulence of the pathogen. The number of people exposed $c$ is the parameter that can be changed through social distancing. + + $\gamma$ is the inverse of the mean recovery time, in days. I.e.: if $\gamma = 1/{recovery_days}$, then the average infection will clear in {recovery_days} days. + + An important descriptive parameter is the _basic reproduction number_, or $R_0$. This represents the average number of people who will be infected by any given infected person. When $R_0$ is greater than 1, it means that a disease will grow. Higher $R_0$'s imply more rapid growth. It is defined as + "$R_0$ gets bigger when...": |+ + + $R_0$ gets bigger when + + - there are more contacts between people + - when the pathogen is more virulent + - when people have the pathogen for longer periods of time + + A doubling time of {doubling_time} days and a recovery time of {recovery_days} days imply an $R_0$ of {r_naught:.2f}. + + #### Effect of social distancing + + After the beginning of the outbreak, actions to reduce social contact will lower the parameter $c$. If this happens at + time $t$, then the number of people infected by any given infected person is $R_t$, which will be lower than $R_0$. + + A {relative_contact_rate:.0%} reduction in social contact would increase the time it takes for the outbreak to double, + to {doubling_time_t:.2f} days from {doubling_time:.2f} days, with a $R_t$ of {r_t:.2f}. + + #### Using the model + + We need to express the two parameters $\beta$ and $\gamma$ in terms of quantities we can estimate. + + - $\gamma$: the CDC is recommending 14 days of self-quarantine, we'll use $\gamma = 1/{recovery_days}$. + - To estimate $$\beta$$ directly, we'd need to know transmissibility and social contact rates. since we don't know these things, we can extract it from known _doubling times_. The AHA says to expect a doubling time $T_d$ of 7-10 days. That means an early-phase rate of growth can be computed by using the doubling time formula: + "Since the rate of new infections in the SIR model...": |+ + - Since the rate of new infections in the SIR model is $g = \beta S - \gamma$, and we've already computed $\gamma$, $\beta$ becomes a function of the initial population size of susceptible individuals. + $$\beta = (g + \gamma)$$. + + + ### Initial Conditions + + - The total size of the susceptible population will be the entire catchment area for Penn Medicine entities (HUP, PAH, PMC, CCH) + - Delaware = {delaware} + - Chester = {chester} + - Montgomery = {montgomery} + - Bucks = {bucks} + - Philly = {philly} + "Number of days to project": "Number of days to project" + "New Admissions": "New Admissions" + "Projected number of **daily** COVID-19 admissions at Penn hospitals": "Projected number of **daily** COVID-19 admissions at Penn hospitals" + "Hospitalized": "Hospitalized" + "ICU": "ICU" + "Ventilated": "Ventilated" + "Days from today": "Days from today" + "Daily admissions": "Daily admissions" + "Show Projected Admissions in tabular form": "Show Projected Admissions in tabular form" + "Admitted Patients (Census)": "Admitted Patients (Census)" + "Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals": "Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals" + "Hospital Census": "Hospital Census" + "ICU Census": "ICU Census" + "Ventilated Census": "Ventilated Census" + "Census": "Census" + "Show Projected Census in tabular form": "Show Projected Census in tabular form" + "Infected": "Infected" + "Recovered": "Recovered" + "Case Volume": "Case Volume" + "**Click the checkbox below to view additional data generated by this simulation**": "**Click the checkbox below to view additional data generated by this simulation**" + "The number of infected and recovered individuals in the hospital catchment region at any given moment": "The number of infected and recovered individuals in the hospital catchment region at any given moment" + "Show Raw SIR Similation Data": "Show Raw SIR Similation Data" + "Show Additional Projections": "Show Additional Projections" + "Guidance on Selecting Inputs": "Guidance on Selecting Inputs" + "**Hospitalized COVID-19 Patients:**...": |+ + * **Hospitalized COVID-19 Patients:** The number of patients currently hospitalized with COVID-19 **at your hospital(s)**. This number is used in conjunction with Hospital Market Share and Hospitalization % to estimate the total number of infected individuals in your region. + * **Doubling Time (days):** This parameter drives the rate of new cases during the early phases of the outbreak. The American Hospital Association currently projects doubling rates between 7 and 10 days. This is the doubling time you expect under status quo conditions. To account for reduced contact and other public health interventions, modify the _Social distancing_ input. + * **Social distancing (% reduction in person-to-person physical contact):** This parameter allows users to explore how reduction in interpersonal contact & transmission (hand-washing) might slow the rate of new infections. It is your estimate of how much social contact reduction is being achieved in your region relative to the status quo. While it is unclear how much any given policy might affect social contact (eg. school closures or remote work), this parameter lets you see how projections change with percentage reductions in social contact. + * **Hospitalization %(total infections):** Percentage of **all** infected cases which will need hospitalization. + * **ICU %(total infections):** Percentage of **all** infected cases which will need to be treated in an ICU. + * **Ventilated %(total infections):** Percentage of **all** infected cases which will need mechanical ventilation. + * **Hospital Length of Stay:** Average number of days of treatment needed for hospitalized COVID-19 patients. + * **ICU Length of Stay:** Average number of days of ICU treatment needed for ICU COVID-19 patients. + * **Vent Length of Stay:** Average number of days of ventilation needed for ventilated COVID-19 patients. + * **Hospital Market Share (%):** The proportion of patients in the region that are likely to come to your hospital (as opposed to other hospitals in the region) when they get sick. One way to estimate this is to look at all of the hospitals in your region and add up all of the beds. The number of beds at your hospital divided by the total number of beds in the region times 100 will give you a reasonable starting estimate. + * **Regional Population:** Total population size of the catchment region of your hospital(s). + * **Currently Known Regional Infections**: The number of infections reported in your hospital's catchment region. This is only used to compute detection rate - **it will not change projections**. This input is used to estimate the detection rate of infected individuals. + "References & Acknowledgements": "References & Acknowledgements" \ No newline at end of file diff --git a/locales/ja.yml b/locales/ja.yml new file mode 100644 index 00000000..8ddb9146 --- /dev/null +++ b/locales/ja.yml @@ -0,0 +1,133 @@ +ja: + "Currently Known Regional Infections (only used to compute detection rate - does not change projections)": "現在知られている地域の感染人数 (検出率産出のみに用いられ、予測に影響しません)" + "Currently Hospitalized COVID-19 Patients": "現在入院中のCOVID-19患者数" + "Doubling time before social distancing (days)": "社会的隔離を行う前の患者倍加時間 (日数)" + "Social distancing (% reduction in social contact)": "社会的隔離 (社会的接触の減少割合 %)" + "Hospitalization %(total infections)": "入院患者数 %(総感染者数中)" + "ICU %(total infections)": "集中治療室患者数 %(総感染者数中)" + "Ventilated %(total infections)": "人工呼吸器患者数 %(総感染者数中)" + "Hospital Length of Stay": "平均入院日数" + "ICU Length of Stay": "平均集中治療室利用日数" + "Vent Length of Stay": "平均人工呼吸器利用日数" + "Hospital Market Share (%)": "地域での病院の患者シェア (%)" + "Regional Population": "地域の人口" + "Penn Medicine - COVID-19 Hospital Impact Model for Epidemics": |+ + + + + "This tool was developed by...": |+ + *このツールはペンシルバニア州立大学の医療部門であるPenn Medicineの、[予測医療チーム](http://predictivehealthcare.pennmedicine.org/)によって開発されました。 + 質問やコメントは我々の[コンタクトページ](http://predictivehealthcare.pennmedicine.org/contact/)で受け付けています(英語のみ)。 + ソースコードは[Github](https://github.com/pennsignals/chime)で配布されています。 + 開発に参加したい場合は是非我々の[Slackチャンネル](https://codeforphilly.org/chat?channel=covid19-chime-penn)に参加してください!* + "The estimated number of currently infected...": |+ + 現在の予測感染者人数は**{total_infections:.0f}**人です。 + この地域で確認された感染事例は**{initial_infections}**例で、検出率は**{detection_prob:.0%}**にあたります。 + この結果は入院患者数(**{current_hosp}**)、患者の入院割合(**{hosp_rate:.0%}**)、地域の人口(**{S}**)および病院の地域での患者シェア(**{Penn_market_share:.0%}**)を元に計算されています。 + + 初期倍加時間が**{doubling_time}**日で、回復時間が**{recovery_days}**であれば、$R_0$は**{r_naught:.2f}**となります。 + + **緩和**: アウトブレイク発生後の社会的接触が**{relative_contact_rate:.0%}**減少すると、倍加時間は**{doubling_time_t:.1f}**日に改善され、実効$R_t$は**${r_t:.2f}$**となります。 + "Show more info about this tool": "さらにこのツールの情報を表示する" + "Discrete-time SIR modeling": "[Discrete-time SIR modeling](https://mathworld.wolfram.com/SIRModel.html) of infections/recovery" + "The model consists of individuals who are either...": |+ + The model consists of individuals who are either _Susceptible_ ($S$), _Infected_ ($I$), or _Recovered_ ($R$). + + The epidemic proceeds via a growth and decline process. This is the core model of infectious disease spread and has been in use in epidemiology for many years. + "The dynamics are given by the following 3 equations.": "The dynamics are given by the following 3 equations." + "To project the expected impact to Penn Medicine...": |+ + To project the expected impact to Penn Medicine, we estimate the terms of the model. + + To do this, we use a combination of estimates from other locations, informed estimates based on logical reasoning, and best guesses from the American Hospital Association. + + + ### Parameters + + The model's parameters, $\beta$ and $\gamma$, determine the virulence of the epidemic. + + $$\beta$$ can be interpreted as the _effective contact rate_: + "which is the transmissibility multiplied...": |+ + which is the transmissibility ($\tau$) multiplied by the average number of people exposed ($$c$$). The transmissibility is the basic virulence of the pathogen. The number of people exposed $c$ is the parameter that can be changed through social distancing. + + $\gamma$ is the inverse of the mean recovery time, in days. I.e.: if $\gamma = 1/{recovery_days}$, then the average infection will clear in {recovery_days} days. + + An important descriptive parameter is the _basic reproduction number_, or $R_0$. This represents the average number of people who will be infected by any given infected person. When $R_0$ is greater than 1, it means that a disease will grow. Higher $R_0$'s imply more rapid growth. It is defined as + "$R_0$ gets bigger when...": |+ + + $R_0$ gets bigger when + + - there are more contacts between people + - when the pathogen is more virulent + - when people have the pathogen for longer periods of time + + A doubling time of {doubling_time} days and a recovery time of {recovery_days} days imply an $R_0$ of {r_naught:.2f}. + + #### Effect of social distancing + + After the beginning of the outbreak, actions to reduce social contact will lower the parameter $c$. If this happens at + time $t$, then the number of people infected by any given infected person is $R_t$, which will be lower than $R_0$. + + A {relative_contact_rate:.0%} reduction in social contact would increase the time it takes for the outbreak to double, + to {doubling_time_t:.2f} days from {doubling_time:.2f} days, with a $R_t$ of {r_t:.2f}. + + #### Using the model + + We need to express the two parameters $\beta$ and $\gamma$ in terms of quantities we can estimate. + + - $\gamma$: the CDC is recommending 14 days of self-quarantine, we'll use $\gamma = 1/{recovery_days}$. + - To estimate $$\beta$$ directly, we'd need to know transmissibility and social contact rates. since we don't know these things, we can extract it from known _doubling times_. The AHA says to expect a doubling time $T_d$ of 7-10 days. That means an early-phase rate of growth can be computed by using the doubling time formula: + "Since the rate of new infections in the SIR model...": |+ + - Since the rate of new infections in the SIR model is $g = \beta S - \gamma$, and we've already computed $\gamma$, $\beta$ becomes a function of the initial population size of susceptible individuals. + $$\beta = (g + \gamma)$$. + + + ### Initial Conditions + + - The total size of the susceptible population will be the entire catchment area for Penn Medicine entities (HUP, PAH, PMC, CCH) + - Delaware = {delaware} + - Chester = {chester} + - Montgomery = {montgomery} + - Bucks = {bucks} + - Philly = {philly} + "Number of days to project": "予測する日数" + "New Admissions": "新規人数" + "Projected number of **daily** COVID-19 admissions at Penn hospitals": "Penn病院での**日毎の**COVID-19関連新規人数予測" + "Hospitalized": "入院患者数" + "ICU": "集中治療室患者数" + "Ventilated": "人工呼吸器患者数" + "Days from today": "今日からの日数" + "Daily admissions": "日毎の人数" + "Show Projected Admissions in tabular form": "予測された人数を表形式で見る" + "Admitted Patients (Census)": "入院患者(国勢調査)" + "Projected **census** of COVID-19 patients, accounting for arrivals and discharges at Penn hospitals": "Penn病院における入院と退院を考慮したCOVID-19患者の予測**国勢調査**" + "Hospital Census": "国勢調査 入院患者数" + "ICU Census": "国勢調査 集中治療室患者数" + "Ventilated Census": "国勢調査 人工呼吸器患者数" + "Census": "国勢調査" + "Show Projected Census in tabular form": "予測された国勢調査を表形式で見る" + "Infected": "感染者数" + "Recovered": "回復者数" + "Case Volume": "件数" + "**Click the checkbox below to view additional data generated by this simulation**": "**このシミュレーションで生成されたその他のデータを見るには下のチェックボックスをクリック**" + "The number of infected and recovered individuals in the hospital catchment region at any given moment": "病院の集患者域における感染者数と回復者数" + "Show Raw SIR Similation Data": "生のSIRモデルシミュレーションデータを表示" + "Show Additional Projections": "その他の予測を表示" + "Guidance on Selecting Inputs": "選択した値の入力についてのガイダンス" + "**Hospitalized COVID-19 Patients:**...": |+ + * **Hospitalized COVID-19 Patients:** The number of patients currently hospitalized with COVID-19 **at your hospital(s)**. This number is used in conjunction with Hospital Market Share and Hospitalization % to estimate the total number of infected individuals in your region. + * **Doubling Time (days):** This parameter drives the rate of new cases during the early phases of the outbreak. The American Hospital Association currently projects doubling rates between 7 and 10 days. This is the doubling time you expect under status quo conditions. To account for reduced contact and other public health interventions, modify the _Social distancing_ input. + * **Social distancing (% reduction in person-to-person physical contact):** This parameter allows users to explore how reduction in interpersonal contact & transmission (hand-washing) might slow the rate of new infections. It is your estimate of how much social contact reduction is being achieved in your region relative to the status quo. While it is unclear how much any given policy might affect social contact (eg. school closures or remote work), this parameter lets you see how projections change with percentage reductions in social contact. + * **Hospitalization %(total infections):** Percentage of **all** infected cases which will need hospitalization. + * **ICU %(total infections):** Percentage of **all** infected cases which will need to be treated in an ICU. + * **Ventilated %(total infections):** Percentage of **all** infected cases which will need mechanical ventilation. + * **Hospital Length of Stay:** Average number of days of treatment needed for hospitalized COVID-19 patients. + * **ICU Length of Stay:** Average number of days of ICU treatment needed for ICU COVID-19 patients. + * **Vent Length of Stay:** Average number of days of ventilation needed for ventilated COVID-19 patients. + * **Hospital Market Share (%):** The proportion of patients in the region that are likely to come to your hospital (as opposed to other hospitals in the region) when they get sick. One way to estimate this is to look at all of the hospitals in your region and add up all of the beds. The number of beds at your hospital divided by the total number of beds in the region times 100 will give you a reasonable starting estimate. + * **Regional Population:** Total population size of the catchment region of your hospital(s). + * **Currently Known Regional Infections**: The number of infections reported in your hospital's catchment region. This is only used to compute detection rate - **it will not change projections**. This input is used to estimate the detection rate of infected individuals. + "References & Acknowledgements": "参考資料と謝辞" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a553ebc5..79af8100 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,6 @@ streamlit pandas numpy pytest -altair \ No newline at end of file +altair +python-i18n +pyyaml \ No newline at end of file