Merge branch 'master' into feature/short_range_concentration

This commit is contained in:
Luis Aleixo 2022-03-03 10:08:58 +01:00
commit ee7bdd560f
13 changed files with 209 additions and 73 deletions

96
.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,96 @@
# This module is part of CARA. Please see the repository at
# https://gitlab.cern.ch/cara/cara for details of the license and terms of use.
name: CI
on:
push:
branches:
- master
- 'feature/*'
pull_request:
branches:
- master
- 'feature/*'
workflow_dispatch:
inputs:
reason:
description: 'Reason'
required: false
default: 'Manual trigger'
jobs:
test-install:
runs-on: ubuntu-latest
env:
PROJECT_ROOT: ./
PROJECT_NAME: cara
steps:
- name: Checkout
uses: actions/checkout@v2
with:
lfs: true
- name: Create LFS file list
run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
- name: Restore LFS cache
uses: actions/cache@v2
id: lfs-cache
with:
path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
- name: Git LFS Pull
run: git lfs pull
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install ${PROJECT_ROOT}[test]
- name: Run tests
run: |
mkdir -p ~/not-the-source-dir && cd ~/not-the-source-dir
python -m pytest --pyargs ${PROJECT_NAME}
test-dev:
runs-on: ubuntu-20.04
env:
PROJECT_ROOT: ./
PROJECT_NAME: cara
steps:
- name: Checkout
uses: actions/checkout@v2
with:
lfs: true
- name: Create LFS file list
run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
- name: Restore LFS cache
uses: actions/cache@v2
id: lfs-cache
with:
path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
- name: Git LFS Pull
run: git lfs pull
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install -e ${PROJECT_ROOT}[test]
python -m pip install pytest-cov
- name: Run tests
run: |
cd ${PROJECT_ROOT}
python -m pytest ./${PROJECT_NAME} --cov=${PROJECT_NAME} --junitxml=report.xml

View file

@ -35,9 +35,14 @@ Andre Henriques<sup>1</sup>, Luis Aleixo<sup>1</sup>, Marco Andreini<sup>1</sup>
<sup>5</sup>Information Technology Department, Collaboration, Devices & Applications Group, CERN<br>
<sup>6</sup>Norwegian University of Science and Technology (NTNU)<br>
### Citation
A. Henriques, M. Andreini, G. Azzopardi, J. Devine, P. Elson, N. Mounet, M. Kongstein, N. Tarocco. CARA - COVID Airborne Risk Assessment tools. CERN (2021).
### Reference and Citation
**For the use of the CARA web app**
CARA COVID Airborne Risk Assessment tool
© Copyright 2020-2021 CERN. All rights not expressly granted are reserved.
**For use of the model**
Henriques A, Mounet N, Aleixo L, Elson P, Devine J, Azzopardi G, Andreini M, Rognlien M, Tarocco N, Tang J. (2022). Modelling airborne transmission of SARS-CoV-2 using CARA: risk assessment for enclosed spaces. _Interface Focus 20210076_. https://doi.org/10.1098/rsfs.2021.0076
## Applications
@ -76,6 +81,8 @@ The CARA repository makes use of Git's Large File Storage (LFS) feature.
You will need a working installation of git-lfs in order to run CARA in development mode.
See https://git-lfs.github.com/ for installation instructions.
CARA is also mirrored to Github if you wish to collaborate on development and can be found at: https://github.com/CERN/cara
### Installing CARA in editable mode
```
@ -292,4 +299,4 @@ $ oc process -f deploymentconfig.yaml --param PROJECT_NAME='cara-test' | oc repl
```
Be aware that if you create/recreate the environment you must manually create a **route** in OpenShift,
specifying the respective annotation to be exposed outside CERN.
specifying the respective annotation to be exposed outside CERN.

View file

@ -21,5 +21,5 @@ FROM registry.cern.ch/docker.io/library/debian
COPY --from=conda /opt/app /opt/app
CMD [ \
"/opt/app/bin/python", "-m", "auth_service" \
"/opt/app/bin/python", "-m", "auth_service", "--no-debug" \
]

