diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index 06c29229..aa161dbc 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -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 diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index a4d88bd9..59ab1302 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -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 diff --git a/cara/apps/static/css/style.css b/cara/apps/static/css/style.css index 008f833a..022b4ca6 100644 --- a/cara/apps/static/css/style.css +++ b/cara/apps/static/css/style.css @@ -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; } } diff --git a/cara/apps/static/images/masks/ffp2.png b/cara/apps/static/images/masks/ffp2.png new file mode 100644 index 00000000..57de5fc1 Binary files /dev/null and b/cara/apps/static/images/masks/ffp2.png differ diff --git a/cara/apps/static/images/masks/t1.png b/cara/apps/static/images/masks/t1.png new file mode 100644 index 00000000..187031f3 Binary files /dev/null and b/cara/apps/static/images/masks/t1.png differ diff --git a/cara/apps/templates/base/calculator.form.html.j2 b/cara/apps/templates/base/calculator.form.html.j2 index 1947ebb3..e9b6cb18 100644 --- a/cara/apps/templates/base/calculator.form.html.j2 +++ b/cara/apps/templates/base/calculator.form.html.j2 @@ -268,11 +268,23 @@

@@ -295,7 +307,7 @@
- +
@@ -310,7 +322,7 @@ - +
@@ -533,8 +545,8 @@
  • Library = all seated, no talking, just breathing,
  • Laboratory = light physical activity, talking 50% of the time,
  • Workshop = moderate physical activity, talking 50% of the time,
  • -
  • Training = trainer standing and talking, rest seated and talking quietly. - Trainer assumed infected (worst case scenario),
  • +
  • Conference/Training = speaker/trainer standing and talking, rest seated and talking quietly. + Speaker/Trainer assumed infected (worst case scenario),
  • Gym = heavy exercise, no talking, just breathing.
  • Activity breaks:
    diff --git a/cara/apps/templates/base/calculator.report.html.j2 b/cara/apps/templates/base/calculator.report.html.j2 index fdccca1f..84fac045 100644 --- a/cara/apps/templates/base/calculator.report.html.j2 +++ b/cara/apps/templates/base/calculator.report.html.j2 @@ -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" %} diff --git a/cara/apps/templates/base/index.html.j2 b/cara/apps/templates/base/index.html.j2 index 4497fd60..4e1ba69a 100644 --- a/cara/apps/templates/base/index.html.j2 +++ b/cara/apps/templates/base/index.html.j2 @@ -11,12 +11,11 @@
    -
    +
    -
    +

    Introduction


    @@ -43,7 +42,7 @@


    - +

    diff --git a/cara/apps/templates/base/layout.html.j2 b/cara/apps/templates/base/layout.html.j2 index 1fddbec9..cfa9bb09 100644 --- a/cara/apps/templates/base/layout.html.j2 +++ b/cara/apps/templates/base/layout.html.j2 @@ -35,19 +35,39 @@
    diff --git a/cara/apps/templates/base/userguide.html.j2 b/cara/apps/templates/base/userguide.html.j2 index 4d32707a..74e6452d 100644 --- a/cara/apps/templates/base/userguide.html.j2 +++ b/cara/apps/templates/base/userguide.html.j2 @@ -159,9 +159,9 @@ Within the number of people occupying the space, you should specify how many are
  • Control Room (night shift) = All persons seated, all talking 10% of the time. Everyone (exposed and infected occupants) is treated the same in this model.
  • Lab = 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.
  • Workshop = 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.
  • -
  • Training = 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.
  • +
  • Conference/Training = 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.
  • Gym = All persons are doing heavy exercise and breathing (not talking). Everyone (exposed and infected occupants) is treated the same in this model.

  • Timings

    @@ -212,7 +212,7 @@ If not, then you can input separate breaks. This is particularly different when

    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.

    -

    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). +

    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

    For the time being only the Type 1 surgical and FFP2 masks can be selected.


    diff --git a/cara/models.py b/cara/models.py index 272b6b47..012e453e 100644 --- a/cara/models.py +++ b/cara/models.py @@ -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))) diff --git a/cara/tests/apps/calculator/test_model_generator.py b/cara/tests/apps/calculator/test_model_generator.py index 6ee821c0..dd635946 100644 --- a/cara/tests/apps/calculator/test_model_generator.py +++ b/cara/tests/apps/calculator/test_model_generator.py @@ -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, diff --git a/server-performance-tests/README.md b/server-performance-tests/README.md new file mode 100644 index 00000000..7bf5e43b --- /dev/null +++ b/server-performance-tests/README.md @@ -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. \ No newline at end of file diff --git a/server-performance-tests/locust.py b/server-performance-tests/locust.py new file mode 100644 index 00000000..1cee4ae0 --- /dev/null +++ b/server-performance-tests/locust.py @@ -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) \ No newline at end of file