Skip to content

Commit

Permalink
Merge pull request #214 from amath-idm/intervention-tooltips
Browse files Browse the repository at this point in the history
Intervention tooltips, intervention functional changes
  • Loading branch information
lmgeorge authored Apr 28, 2020
2 parents c62c8a0 + 71b5c53 commit e5c3d3e
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 48 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ changes from multiple patch versions are grouped together, so numbering will not
strictly consecutive.


Version 0.29.8 (2020-04-28)
---------------------------
- Update webapp UI with more detail on and control over interventions.


Version 0.29.7 (2020-04-27)
---------------------------
- New functions ``cv.date()`` and ``cv.daydiff()`` have been added, to ease handling of dates of different formats.
Expand Down
4 changes: 2 additions & 2 deletions covasim/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

__all__ = ['__version__', '__versiondate__', '__license__']

__version__ = '0.29.7'
__versiondate__ = '2020-04-27'
__version__ = '0.29.8'
__versiondate__ = '2020-04-28'
__license__ = f'Covasim {__version__} ({__versiondate__}) — © 2020 by IDM'
1 change: 1 addition & 0 deletions covasim/webapp/cova_app.css
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ fieldset > legend {
display: block;
position: relative !important;
width: initial;
/*overflow-y: scroll; CK: for later use */
height: auto !important;
grid-area: sidebar;
margin: 0;
Expand Down
76 changes: 53 additions & 23 deletions covasim/webapp/cova_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,45 +33,65 @@ const PlotlyChart = {

const interventionTableConfig = {
social_distance: {
formTitle: "Physical distancing",
fields: [{key: 'start', type: 'number', label: 'Start day'},
{key: 'end', type: 'number', label: 'End day'},
{label: 'Effectiveness', key: 'level', type: 'select', options: [{label: 'Aggressive (80%)', value: 'aggressive'}, {label: 'Moderate (50%)', value: 'moderate'}, {label: 'Mild (20%)', value: 'mild'}]}],
formTitle: "Distancing",
toolTip: "Physical distancing and social distancing interventions",
fields: [
{key: 'start', type: 'number', label: 'Start day', tooltip: 'Start day of intervention', value: 0},
{key: 'end', type: 'number', label: 'End day', tooltip: 'End day of intervention (leave blank for no end)', value: null},
{key: 'level', type: 'number', label: 'Effectiveness', tooltip: 'Impact of social distancing (examples: 20 = mild, 50 = moderate, 80 = aggressive)', min: 0, max: 100, value: 50}
],
handleSubmit: function(event) {
const start = parseInt(event.target.elements.start.value);
const end = parseInt(event.target.elements.end.value);
const start = vm.parse_day(event.target.elements.start.value);
const end = vm.parse_day(event.target.elements.end.value);
const level = event.target.elements.level.value;
return {start, end, level};
}
},
school_closures: {
formTitle: "School closures",
fields: [{key: 'start', type: 'number', label: 'Start day'}, {key: 'end', type: 'number', label: 'End day'}],
formTitle: "Schools",
toolTip: "School and university closures",
fields: [
{key: 'start', type: 'number', label: 'Start day', tooltip: 'Start day of intervention', value: 0},
{key: 'end', type: 'number', label: 'End day', tooltip: 'End day of intervention (leave blank for no end)', value: null},
{key: 'level', type: 'number', label: 'Effectiveness', tooltip: 'Impact of school closures (0 = no schools closed, 100 = all schools closed)', min: 0, max: 100, value: 90}
],
handleSubmit: function(event) {
const start = parseInt(event.target.elements.start.value);
const end = parseInt(event.target.elements.end.value);
return {start, end};
const start = vm.parse_day(event.target.elements.start.value);
const end = vm.parse_day(event.target.elements.end.value);
const level = event.target.elements.level.value;
return {start, end, level};
}
},
symptomatic_testing: {
formTitle: "Symptomatic testing",
fields: [{key: 'start', type: 'number', label: 'Start day'}, {key: 'end', type: 'number', label: 'End day'}, {label: 'Coverage', key: 'level', type: 'select', options: [{label: '10% per day', value: '10'}, {label: '30% per day', value: '30'},]}],
formTitle: "Testing",
toolTip: "Testing rates for people with symptoms",
fields: [
{key: 'start', type: 'number', label: 'Start day', tooltip: 'Start day of intervention', value: 0},
{key: 'end', type: 'number', label: 'End day', tooltip: 'End day of intervention (leave blank for no end)', value: null},
{key: 'level', type: 'number', label: 'Effectiveness', tooltip: 'Proportion of people tested per day (0 = no testing, 10 = 10% of people tested per day, 100 = everyone tested every day); assumes 1 day test delay', min: 0, max: 100, value: 10}
],
handleSubmit: function(event) {
const start = parseInt(event.target.elements.start.value);
const end = parseInt(event.target.elements.end.value);
const level = parseInt(event.target.elements.level.value);
const start = vm.parse_day(event.target.elements.start.value);
const end = vm.parse_day(event.target.elements.end.value);
const level = event.target.elements.level.value;
return {start, end, level};
}
},
contact_tracing: {
formTitle: "Contact tracing",
fields: [{key: 'start', type: 'number', label: 'Start Day'}, {key: 'end', type: 'number', label: 'End day'}],
formTitle: "Tracing",
toolTip: "Contact tracing of diagnosed cases (requires testing intervention)",
fields: [
{key: 'start', type: 'number', label: 'Start day', tooltip: 'Start day of intervention', value: 0},
{key: 'end', type: 'number', label: 'End day', tooltip: 'End day of intervention (leave blank for no end)', value: null},
{key: 'level', type: 'number', label: 'Effectiveness', tooltip: 'Effectiveness of contact tracing (0 = no tracing, 100 = all contacts traced); assumes 1 day tracing delay. Please note: you must implement a testing intervention as well for tracing to have any effect.', min: 0, max: 100, value: 80}
],
handleSubmit: function(event) {
const start = parseInt(event.target.elements.start.value);
const end = parseInt(event.target.elements.end.value);
return {start, end};
const start = vm.parse_day(event.target.elements.start.value);
const end = vm.parse_day(event.target.elements.end.value);
const level = event.target.elements.level.value;
return {start, end, level};
}
}
}

};

Expand Down Expand Up @@ -235,7 +255,7 @@ var vm = new Vue({
this.int_pars[key].push(intervention);
const result = this.int_pars[key].sort((a, b) => a.start - b.start);
this.$set(this.int_pars, key, result);
const response = await sciris.rpc('get_gantt', undefined, {int_pars: this.int_pars, intervention_config: this.interventionTableConfig});
const response = await sciris.rpc('get_gantt', undefined, {int_pars: this.int_pars, intervention_config: this.interventionTableConfig, n_days: this.sim_length.best});
this.intervention_figs = response.data;
},
async deleteIntervention(scenarioKey, index) {
Expand All @@ -244,6 +264,16 @@ var vm = new Vue({
this.intervention_figs = response.data;
},

parse_day(day) {
if (day == null || day == '') {
const output = this.sim_length.best
return output
} else {
const output = parseInt(day)
return output
}
},

resize_start() {
this.resizing = true;
},
Expand Down
27 changes: 10 additions & 17 deletions covasim/webapp/cova_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,18 @@ def upload_file(file):


@app.register_RPC()
def get_gantt(int_pars=None, intervention_config=None):
def get_gantt(int_pars=None, intervention_config=None, n_days=90):
df = []
response = {'id': 'test'}
for key,scenario in int_pars.items():
for timeline in scenario:
task = intervention_config[key]['formTitle']
level = task + ' ' + str(timeline.get('level', ''))
level = task + ' ' + str(timeline.get('level', '')) + '%'
df.append(dict(Task=task, Start=timeline['start'], Finish=timeline['end'], Level= level))
if len(df) > 0:
fig = ff.create_gantt(df, height=400, index_col='Level', title='Intervention timeline',
show_colorbar=True, group_tasks=True, showgrid_x=True, showgrid_y=True)
fig.update_xaxes(type='linear')
fig.update_xaxes(type='linear', range=[0, n_days])
response['json'] = fig.to_json()

return response
Expand Down Expand Up @@ -240,27 +240,20 @@ def parse_interventions(int_pars):
ikey = iconfig['ikey']
start = iconfig['start']
end = iconfig['end']
level = float(iconfig['level'])/100
if ikey == 'social_distance':
level = iconfig['level']
mapping = {
'mild': 0.8,
'moderate': 0.5,
'aggressive': 0.2,
}
change = mapping[level]
change = 1.0-level
interv = cv.change_beta(days=[start, end], changes=[change, 1.0])
elif ikey == 'school_closures':
change = 0.0
change = 1.0-level
interv = cv.change_beta(days=[start, end], changes=[change, 1.0], layers='s')
elif ikey == 'symptomatic_testing':
level = iconfig['level']
level = float(level)/100
asymp_prob = 0.0
delay = 0.0
asymp_prob = level/10
delay = 1.0
interv = cv.test_prob(start_day=start, end_day=end, symp_prob=level, asymp_prob=asymp_prob, test_delay=delay)
elif ikey == 'contact_tracing':
trace_prob = {k:1.0 for k in 'hswc'}
trace_time = {k:0.0 for k in 'hswc'}
trace_prob = {k:level for k in 'hswc'}
trace_time = {k:1.0 for k in 'hswc'}
interv = cv.contact_tracing(start_day=start, end_day=end, trace_probs=trace_prob, trace_time=trace_time)
else:
raise NotImplementedError
Expand Down
21 changes: 15 additions & 6 deletions covasim/webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,29 @@
<b-button block v-b-toggle.parameters-intervention variant="link">Interventions <span class="ti-angle-up"></span> <span class="ti-angle-down"></span></b-button>
</b-card-header>
<b-collapse id="parameters-intervention" visible role="tabpanel">
<plotly-chart v-if="intervention_figs && intervention_figs.id" :graph="intervention_figs" :key="intervention_figs.id" ></plotly-chart>
<b-card no-body class="border-0 rounded-0">
<b-tabs fill card>
<template v-for="(intervention, name, index) in interventionTableConfig">
<b-tab :class="[index === 0? 'active' : '']" :id="'intervention-'+name">
<template v-slot:title>
{{ intervention.formTitle }}
<span v-b-tooltip.hover :title="intervention.toolTip">
{{ intervention.formTitle }}
</span>
</template>
<fieldset>
<form @submit.prevent="(e) => addIntervention(name, e)">
<div class="form-group">
<div class="input-group mb-3">
<template v-for="field in intervention.fields">
<input :max="sim_length.max" :min="0" :placeholder="field.label" :name="field.key" :aria-label="field.label" class="form-control" v-if="field.type !== 'select'" :type="field.type">
<input :max="field.max || sim_length.max" :min="field.min || 0" :value="field.value" :name="field.key"
:aria-label="field.label" class="form-control" v-if="field.type !== 'select'"
:type="field.type" v-b-tooltip.hover :title="field.tooltip">
<select class="form-control" :name="field.key" v-else>
<option v-for="option in field.options" :value="option.value">{{option.label}}</option>
<option v-for="option in field.options" :value="option.value">{{option.value}}</option>
</select>
<div class="input-group-append" v-if="field.key === 'level'">
<span class="input-group-text">%</span>
</div>
</template>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Add intervention</button>
Expand All @@ -186,9 +192,11 @@
</thead>
<tbody>
<tr v-for="(par, index) in int_pars[name]">
<th v-for="field in intervention.fields" scope="row">{{par[field.key]}}</th>
<td v-for="field in intervention.fields" scope="row">
{{par[field.key]}}<span v-if="field.key=== 'level'">%</span>
</td>
<td>
<button @click="deleteIntervention(name, index)" class="btn btn-outline-secondary" type="button">Delete</button>
<button @click="deleteIntervention(name, index)" class="btn btn-outline-danger" type="button">Delete</button>
</td>
</tr>
</tbody>
Expand All @@ -199,6 +207,7 @@
</template>
</b-tabs>
</b-card>
<plotly-chart v-if="intervention_figs && intervention_figs.id" :graph="intervention_figs" :key="intervention_figs.id" ></plotly-chart>
</b-collapse>
</b-card>

Expand Down

0 comments on commit e5c3d3e

Please sign in to comment.