View file

@ -10,15 +10,14 @@ import typing
import aiohttp
from keycloak.aio.realm import KeycloakRealm
import tornado.ioloop
from tornado.web import Application, RequestHandler
import tornado.log
import tornado.web
LOG = logging.getLogger(__name__)
class BaseHandler(tornado.web.RequestHandler):
class BaseHandler(RequestHandler):
def set_session_cookie(self, session_data: dict, expiry_in_seconds: int) -> None:
seconds_per_day = 60 * 60 * 24
self.set_secure_cookie(
@ -152,18 +151,19 @@ class MainHandler(BaseHandler):
session = self.get_session_cookie()
if session is None:
return self.finish("""
You are currently not logged in: <a href="/auth/login">Login</a>
You are currently not logged in: <a href="/auth/login">Login</a>
""")
else:
return self.finish(f"""
You are currently logged in as "{session['username']}":
<a href="/auth/logout">Logout</a>
<a href="/auth/logout">Logout</a>
""")
def make_app():
tornado.log.enable_pretty_logging()
return tornado.web.Application(
def make_app(debug: bool = False) -> Application:
if debug:
tornado.log.enable_pretty_logging()
return Application(
[
(r"/", MainHandler),
(r"/auth/probe", ProbeAuthentication),
@ -174,7 +174,7 @@ def make_app():
(r'/auth/logout', Logout),
],
cookie_secret=os.environ['COOKIE_SECRET'],
debug=True,
debug=debug,
oicd_server=os.environ['OIDC_SERVER'],
oicd_realm=os.environ['OIDC_REALM'],
client_id=os.environ['CLIENT_ID'],

View file

@ -1,9 +1,30 @@
import tornado.ioloop
import argparse
from tornado.ioloop import IOLoop
from . import make_app
def configure_parser(parser) -> argparse.ArgumentParser:
parser.add_argument(
"--no-debug", help="Don't enable debug mode",
action="store_false",
)
parser.add_argument(
"--port",
help="The port to listen on",
default="8080"
)
return parser
def main():
parser = configure_parser(argparse.ArgumentParser())
args = parser.parse_args()
app = make_app(debug=args.no_debug)
app.listen(args.port)
IOLoop.instance().start()
if __name__ == "__main__":
app = make_app()
app.listen(8080)
tornado.ioloop.IOLoop.current().start()
main()

View file

@ -60,13 +60,6 @@ function draw_concentration_plot(svg_id, times, concentrations, cumulative_doses
.attr('fill-opacity', '0.1');
});
// Plot tittle.
var plotTitleEl = vis.append('svg:foreignObject')
.attr("background-color", "transparent")
.attr('height', 30)
.style('text-align', 'center')
.html('<b>Mean concentration of virions</b>');
// X axis declaration.
var xAxisEl = vis.append('svg:g')
.attr('class', 'x axis');
@ -100,7 +93,7 @@ function draw_concentration_plot(svg_id, times, concentrations, cumulative_doses
.attr('class', 'y label')
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text('Mean cumulative dose (virions)');
.text('Mean cumulative dose (infectious virus)');
// Legend for the plot elements - line and area.
var legendLineIcon = vis.append('rect')
@ -266,10 +259,6 @@ function draw_concentration_plot(svg_id, times, concentrations, cumulative_doses
})))
})
// Title.
plotTitleEl.attr('width', graph_width);
// Axis.
var xAxis = d3.axisBottom(xRange).tickFormat(d => time_format(d));
var yAxis = d3.axisLeft(yRange);
@ -471,15 +460,7 @@ function draw_alternative_scenarios_plot(concentration_plot_svg_id, alternative_
.attr('alignment-baseline', 'central');
}
// Plot title.
var plotTitleEl = vis.append('svg:foreignObject')
.attr("background-color", "transparent")
.attr('height', 30)
.style('text-align', 'center')
.html('<b>Mean concentration of virions</b>');
// X axis.
var xAxisEl = vis.append('svg:g')
.attr('class', 'x axis');
@ -610,9 +591,6 @@ function draw_alternative_scenarios_plot(concentration_plot_svg_id, alternative_
}
// Title.
plotTitleEl.attr('width', graph_width);
// Axis.
var xAxis = d3.axisBottom(xRange).tickFormat(d => time_format(d));
var yAxis = d3.axisLeft(yRange);

