Merge branch 'master' into feature/base_theme_improvements
This commit is contained in:
commit
abb428635e
14 changed files with 149 additions and 51 deletions
|
|
@ -303,7 +303,7 @@ class FormData:
|
|||
# Initializes a ventilation instance as a window if 'natural_ventilation' is selected, or as a HEPA-filter otherwise
|
||||
if self.ventilation_type == 'natural_ventilation':
|
||||
if self.window_opening_regime == 'windows_open_periodically':
|
||||
window_interval = models.PeriodicInterval(self.windows_frequency, self.windows_duration)
|
||||
window_interval = models.PeriodicInterval(self.windows_frequency, self.windows_duration, min(self.infected_start, self.exposed_start))
|
||||
else:
|
||||
window_interval = always_on
|
||||
|
||||
|
|
|
|||
|
|
@ -308,9 +308,9 @@ class ReportGenerator:
|
|||
context['permalink'] = generate_permalink(base_url, self.calculator_prefix, form)
|
||||
context['calculator_prefix'] = self.calculator_prefix
|
||||
context['scale_warning'] = {
|
||||
'level': 'yellow-2',
|
||||
'incidence_rate': 'lower than 25 new cases per 100 000 inhabitants',
|
||||
'onsite_access': 'of about 8000',
|
||||
'level': 'orange-3',
|
||||
'incidence_rate': 'somewhere in between 25 and 100 new cases per 100 000 inhabitants',
|
||||
'onsite_access': 'of about 5000',
|
||||
'threshold': ''
|
||||
}
|
||||
return context
|
||||
|
|
|
|||
|
|
@ -170,6 +170,10 @@ body {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.mask_icons {
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
/*===== FIXED BACKGROUND IMG =====*/
|
||||
|
||||
.fixed-background {
|
||||
|
|
@ -230,6 +234,10 @@ footer img {
|
|||
width: 25%;
|
||||
}
|
||||
|
||||
#mobile_calculator_option {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nat_vent_image {
|
||||
height: 15em;
|
||||
}
|
||||
|
|
@ -242,7 +250,7 @@ footer img {
|
|||
height: 6em;
|
||||
margin: 1%;
|
||||
}
|
||||
#mobile-app-buttons {
|
||||
#calculator_app_button {
|
||||
display: none!important;
|
||||
}
|
||||
.feedback {
|
||||
|
|
@ -282,6 +290,9 @@ footer img {
|
|||
.nav-link {
|
||||
padding: .5rem .5rem!important;
|
||||
}
|
||||
#apps_dropdown {
|
||||
display: none;
|
||||
}
|
||||
#report_version {
|
||||
font-size: .5rem;
|
||||
}
|
||||
|
|
@ -298,13 +309,6 @@ footer img {
|
|||
#mobile_link {
|
||||
display: inline!important;
|
||||
}
|
||||
#desktop_logo {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
#mobile_logo {
|
||||
display: block!important;
|
||||
}
|
||||
.feedback {
|
||||
float:right;
|
||||
font-size:.75rem;
|
||||
|
|
@ -324,16 +328,16 @@ footer img {
|
|||
|
||||
} */
|
||||
|
||||
/* Large (lg) devices (desktops, 992px and up) */
|
||||
@media (max-width: 992px) {
|
||||
#download-pdf {
|
||||
/* Large (lg) devices (tablets) */
|
||||
@media (max-width: 64em) {
|
||||
.expert_app_button {
|
||||
display: none;
|
||||
}
|
||||
#link_reproduce_results {
|
||||
display: none;
|
||||
#desktop_logo {
|
||||
display: none!important;
|
||||
}
|
||||
#mobile_link {
|
||||
display: inline!important;
|
||||
#mobile_logo {
|
||||
display: block!important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
cara/apps/static/images/masks/ffp2.png
Normal file
BIN
cara/apps/static/images/masks/ffp2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
BIN
cara/apps/static/images/masks/t1.png
Normal file
BIN
cara/apps/static/images/masks/t1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -268,11 +268,23 @@
|
|||
<label for="mask_off">No</label><br>
|
||||
|
||||
<div id="DIVmasks_used" style="display:none">
|
||||
Type of masks used:
|
||||
<input type="radio" id="mask_type_1" name="mask_type" value="Type I" checked="checked" onclick="require_fields(this)">
|
||||
<label for="mask_type_1">Type 1</label>
|
||||
<input class="ml-2" type="radio" id="mask_type_ffp2" name="mask_type" value="FFP2" onclick="require_fields(this)">
|
||||
<label for="mask_type_ffp2">FFP2</label><br>
|
||||
<div class='sub_title'>Type of masks used:</div>
|
||||
<div class='split'>
|
||||
<div>
|
||||
<input type="radio" id="mask_type_1" name="mask_type" value="Type I" checked="checked" onclick="require_fields(this)">
|
||||
<label for="mask_type_1">
|
||||
Surgical/Type I
|
||||
<img class="mask_icons" src="/static/images/masks/t1.png">
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="mask_type_ffp2" name="mask_type" value="FFP2" onclick="require_fields(this)">
|
||||
<label for="mask_type_ffp2">
|
||||
Respirator/FFP2
|
||||
<img class="mask_icons" src="/static/images/masks/ffp2.png">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr width="80%">
|
||||
|
||||
|
|
@ -295,7 +307,7 @@
|
|||
<div class="col-sm-6 align-self-center"><input type="number" id="infected_people" class="form-control" name="infected_people" min=1 value=1 required></div>
|
||||
</div>
|
||||
|
||||
<span id="training_limit_error" class="red_text" hidden>Training activities limited to 1 infected<br></span>
|
||||
<span id="training_limit_error" class="red_text" hidden>Conference/Training activities limited to 1 infected<br></span>
|
||||
<hr width="80%">
|
||||
|
||||
<div class="form-group row">
|
||||
|
|
@ -310,7 +322,7 @@
|
|||
<option value="library">Library</option>
|
||||
<option value="lab">Laboratory</option>
|
||||
<option value="workshop">Workshop</option>
|
||||
<option value="training">Training</option>
|
||||
<option value="training">Conference/Training</option>
|
||||
<option value="gym">Gym</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -533,8 +545,8 @@
|
|||
<li>Library = all seated, no talking, just breathing,</li>
|
||||
<li>Laboratory = light physical activity, talking 50% of the time,</li>
|
||||
<li>Workshop = moderate physical activity, talking 50% of the time,</li>
|
||||
<li>Training = trainer standing and talking, rest seated and talking quietly.
|
||||
Trainer assumed infected (worst case scenario),</li>
|
||||
<li>Conference/Training = speaker/trainer standing and talking, rest seated and talking quietly.
|
||||
Speaker/Trainer assumed infected (worst case scenario),</li>
|
||||
<li>Gym = heavy exercise, no talking, just breathing.</li>
|
||||
</ul>
|
||||
<b>Activity breaks:</b><br>
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@
|
|||
{% elif form.activity_type == "workshop" %}
|
||||
Workshop = assembly workshop environment, all persons doing moderate physical activity, talking 50% of the time.
|
||||
{% elif form.activity_type == "training" %}
|
||||
Training – one person (the trainer) standing, talking, all others seated, talking quietly (whispering). It is assumed the trainer is the infected person, for the worst case scenario.
|
||||
Conference/Training – one person (the speaker/trainer) standing, talking, all others seated, talking quietly (whispering). It is assumed the speaker/trainer is the infected person, for the worst case scenario.
|
||||
{% elif form.activity_type == "lab" %}
|
||||
Laboratory = Lab or technical environment, all persons doing light physical activity, talking 50% of the time.
|
||||
{% elif form.activity_type == "gym" %}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@
|
|||
</header>
|
||||
|
||||
<div class="container container--padding">
|
||||
<div class="d-flex flex-row mb-3 justify-content-around" id="mobile-app-buttons">
|
||||
<div class="d-flex mb-3 justify-content-center" id="calculator_app_button">
|
||||
<div><a href="{{ calculator_prefix }}" role="button" class="btn btn-outline-primary"><div class="d-flex d-row"><i class="icon-calculator"></i><span class="pl-1">Calculator</div></a></div>
|
||||
<div><a href="https://cara.web.cern.ch/expert-app" role="button" class="btn btn-outline-secondary"><div class="d-flex d-row"><i class="icon-expert"></i><span class="pl-1">Expert (beta)</div></a></div>
|
||||
</div>
|
||||
<div class="split">
|
||||
<div class="col-sm-8 pl-0">
|
||||
<div class="col-lg-8 col-md-7 pl-0">
|
||||
<h2 class="paragraph-title">Introduction</h2><br>
|
||||
<div>
|
||||
<p>
|
||||
|
|
@ -43,7 +42,7 @@
|
|||
<br>
|
||||
<div class="pr-3"><a href="{{ calculator_prefix }}" role="button" class="btn btn-lg btn-outline-primary"><div class="d-flex d-row"><i class="icon-calculator"></i><span class="pl-2">Calculator</div></a></div>
|
||||
<br>
|
||||
<div><a href="https://cara.web.cern.ch/expert-app" role="button" class="btn btn-lg btn-outline-secondary"><div class="d-flex d-row"><i class="icon-expert"></i><span class="pl-2">Expert (beta)</div></a></div>
|
||||
<div class="expert_app_button"><a href="https://cara.web.cern.ch/expert-app" role="button" class="btn btn-lg btn-outline-secondary"><div class="d-flex d-row"><i class="icon-expert"></i><span class="pl-2">Expert (beta)</div></a></div>
|
||||
</div>
|
||||
<hr width="95%">
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -35,19 +35,39 @@
|
|||
<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-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
|
||||
</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>
|
||||
</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
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
{% 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">
|
||||
Signed in as: {{ user.username }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropwown-navbar-colors" style="min-width: 14rem;" aria-labelledby="navbarDropdown">
|
||||
<li><a href="/auth/logout" class="nav-link ml-2">Sign out</a></li>
|
||||
</ul>
|
||||
{# Sent to Piwik for statistics #}
|
||||
<script>
|
||||
var AuthUserDomain = "{{ user.domain() }}";
|
||||
</script>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -159,9 +159,9 @@ Within the number of people occupying the space, you should specify how many are
|
|||
<li><strong>Control Room (night shift)</strong> = All persons seated, all talking 10% of the time. Everyone (exposed and infected occupants) is treated the same in this model.</li>
|
||||
<li><strong>Lab</strong> = Based on a typical lab or technical working area, all persons are doing light activity and talking 50% of the time. Everyone (exposed and infected occupants) is treated the same in this model.</li>
|
||||
<li><strong>Workshop</strong> = Based on a mechanical assembly workshop or equipment installation scenario, all persons are doing moderate activity and talking 50% of the time. This activity is equally applicable to bicycling, or walking on a gradient, in the LHC tunnels. Everyone (exposed and infected occupants) is treated the same in this model.</li>
|
||||
<li><strong>Training</strong> = Based on a typical training course scenario.
|
||||
One individual (the trainer) is standing and talking, with all other individuals seated and talking quietly (whispering).
|
||||
In this case it is assumed that the infected person is the trainer, because this is the worst case in terms of viral shedding.</li>
|
||||
<li><strong>Conference/Training</strong> = Based on a typical conference/training course scenario.
|
||||
One individual (the speaker/trainer) is standing and talking, with all other individuals seated and talking quietly (whispering).
|
||||
In this case it is assumed that the infected person is the speaker/trainer, because this is the worst case in terms of viral shedding.</li>
|
||||
<li><strong>Gym</strong> = All persons are doing heavy exercise and breathing (not talking). Everyone (exposed and infected occupants) is treated the same in this model.</li>
|
||||
</ul>
|
||||
<br><h3>Timings</h3>
|
||||
|
|
@ -212,7 +212,7 @@ If not, then you can input separate breaks. This is particularly different when
|
|||
<p>The model allows for a simulation with either a continuous wearing of face masks throughout the duration of the event, or have the removed at all times - i.e. all occupants (infected and exposed alike) wear or not masks for the duration of the simulation.
|
||||
Please bear in mind the user inputs shall be aligned with the current applicable public health & safety instructions.
|
||||
Please check what are the applicable rules, before deciding which assumptions are used for the simulation.</p>
|
||||
<p>If you have selected the Training activity type, this equates to the trainer and all participants either wearing masks throughout the training (Yes), or removing them when seated/standing at their socially distanced positions within the training room (No).
|
||||
<p>If you have selected the Conference/Training activity type, this equates to the speakr/trainer and all participants either wearing masks throughout the conference/training (Yes), or removing them when seated/standing at their socially distanced positions within the conference/training room (No).
|
||||
Please confirm what are the applicable rules, before deciding which assumptions are used for the simulation</p>
|
||||
<p>For the time being only the Type 1 surgical and FFP2 masks can be selected.</p>
|
||||
<br>
|
||||
|
|
|
|||
|
|
@ -123,11 +123,14 @@ class PeriodicInterval(Interval):
|
|||
#: occurring, a value of 0 signifies that the event never happens.
|
||||
duration: float
|
||||
|
||||
#: Time at which the first person (infected or exposed) arrives at the enclosed space.
|
||||
start: float = 0.0
|
||||
|
||||
def boundaries(self) -> BoundarySequence_t:
|
||||
if self.period == 0 or self.duration == 0:
|
||||
return tuple()
|
||||
result = []
|
||||
for i in np.arange(0, 24, self.period / 60):
|
||||
for i in np.arange(self.start, 24, self.period / 60):
|
||||
# NOTE: It is important that the time type is float, not np.float, in
|
||||
# order to allow hashability (for caching).
|
||||
result.append((float(i), float(i+self.duration/60)))
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ def test_ventilation_slidingwindow(baseline_form: model_generator.FormData):
|
|||
assert isinstance(baseline_window, models.SlidingWindow)
|
||||
|
||||
window = models.SlidingWindow(
|
||||
active=models.PeriodicInterval(period=120, duration=10),
|
||||
active=models.PeriodicInterval(period=120, duration=10, start=minutes_since_midnight(9 * 60)),
|
||||
inside_temp=models.PiecewiseConstant((0, 24), (293,)),
|
||||
outside_temp=baseline_window.outside_temp,
|
||||
window_height=1.6, opening_length=0.6,
|
||||
|
|
@ -80,7 +80,7 @@ def test_ventilation_hingedwindow(baseline_form: model_generator.FormData):
|
|||
assert isinstance(baseline_window, models.HingedWindow)
|
||||
|
||||
window = models.HingedWindow(
|
||||
active=models.PeriodicInterval(period=120, duration=10),
|
||||
active=models.PeriodicInterval(period=120, duration=10, start=minutes_since_midnight(9 * 60)),
|
||||
inside_temp=models.PiecewiseConstant((0, 24), (293,)),
|
||||
outside_temp=baseline_window.outside_temp,
|
||||
window_height=1.6, window_width=1., opening_length=0.6,
|
||||
|
|
@ -141,7 +141,7 @@ def test_ventilation_window_hepa(baseline_form: model_generator.FormData):
|
|||
|
||||
# Now build the equivalent ventilation instance directly, and compare.
|
||||
window = models.SlidingWindow(
|
||||
active=models.PeriodicInterval(period=120, duration=10),
|
||||
active=models.PeriodicInterval(period=120, duration=10, start=minutes_since_midnight(9 * 60)),
|
||||
inside_temp=models.PiecewiseConstant((0, 24), (293,)),
|
||||
outside_temp=baseline_window.outside_temp,
|
||||
window_height=1.6, opening_length=0.6,
|
||||
|
|
|
|||
18
server-performance-tests/README.md
Normal file
18
server-performance-tests/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# locust
|
||||
|
||||
A simple open source load testing tool that allows to define user behavior.
|
||||
|
||||
In order to set it up for the first time, we followed the documentation at https://locust.io/. In particular, we:
|
||||
|
||||
* Defined a class for the users that will be simulating.
|
||||
* Defined a ``wait_time`` variable that will make the simulated users wait between the specified seconds after each task executed.
|
||||
* Decorated our method with ``@Task`` that creates a micro-thread that calls this method.
|
||||
* Defined the ``self.client`` attribute that makes it possible to make HTTP calls that will be logged by Locust.
|
||||
|
||||
To use, uncomment the desired method on ``lucust.py``` file, open the terminal on this folder and run the following command:
|
||||
|
||||
``locust -f locust.py --host https://cara.web.cern.ch``
|
||||
|
||||
Then, open up a browser and point it to http://localhost:8089.
|
||||
By default we pointed out the test to our own web server.
|
||||
``Start swarming`` will trigger the simulation.
|
||||
42
server-performance-tests/locust.py
Normal file
42
server-performance-tests/locust.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from locust import HttpUser, task, between
|
||||
from gevent.pool import Group
|
||||
import time
|
||||
|
||||
'''
|
||||
Method no. 1 - Simulation with each single user
|
||||
running x requests in parallel.
|
||||
Specify the desired number of parallel requests in
|
||||
"num_of_parallel_requests" variable (35 by default).
|
||||
|
||||
This method was used in simulations with one single
|
||||
user perfoming 35 requests in parallel.
|
||||
'''
|
||||
# num_of_parallel_requests = 35
|
||||
# class User(HttpUser):
|
||||
# wait_time = between(0.05, 0.1)
|
||||
|
||||
# @task(1)
|
||||
# def test_api(self):
|
||||
# group = Group()
|
||||
# for i in range(0, num_of_parallel_requests):
|
||||
# group.spawn(lambda:self.client.get("/calculator-open/baseline-model/result"))
|
||||
# group.join()
|
||||
# while(1):
|
||||
# time.sleep(1)
|
||||
|
||||
'''
|
||||
Method no. 2 - Simulation with different users
|
||||
running x requests concurrently.
|
||||
With this method, each user is intended to
|
||||
perform one single request.
|
||||
|
||||
This method was used in simulations with different
|
||||
number of users requesting once at the same time.
|
||||
'''
|
||||
# class User(HttpUser):
|
||||
|
||||
# @task(1)
|
||||
# def test_api(self):
|
||||
# self.client.get("/calculator-open/baseline-model/result")
|
||||
# while(1):
|
||||
# time.sleep(1)
|
||||
Loading…
Reference in a new issue