def healthcare(self, region: covid.Region): """ Return a dictionary with hospital and icu capacities from user input. Returns: icu_capacity, hospital_capacity (float): maximum system capacity """ st.sidebar.header(_("Hospital capacity")) def get(msg, capacity, rate, key=None): st.sidebar.subheader(msg) msg = _( "Location has {n} beds, but only {rate} are typically available in a given day." ) st.sidebar.markdown(msg.format(n=fmt(int(capacity)), rate=pc(1 - rate))) total = st.sidebar.number_input( _("Beds dedicated exclusively to COVID-19"), min_value=0, value=int((1 - rate) * capacity), key=key + "_total", ) return (1 - rate) * total h_total = region.hospital_total_capacity h_rate = max(region.hospital_occupancy_rate, 0.75) c_total = region.icu_total_capacity c_rate = max(region.icu_occupancy_rate, 0.75) return { "hospital_capacity": get(_("Clinical beds"), h_total, h_rate, key="hospital"), "icu_capacity": get(_("ICU beds"), c_total, c_rate, key="icu"), }
def write_population_info(self, model: SEICHAR): """Write additional information about the model.""" # Demography st.subheader(_("Population")) seniors = model.demography.loc["60-69":].sum() total = model.population entries = {_("Total"): fmt(total), _("Age 60+"): f"{fmt(seniors)} ({pc(seniors / total)})"} cards(entries)
def write_fatalities_chart(self, model): st.subheader(" ") st.subheader(_("Anticipated age distribution of COVID deaths")) data = model.data.loc[model.time, "fatalities"] data = pd.DataFrame( { "fatalities": data.astype(int), "pc": (1e5 * data.values / model.demography.values).astype(int), } ) data.columns = ["left", "right"] double_bar_chart(data, _("Total deaths"), _("Mortality per 100k"))
def get(msg, capacity, rate, key=None): st.sidebar.subheader(msg) msg = _( "Location has {n} beds, but only {rate} are typically available in a given day." ) st.sidebar.markdown(msg.format(n=fmt(int(capacity)), rate=pc(1 - rate))) total = st.sidebar.number_input( _("Beds dedicated exclusively to COVID-19"), min_value=0, value=int((1 - rate) * capacity), key=key + "_total", ) return (1 - rate) * total
def run(self): """ Run streamlit app. """ components.icon(_("COVID-19"), _("Epidemic Calculator"), where=st.sidebar) with self.info(_("Loading region...")): region = self.input.region() self.input.pause() self._title = f"{self.title} - {region}" with self.info(_("Loading simulation parameters...")): kwargs = {"region": region, **self.input.params(region)} with self.info(_("Performing simulation...")): self.run_simulation(**kwargs)
def write_protection_equipment_demand(self, model): st.markdown("---") st.subheader(_("Protection equipment")) df = model.health_resources(translate=_) df[df.columns[1]] = df[df.columns[1]].apply(fmt) st.table(df)
def write_footnotes(self, *args): """Write footnotes""" template = '<a href="{href}">{name}</a>' institutions = [ template.format(href=_("https://www.paho.org/hq/index.php?lang=en"), name=_("PAHO")), template.format(href="https://saude.gov.br/", name="MS/SVS"), template.format(href="https://lappis.rocks", name="UnB/LAPPIS"), template.format(href="http://medicinatropical.unb.br/", name="UnB/NMT"), template.format(href="http://fce.unb.br/", name="UnB/FCE"), template.format(href="http://www.butantan.gov.br/", name="Butantã"), template.format(href="http://www.matogrossodosul.fiocruz.br/", name="Fiocruz"), template.format(href="https://famed.ufms.br/", name="FAMED"), ] links = _("Support: {institutions}").format(institutions=", ".join(institutions)) styles = "text-align: center; margin: 5rem 0 -5rem 0;" html(f'<div style="{styles}">{links}</div>')
def write_healthcare_parameters(self, model): st.markdown("---") st.subheader(_("Healthcare system")) r = model.region md_description( { _("COVID ICUs"): fmt(int(model.icu_capacity)), _("COVID hospital beds"): fmt(int(model.hospital_capacity)), _("ICUs"): _("{n} (source: CNES)").format(n=fmt(r.icu_total_capacity)), _("Hospital beds"): _("{n} (source: CNES)").format( n=fmt(r.hospital_total_capacity) ), } ) if model.region.icu_total_capacity == 0: msg = _( """ The location does not have any ICU beds. At peak demand, it needs to reserve {n} beds from neighboring cities. """ ) msg = msg.format(n=fmt(model.peak_icu_demand)) elif model.icu_overflow_date: msg = _( """ The location will **run out of ICU beds at {date}**. At peak demand, it will need **{n} new ICUs**. This demand corresponds to **{surge} times** the number of beds dedicated to COVID-19 and {total} of the total number of ICU beds. """ ) msg = msg.format( date=naturaldate(model.icu_overflow_date), n=fmt(int(model.peak_icu_demand - model.icu_capacity)), surge=fmt(model.peak_icu_demand / model.icu_capacity), total=fmt(model.peak_icu_demand / model.icu_total_capacity), ) else: msg = _( """ The number of ICU beds is sufficient for the expected demand in this scenario. """ ) st.markdown(msg)
def hospitalizations_chart(self, model): """ Write plot of hospitalization """ st.subheader(_("Hospital demand")) hospitalized = model["hospitalized:total"] icu = model["critical:total"] fatalities = fatality_rate(model["fatalities:total"], model.dt) columns = { _("Deaths/day"): fatalities, _("Required hospital beds"): hospitalized.astype(int), _("Required ICU beds"): icu.astype(int), } df = model.get_dates(pd.DataFrame(columns)) st.area_chart(df)
def intervention(self, region: covid.Region): """ Return a dictionary with intervention parameters. Returns: icu_capacity, hospital_capacity (float): maximum system capacity """ st.sidebar.header(_("Intervention")) baseline, social_distance = interventions = [_("None"), _("Social distancing")] intervention = st.sidebar.selectbox(_("Scenario"), interventions) if intervention == baseline: return {} elif intervention == social_distance: st.sidebar.markdown( _( """ This intervention simulates a situation in which everyone reduces the average number of encounters throughout the day. Small reductions (~15%) are possible through small behavioral changes. Larger reductions require implementation of many non-pharmacological measures. """ ) ) week = TODAY + datetime.timedelta(days=7) date = st.sidebar.date_input(_("Date of intervention"), value=week) rate = st.sidebar.slider(_("Reduction in the number of contacts"), value=15) return {"intervention": social_distance_intervention(date, 1 - rate / 100)}
def simulation(self, region: covid.Region): """ Return a dictionary with basic simulation parameters from user input. Returns: period (int): Simulation period in days. start_date (date): Initial date. seed (int): Initial number of cases. """ st.sidebar.header(_("Simulation options")) seed = int(max(5e-6 * region.population_size, 1)) return { "period": st.sidebar.slider(_("Duration (weeks)"), 1, 30, value=10) * 7, "start_date": st.sidebar.date_input(_("Simulation date")), "seed": st.sidebar.number_input( _("Number of detected cases"), 1, int(region.population_size), value=seed ), }
def write_epidemiological_parameters(self, model): st.markdown("---") st.subheader(_("Advanced epidemiological information")) mortality = fmt(1e5 * model.fatalities / model.population) fatality = pc(model.CFR()) infected = pc(model.total_exposed / model.population) symptomatic = pc(model.prob_symptomatic) md_description( { _("Number of cases generated by a single case"): fmt(model.R0_average), _("Mortality (deaths per 100k population)"): mortality, _("Letality ({pc} of deaths among the ill)").format(pc="%"): fatality, } ) lang = os.environ.get("LANGUAGE", "en_US") footnote_disclaimer(**locals())
def available_beds_chart(self, model): """ Write plot of available beds. """ st.subheader(_("Available hospital beds")) hospitalized = model["hospitalized:total"] icu = model["critical:total"] available_beds = model.hospital_capacity - hospitalized available_beds[available_beds < 0] = 0 available_icu = model.icu_capacity - icu available_icu[available_icu < 0] = 0 columns = {_("Regular"): available_beds, _("ICU"): available_icu} df = model.get_dates(pd.DataFrame(columns)) st.line_chart(df)
def summary_cards(self, model: SEICHAR): """ Show list of summary cards for the results of simulation. """ # Which one? df = model.data["infectious"] * model.gamma_i * model.prob_hospitalization hospitalized = df.apply(model.integral).sum() h_date = model.hospital_overflow_date c_date = model.icu_overflow_date missing_icu = max(int(model.peak_icu_demand - model.icu_capacity), 0) missing_hospital = max(int(model.peak_hospitalization_demand - model.hospital_capacity), 0) fatalities_pc = pc(model.fatalities / model.initial_population) hospitalized_pc = pc(hospitalized / model.initial_population) entries = { _("Deaths"): f"{fmt(int(model.fatalities))} ({fatalities_pc})", _("Hospitalizations"): f"{fmt(int(hospitalized))} ({hospitalized_pc})", _("Required extra ICU beds"): f"{fmt(missing_icu)}", _("Required extra hospital beds"): f"{fmt(missing_hospital)}", _("No more ICU beds available by"): f"{naturaldate(c_date)}", _("No more hospital beds available by"): f"{naturaldate(h_date)}", } cards(entries)
def fn(model): if date < model.start_date: st.warning(_("Intervention starts prior to simulation")) model.R0 *= rate return model else: delta = date - model.start_date R0_initial = model.R0 R0_final = model.R0 * rate t_intervention = delta.days def Rt(t): return R0_initial if t < t_intervention else R0_final model.R0 = Rt return model
def region(self): """ Return a region instance from user input. """ # Select a state st.sidebar.header(_("Location")) df = states(self.country) choices = [self.display_country, *df["name"]] choice = st.sidebar.selectbox(_("State"), choices) if choice == choices[0]: return get_region(self.country) state_id = state_code(self.country, choice) # State selected, now ask for sub-region df = sub_regions(self.country, state_id) if len(df) == 1: choice = df["name"].iloc[0] else: choices = [_("All"), *df["name"]] choice = st.sidebar.selectbox(_("Region"), choices) if choice == choices[0]: return get_region(f"{self.country}/{state_id}") sub_region_id = sub_region_code(self.country, state_id, choice) # Sub-region selected, now ask for a city df = cities(self.country, sub_region_id) if len(df) == 1: choice = df["name"].iloc[0] else: choices = [_("All"), *df["name"]] choice = st.sidebar.selectbox(_("City"), choices) if choice == choices[0]: return get_region(f"{self.country}/{sub_region_id}") city_id = df[df["name"] == choice].index[0] return get_region(f"{self.country}/{city_id}")
cities = countries.cities(country.lower()) return cities[cities["sub_region"] == sub_region] @st.cache def state_code(country, state): names = states(country)["name"] return names[names == state].index[0] @st.cache def sub_region_code(country, state_code, sub_region): regions = sub_regions(country, state_code) return regions[regions["name"] == sub_region].index[0] @st.cache(allow_output_mutation=True) def get_region(ref): region = covid.region(ref) if ref.lower().startswith("brazil/"): region.demography = DEMOGRAPHY_CORRECTION return region if __name__ == "__main__": from pprint import pformat app = Input("brazil", _("Brazil"), st.sidebar) res = app.run() st.text(pformat(res))
class CalcUI: """ Renders Streamlit UI. """ simulation_class = SEICHAR title = _("COVID-19 Hospital Pressure") def __init__(self, country=COUNTRY, display_country=DISPLAY_COUNTRY): st.write(asset("custom.html"), unsafe_allow_html=True) self._title = st.title(self.title) self._info = st.text("") self.country = country self.input = Input(self.country, display_country=DISPLAY_COUNTRY) @contextmanager def info(self, st): """ Context manager that displays info text while code inside the with block is being executed and clear it afterwards. """ self._info.text(st) yield self._info.text("") def run(self): """ Run streamlit app. """ components.icon(_("COVID-19"), _("Epidemic Calculator"), where=st.sidebar) with self.info(_("Loading region...")): region = self.input.region() self.input.pause() self._title = f"{self.title} - {region}" with self.info(_("Loading simulation parameters...")): kwargs = {"region": region, **self.input.params(region)} with self.info(_("Performing simulation...")): self.run_simulation(**kwargs) def run_simulation( self, period, hospital_capacity, icu_capacity, hospitalization_bias=2.0, intervention=None, **kwargs, ): """ Initialize class with given arguments and run simulation. """ kwargs = { "prob_symptomatic": 0.14, "hospital_prioritization": 0.0, **kwargs } model = self.simulation_class(**kwargs) # FIXME: should be able to setup on the constructor model.hospital_capacity = hospital_capacity model.icu_capacity = icu_capacity model.prob_hospitalization *= hospitalization_bias if intervention: model = intervention(model) model.run(period) out = Output(model) out.run()
from contextlib import contextmanager import streamlit as st import covid from covid import gettext as _ from covid.models import SEICHARDemographic as SEICHAR from covid.ui import components from covid.ui.components import asset from covid.ui.input import Input from covid.ui.output import Output COUNTRY = "Brazil" DISPLAY_COUNTRY = _("Brazil") SEIR_HUMANIZED_NAMES = { "susceptible": _("susceptible"), "exposed": _("exposed"), "infectious": _("infectious"), "critical": _("critical"), "hospitalized": _("hospitalized"), "asymptomatic": _("asymptomatic"), "recovered": _("recovered"), "fatalities": _("fatalities"), } e = 1e-50 class CalcUI: """ Renders Streamlit UI.
from babel.dates import format_date import covid from covid import gettext as _ from covid.models import SEICHARDemographic as SEICHAR from covid.ui.components import ( cards, md_description, asset, double_bar_chart, html, footnote_disclaimer, ) from covid.utils import fmt, pc naturaldate = lambda d: format_date(d, format="short") if d else _("Not soon...") class Output: def __init__(self, model): self.model = model def run(self): """ Show all outputs for the given simulated model. """ model = self.model self.summary_cards(model) self.hospitalizations_chart(model) self.available_beds_chart(model) self.write_population_info(model)
def write_age_distribution_chart(self, model): st.subheader(" ") st.subheader(_("Population pyramid")) data = model.region.detailed_demography data.columns = ["left", "right"] double_bar_chart(data, _("Female"), _("Male"))
def epidemiology(self, region: covid.Region): """ Return a dictionary with additional simulation parameters from user input. Those parameters are related to basic epidemiology assumptions such as the value of R0, incubation period, etc. """ e = 1e-50 st.sidebar.header(_("Epidemiology")) std, fast, slow, custom = scenarios = [_("Standard"), _("Fast"), _("Slow"), _("Advanced")] scenario = st.sidebar.selectbox(_("Scenario"), scenarios) if scenario == std: return {"R0": 2.74} if scenario == fast: return {"R0": 3.5} elif scenario == slow: return {"R0": 2.0} # Custom st.sidebar.subheader(_("Epidemiological parameters")) R0 = SEICHAR.R0 msg = _("Newly infected people for each infection (R0)") R0 = st.sidebar.slider(msg, min_value=0.0, max_value=5.0, value=R0) incubation_period = 1 / SEICHAR.sigma msg = _("Virus incubation period") incubation_period = st.sidebar.slider( msg, min_value=1.0, max_value=10.0, value=incubation_period ) infectious_period = 1 / SEICHAR.gamma_i msg = _("Infectious period") infectious_period = st.sidebar.slider( msg, min_value=1.0, max_value=14.0, value=infectious_period ) prob_symptomatic = 100 * SEICHAR.prob_symptomatic msg = _("Fraction of symptomatic cases") prob_symptomatic = 0.01 * st.sidebar.slider( msg, min_value=0.0, max_value=100.0, value=prob_symptomatic ) st.sidebar.subheader(_("Clinical parameters")) prob_hospitalization = 200 * region.prob_hospitalization prob_hospitalization = st.sidebar.slider( _("Fraction of hospitalized cases"), min_value=0.0, max_value=100.0, value=prob_hospitalization, ) hospitalization_bias = prob_hospitalization / (100 * region.prob_hospitalization) hospitalization_period = 1 / SEICHAR.gamma_h msg = _("Hospitalization period (days)") hospitalization_period = st.sidebar.slider( msg, min_value=0.0, max_value=30.0, value=hospitalization_period ) icu_period = 1 / SEICHAR.gamma_c msg = _("Hospitalization period for ICU patients (days)") icu_period = st.sidebar.slider(msg, min_value=0.0, max_value=30.0, value=icu_period) return { "R0": R0, "sigma": 1.0 / (incubation_period + e), "gamma_i": 1.0 / (infectious_period + e), "gamma_h": 1.0 / (hospitalization_period + e), "gamma_c": 1.0 / (icu_period + e), "prob_symptomatic": prob_symptomatic, "hospitalization_bias": hospitalization_bias, }