View file

@ -121,6 +121,11 @@ body {
font-size: 1.25rem;
}
.acknowledgements {
text-align: left;
font-size: 1.5rem;
}
.center {
position: relative;
display: flex;
@ -220,11 +225,6 @@ footer img {
text-align: left;
font-size: 2rem;
}
.developers {
text-align: left;
font-size: 1.5rem;
}
.split > * + * {
margin-left: 2em;

View file

@ -8,7 +8,7 @@
Currently, the existing public health measures point to the importance of proper building and environmental engineering control measures, such as proper Indoor Air Quality (IAQ).
This pandemic clearly raised increased awareness on airborne transmission of respiratory viruses in indoor settings.
Out of the main modes of viral transmission, the airborne route of SARS-CoV-2 seems to have a significant importance to the spread of COVID-19 infections world-wide, hence proper guidance to building engineers or facility managers, on how to prevent on-site transmission, is essential.<br>
For information on the Airborne Transmission of SARS-CoV-2, feel free to check out the HSE Seminar: <a href=https://cds.cern.ch/record/2743403>https://cds.cern.ch/record/2743403</a>.<br>
For information on the Airborne Transmission of SARS-CoV-2, feel free to check out the special issue on the Interface Focus journal from Royal Society publishing: <a href=https://royalsocietypublishing.org/toc/rsfs/2022/12/2>Interface Focus: Volume 12, Issue 2</a> and an CERN HSE Seminar: <a href=https://cds.cern.ch/record/2743403>https://cds.cern.ch/record/2743403</a>.<br>
<br><br>
<h1 class="paragraph-title">What is CARA?</h1><br>
CARA stands for COVID Airborne Risk Assessment and was developed in the spring of 2020 to better understand and quantify the risk of long-range airborne spread of SARS-CoV-2 virus in workplaces. CARA comes with different applications that allow more or less flexibility in the input parameters:
@ -17,8 +17,8 @@ CARA stands for COVID Airborne Risk Assessment and was developed in the spring o
<li><a href='/expert-app'>CARA expert app</a></li>
</ul>
The mathematical and physical model simulate the long-range airborne spread of SARS-CoV-2 virus in a finite volume, assuming a homogenous mixture, and estimates the risk of COVID-19 airborne transmission therein. The results DO NOT include short-range airborne exposure (where the physical distance plays a factor) nor the other known modes of SARS-CoV-2 transmission. Hence, the output from this model is only valid when the other recommended public health & safety instructions are observed, such as adequate physical distancing, good hand hygiene and other barrier measures.<br>
<p>The methodology, mathematical equations and parameters of the model are described here in the CARA paper: <a href="https://cds.cern.ch/record/2756083"> CERN-OPEN-2021-004</a>.</p>
The mathematical and physical model simulate the long-range airborne spread of SARS-CoV-2 virus in a finite volume, assuming a homogenous mixture, and estimates the risk of COVID-19 airborne transmission therein. The results DO NOT include (for now) short-range airborne exposure (where the physical distance plays a factor) nor the other known modes of SARS-CoV-2 transmission. Hence, the output from this model is only valid when the other recommended public health & safety instructions are observed, such as adequate physical distancing, good hand hygiene and other barrier measures.<br>
<p>The methodology, mathematical equations and parameters of the model are published here in the CARA paper: <a href="https://doi.org/10.1098/rsfs.2021.0076"> Modelling airborne transmission of SARS-CoV-2 using CARA: risk assessment for enclosed spaces</a>.</p>
The model used is based on scientific publications relating to airborne transmission of infectious diseases, virology, epidemiology and aerosol science. It can be used to compare the effectiveness of different airborne-related risk mitigation measures.
@ -48,11 +48,15 @@ Although the user is able to calculate the infection probability of a stand-alon
<h2 class="paragraph-title">Code Contributors:</h2><br>
{{ text_blocks['Code Contributors'] }}
<br>
<h1 class="paragraph-title">Acknowledgements:</h1><br>
{{ text_blocks['Acknowledgements'] }}
<br>
<a id="references_block" style="color:#2f4858"><h1 class="paragraph-title">References:</h1></a><br>
{{ text_blocks['References'] }}
<br>
<h3 class="acknowledgements">Acknowledgements:</h3><br>
<details>
<summary>Click to expand</summary>
<br>
{{ text_blocks['Acknowledgements'] }}
</details>
<div class="text-component text-component-page clearfix"></div>
<br>

View file

@ -49,10 +49,35 @@
{% block cara_at_cern %}
{% endblock cara_at_cern %}
<br>
<div>
<h2 class="paragraph-title">Reference & Citation</h2><br>
<p>
<b>For use of the CARA model:</b><br>
<ul>
<li> Henriques A, Mounet N, Aleixo L, Elson P, Devine J, Azzopardi G, Andreini M, Rognlien M, Tarocco N, Tang J. (2022).
Modelling airborne transmission of SARS-CoV-2 using CARA: risk assessment for enclosed spaces.
<i>Interface Focus</i> <b>12</b>: 20210076. <a href=https://doi.org/10.1098/rsfs.2021.0076>doi.org/10.1098/rsfs.2021.0076</a> </li>
<a href=https://royalsocietypublishing.org/action/showCitFormats?doi=10.1098%2Frsfs.2021.0076><i>Download citation</i></a>
</ul>
<b>For use of the CARA web app:</b><br>
<ul>
<li>CARA COVID Airborne Risk Assessment tool</li>
© Copyright 2020-2021 CERN. All rights not expressly granted are reserved.<br>
Licensed under the Apache License, Version 2.0<br>
<a href=https://gitlab.cern.ch/cara/cara/-/blob/master/LICENSE><i>LICENSE</i></a>
</ul>
</p>
</div>
<br>
<h3 class="paragraph-title">Acknowledgements</h3><br>
{{ text_blocks['Acknowledgements'] }}
<h3 class="acknowledgements">Acknowledgements</h3><br>
<details>
<summary>Click to expand</summary>
<br>
<em>{{ text_blocks['Acknowledgements'] }}</em>
</details>
<span style="height: 3vh; display: block;"></span>
</div>
</div>

View file

@ -34,26 +34,26 @@
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-link"><a href="/" class="{{ "header-navbar nav-link active" if "home/" == active_page else "header-navbar nav-link" }}">HOME</a></li>
<li class="nav-link"><a href="/" class="{{ "header-navbar nav-link active" if "home/" == active_page else "header-navbar nav-link" }}">Home</a></li>
<div id="apps_dropdown">
<li class="nav-item dropdown p-2">
<a class="nav-link dropdown-toggle {{ "header-navbar nav-link active" if "calculator/" in active_page else "header-navbar nav-link" }}" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
APPS
Apps
</a>
<ul class="dropdown-menu dropwown-navbar-colors" style="min-width: 14rem;" aria-labelledby="navbarDropdown">
<li><a href="{{ calculator_prefix }}" class="{{ "header-navbar nav-link active" if "calculator/" == active_page else "header-navbar nav-link" }}">CARA CALCULATOR</a></li>
<li><a href="{{ calculator_prefix }}/user-guide" style="margin-left: 4rem" class="{{ "header-navbar nav-link active" if "user-guide" in active_page else "header-navbar nav-link" }}">USER GUIDE</a></li>
<li><a href="/expert-app" class="{{ "header-navbar nav-link active" if "/expert-app" == active_page else "header-navbar nav-link" }}">EXPERT APP (BETA)</a></li>
<ul class="dropdown-menu dropwown-navbar-colors text-right" style="min-width: 12rem;" aria-labelledby="navbarDropdown">
<li><a href="{{ calculator_prefix }}" class="{{ "header-navbar nav-link active" if "calculator/" == active_page else "header-navbar nav-link" }}">CARA Calculator</a></li>
<li><a href="{{ calculator_prefix }}/user-guide" style="margin-left: 2rem" class="{{ "header-navbar nav-link active" if "user-guide" in active_page else "header-navbar nav-link" }}">User Guide</a></li>
<li><a href="/expert-app" class="{{ "header-navbar nav-link active" if "/expert-app" == active_page else "header-navbar nav-link" }}">Expert app (beta)</a></li>
</ul>
</li>
</div>
<div id="mobile_calculator_option">
<li class="nav-link"><a href="{{ calculator_prefix }}" class="{{ "header-navbar nav-link active" if "calculator/" == active_page else "header-navbar nav-link" }}">CARA CALCULATOR</a></li>
<li class="nav-link"><a href="{{ calculator_prefix }}/user-guide" class="{{ "header-navbar nav-link active" if "user-guide" in active_page else "header-navbar nav-link" }}">USER GUIDE</a></li>
<li class="nav-link"><a href="{{ calculator_prefix }}" class="{{ "header-navbar nav-link active" if "calculator/" == active_page else "header-navbar nav-link" }}">CARA Calculator</a></li>
<li class="nav-link"><a href="{{ calculator_prefix }}/user-guide" class="{{ "header-navbar nav-link active" if "user-guide" in active_page else "header-navbar nav-link" }}">User Guide</a></li>
</div>
{% block covid_information%}
{% endblock covid_information%}
<li class="nav-link"><a href="/about" class="{{ "header-navbar nav-link active" if "about" == active_page else "header-navbar nav-link" }}">ABOUT</a></li>
<li class="nav-link"><a href="/about" class="{{ "header-navbar nav-link active" if "about" == active_page else "header-navbar nav-link" }}">About</a></li>
{% if user.is_authenticated() %}
<li class="nav-item dropdown p-2">
<a class="nav-link active dropdown-toggle d-inline-block" href="https://cern.ch/users-portal" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

View file

@ -1,5 +1,10 @@
{% extends "base/calculator.report.html.j2" %}
{% if ((prob_inf > 10) or (expected_new_cases >= 1)) %} {% set cern_level = 'red' %}
{% elif (2 <= prob_inf <= 10) %} {% set cern_level = 'orange' %}
{% else %} {% set cern_level = 'green' %}
{% endif %}
{% block report_preamble_navtab %}
<li class="nav-item">
<a class="nav-link" id="rules-tab" data-toggle="tab" href="#rules" role="tab" aria-controls="rules" aria-selected="false">Applicable Rules</a>
@ -7,9 +12,9 @@
{% endblock report_preamble_navtab %}
{% block warning_animation %}
{% if ((prob_inf > 10) or (expected_new_cases >= 1)) %} {% set warning_color= 'bg-danger' %}
{% elif (2 <= prob_inf <= 10) %} {% set warning_color = 'bg-warning' %}
{% elif (prob_inf < 2) %} {% set warning_color = 'bg-success' %}
{% if cern_level == 'red' %} {% set warning_color= 'bg-danger' %}
{% elif cern_level == 'orange' %} {% set warning_color = 'bg-warning' %}
{% elif cern_level == 'green' %} {% set warning_color = 'bg-success' %}
{% endif %}
<div class="intro-banner-vdo-play-btn {{warning_color}} m-auto d-flex align-items-center justify-content-center">
@ -23,18 +28,18 @@
{% block report_summary %}
<div class="flex-row align-self-center">
{% if ((prob_inf > 10) or (expected_new_cases >= 1)) %}
{% if cern_level == 'red' %}
<div class="alert alert-danger mb-0" role="alert">
<strong>Not Acceptable:</strong>
Taking into account the uncertainties tied to the model variables, in this scenario, the <b>probability of one exposed occupant getting infected is {{ prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases is {{ expected_new_cases | float_format }}</b>*.
</div>
{% elif 2 <= prob_inf <= 10 %}
{% elif cern_level == 'orange' %}
<div class="alert alert-warning mb-0" role="alert">
<strong>Attention:</strong>
Taking into account the uncertainties tied to the model variables, in this scenario, the <b>probability of one exposed occupant getting infected is {{ prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases is {{ expected_new_cases | float_format }}</b>*.
</div>
{% elif prob_inf < 2 %}
{% elif cern_level == 'green' %}
<div class="alert alert-success mb-0" role="alert">
<strong>Acceptable:</strong>
Taking into account the uncertainties tied to the model variables, in this scenario, the <b>probability of one exposed occupant getting infected is {{ prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases is {{ expected_new_cases | float_format }}</b>*.
@ -67,13 +72,13 @@
{% endblock report_summary %}
{% block report_summary_footnote %}
{% if ((prob_inf > 10) or (expected_new_cases >= 1)) %}
{% if cern_level == 'red' %}
This exceeds the authorised risk threshold or number of expected new cases.
The risk level must be reduced before this activity can be undertaken.
{% elif (2 <= prob_inf <= 10) %}
{% elif cern_level == 'orange' %}
This activity has an elevated level of risk, ALARA principles must be applied to minimise the level of risk before undertaking the activity.
See the footnotes for more details on the ALARA principles.
{% elif (prob_inf < 2) %}
{% elif cern_level == 'green' %}
This level of risk is within acceptable parameters, no further actions are required.
{% endif %}
{% endblock report_summary_footnote %}

View file

@ -1,5 +1,5 @@
{% extends "base/layout.html.j2" %}
{% block covid_information %}
<li class="nav-link"><a href="https://hse.cern/covid-19-information" class="header-navbar nav-link">COVID INFORMATION</a></li>
<li class="nav-link"><a href="https://hse.cern/covid-19-information" class="header-navbar nav-link">COVID information</a></li>
{% endblock covid_information %}

View file

@ -20,7 +20,7 @@
## Acknowledgements
We wish to thank CERNs HSE Unit, Beams Department, Experimental Physics Department, Information Technology Department, Industry, Procurement and Knowledge Transfer Department and International Relations Sector for their support to the study. Thanks to Doris Forkel-Wirth, Benoit Delille, Walid Fadel, Olga Beltramello, Letizia Di Giulio, Evelyne Dho, Wayne Salter, Benoit Salvant and colleagues from the COVID working group for providing expert advice and extensively testing the model. Finally, we wish to thank Fabienne Landua and the design service for preparing the illustrations and Alessandro Raimondo, Ana Padua and Manuela Cirilli from the Knowledge Transfer Group for their continuous support. Our compliments towards the work and research performed by world leading scientists in this domain: Dr. Julian Tang, Prof. Manuel Gameiro, Dr. Linsey Marr, Prof. Jose Jimenez, Prof. Lidia Morawska, Prof. Yuguo Li et al. their scientific contribution was indispensable for this project.
We wish to thank CERNs HSE Unit, Beams Department, Experimental Physics Department, Information Technology Department, Industry, Procurement and Knowledge Transfer Department and International Relations Sector for their support to the study. Thanks to Doris Forkel-Wirth, Benoit Delille, Walid Fadel, Olga Beltramello, Letizia Di Giulio, Evelyne Dho, Wayne Salter, Benoit Salvant and colleagues from the COVID working group for providing expert advice and extensively testing the model. Finally, we wish to thank Fabienne Landua and the design service for preparing the illustrations and Alessandro Raimondo and Manuela Cirilli from the Knowledge Transfer Group for their continuous support. Our compliments towards the work and research performed by world leading scientists in this domain: Dr. Julian Tang, Prof. Manuel Gameiro, Dr. Linsey Marr, Prof. Lidia Morawska, Prof. Yuguo Li, and others their scientific contribution was indispensable for this project.
## References