Merge branch 'master' into feature/base_theme_improvements

This commit is contained in:
Luis Aleixo 2021-12-16 12:13:39 +00:00
commit abb428635e
14 changed files with 149 additions and 51 deletions

View file

@ -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

View file

@ -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

View file

@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -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>

View file

@ -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" %}

View file

@ -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>

View file

@ -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>

View file

@ -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 &amp; 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>

View file

@ -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)))

View file

@ -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,

View 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.

View 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)