From 80ead5e58de152ddf48415125d9cf2efdc4167b2 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Sat, 21 Nov 2020 11:06:25 +0100 Subject: [PATCH 1/5] New tests for long/many coffee breaks (test_model_generator.py) --- .../apps/calculator/test_model_generator.py | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/cara/tests/apps/calculator/test_model_generator.py b/cara/tests/apps/calculator/test_model_generator.py index 64c741c9..9f354d45 100644 --- a/cara/tests/apps/calculator/test_model_generator.py +++ b/cara/tests/apps/calculator/test_model_generator.py @@ -116,7 +116,7 @@ def test_infected_present_intervals(baseline_form): baseline_form.lunch_finish = 13 * 60 + 30 baseline_form.infected_start = 10 * 60 baseline_form.infected_finish = 15 * 60 - correct = ((10, 11), (11.25, 12.5), (13.5, 15.0)) + correct = ((10, 10+37/60), (10+52/60, 12.5), (13.5, 15.0)) assert baseline_form.infected_present_interval().present_times == correct @@ -129,9 +129,32 @@ def test_exposed_present_intervals(baseline_form): baseline_form.lunch_finish = 13 * 60 + 30 baseline_form.infected_start = 10 * 60 baseline_form.infected_finish = 15 * 60 - correct = ((9, 11), (11.25, 12.5), (13.5, 15), (15.25, 17.0)) + correct = ((9, 10+37/60), (10+52/60, 12.5), (13.5, 15+7/60), (15+22/60, 17.0)) assert baseline_form.exposed_present_interval().present_times == correct + +def test_coffee_lunch_breaks(baseline_form): + baseline_form.coffee_duration = 30 + baseline_form.coffee_breaks = 4 + baseline_form.activity_start = 9 * 60 + baseline_form.activity_finish = 18 * 60 + baseline_form.lunch_start = 12 * 60 + 30 + baseline_form.lunch_finish = 13 * 60 + 30 + correct = ((9, 9+50/60), (10+20/60, 11+10/60), (11+40/60, 12+30/60), + (13+30/60, 14+40/60), (15+10/60, 16+20/60), (16+50/60, 18)) + np.testing.assert_allclose(baseline_form.exposed_present_interval().present_times, correct, rtol=1e-14) + + +def test_coffee_breaks(baseline_form): + baseline_form.coffee_duration = 10 + baseline_form.coffee_breaks = 4 + baseline_form.activity_start = 9 * 60 + baseline_form.activity_finish = 10 * 60 + baseline_form.lunch_option = False + correct = ((9, 9+4/60), (9+14/60, 9+18/60), (9+28/60, 9+32/60), (9+42/60, 9+46/60), (9+56/60, 10)) + np.testing.assert_allclose(baseline_form.exposed_present_interval().present_times, correct, rtol=1e-14) + + def test_key_validation(baseline_form_data): baseline_form_data['activity_type'] = 'invalid key' with pytest.raises(ValueError): From 3ee7931a6e8069c6513866f32d4ce1760c8ee058 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Sat, 21 Nov 2020 11:11:58 +0100 Subject: [PATCH 2/5] Correction of the bug on coffee breaks in calculator --- cara/apps/calculator/model_generator.py | 30 ++++++++++++------- cara/apps/calculator/templates/report.html.j2 | 13 ++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index bdb1cf80..e467c5dc 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -242,27 +242,37 @@ class FormData: ) return exposed - def coffee_break_times(self) -> typing.Tuple[typing.Tuple[int, int]]: + def coffee_break_times(self, start, finish, nbreaks) -> typing.Tuple[typing.Tuple[int, int]]: if not self.coffee_breaks: return () - coffee_period = (self.activity_finish - self.activity_start) // self.coffee_breaks + break_delay = ((finish - start) - (nbreaks * self.coffee_duration)) // (nbreaks+1) coffee_times = [] - for minute in range(self.activity_start, self.activity_finish, coffee_period): - start = minute + coffee_period // 2 - end = start + self.coffee_duration - coffee_times.append((start, end)) + end = start + for n in range(nbreaks): + begin = end + break_delay + end = begin + self.coffee_duration + coffee_times.append((begin, end)) return tuple(coffee_times) def present_interval(self, start, finish) -> models.Interval: leave_times = [] enter_times = [] if self.lunch_option: + for coffee_start, coffee_end in self.coffee_break_times( + self.activity_start,self.lunch_start,self.coffee_breaks // 2): + leave_times.append(coffee_start) + enter_times.append(coffee_end) leave_times.append(self.lunch_start) enter_times.append(self.lunch_finish) - - for coffee_start, coffee_end in self.coffee_break_times(): - leave_times.append(coffee_start) - enter_times.append(coffee_end) + for coffee_start, coffee_end in self.coffee_break_times( + self.lunch_finish,self.activity_finish,self.coffee_breaks // 2): + leave_times.append(coffee_start) + enter_times.append(coffee_end) + else: + for coffee_start, coffee_end in self.coffee_break_times( + self.activity_start,self.activity_finish,self.coffee_breaks): + leave_times.append(coffee_start) + enter_times.append(coffee_end) # These lists represent the times where the infected person leaves or enters the room, respectively, sorted in # reverse order. Note that these lists allows the person to "leave" when they should not even be present in the diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index 451e60f2..8d50348b 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -121,9 +121,18 @@ each of {{ form.coffee_duration }} minutes duration

{% endif %} @@ -178,4 +187,4 @@ We do not assume responsibility for any injury or damage to persons or property arising out of or related to any use of this app.

- \ No newline at end of file + From a42789812652879b3d52c48aef7c6443c99929ef Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Mon, 23 Nov 2020 09:21:31 +0100 Subject: [PATCH 3/5] Improvement of the coffee breaks implementation (avoiding too much logic in report generation) - thanks to P. Elson --- cara/apps/calculator/model_generator.py | 48 +++++++++++-------- cara/apps/calculator/templates/report.html.j2 | 11 +---- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index e467c5dc..bd63f625 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -242,37 +242,43 @@ class FormData: ) return exposed - def coffee_break_times(self, start, finish, nbreaks) -> typing.Tuple[typing.Tuple[int, int]]: - if not self.coffee_breaks: - return () - break_delay = ((finish - start) - (nbreaks * self.coffee_duration)) // (nbreaks+1) - coffee_times = [] + def _compute_breaks_in_interval(self, start, finish, n_breaks) -> typing.Tuple[typing.Tuple[int, int]]: + break_delay = ((finish - start) - (n_breaks * self.coffee_duration)) // (n_breaks+1) + break_times = [] end = start - for n in range(nbreaks): + for n in range(n_breaks): begin = end + break_delay end = begin + self.coffee_duration - coffee_times.append((begin, end)) - return tuple(coffee_times) + break_times.append((begin, end)) + return tuple(break_times) + + def coffee_break_times(self) -> typing.Tuple[typing.Tuple[int, int]]: + if not self.coffee_breaks: + return () + if self.lunch_option: + n_morning_breaks = self.coffee_breaks // 2 + breaks = ( + self._compute_breaks_in_interval( + self.activity_start, self.lunch_start, n_morning_breaks + ) + + self._compute_breaks_in_interval( + self.lunch_finish, self.activity_finish, self.coffee_breaks - n_morning_breaks + ) + ) + else: + breaks = self._compute_breaks_in_interval(self.activity_start, self.activity_finish, self.coffee_breaks) + return breaks def present_interval(self, start, finish) -> models.Interval: leave_times = [] enter_times = [] if self.lunch_option: - for coffee_start, coffee_end in self.coffee_break_times( - self.activity_start,self.lunch_start,self.coffee_breaks // 2): - leave_times.append(coffee_start) - enter_times.append(coffee_end) leave_times.append(self.lunch_start) enter_times.append(self.lunch_finish) - for coffee_start, coffee_end in self.coffee_break_times( - self.lunch_finish,self.activity_finish,self.coffee_breaks // 2): - leave_times.append(coffee_start) - enter_times.append(coffee_end) - else: - for coffee_start, coffee_end in self.coffee_break_times( - self.activity_start,self.activity_finish,self.coffee_breaks): - leave_times.append(coffee_start) - enter_times.append(coffee_end) + + for coffee_start, coffee_end in self.coffee_break_times(): + leave_times.append(coffee_start) + enter_times.append(coffee_end) # These lists represent the times where the infected person leaves or enters the room, respectively, sorted in # reverse order. Note that these lists allows the person to "leave" when they should not even be present in the diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index 8d50348b..3701f2e2 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -121,18 +121,9 @@ each of {{ form.coffee_duration }} minutes duration

    - {% if form.lunch_option%} - {%- for start_time, end_time in form.coffee_break_times(form.activity_start,form.lunch_start,form.coffee_breaks//2) %} + {%- for start_time, end_time in form.coffee_break_times() %}
  • Coffee break {{ loop.index }}: Start: {{ start_time | minutes_to_time }}    End: {{ end_time | minutes_to_time }}

  • {%- endfor %} - {%- for start_time, end_time in form.coffee_break_times(form.lunch_finish,form.activity_finish,form.coffee_breaks//2) %} -
  • Coffee break {{ loop.index }}: Start: {{ start_time | minutes_to_time }}    End: {{ end_time | minutes_to_time }}

  • - {%- endfor %} - {% else%} - {%- for start_time, end_time in form.coffee_break_times(form.activity_start,form.activity_finish,form.coffee_breaks) %} -
  • Coffee break {{ loop.index }}: Start: {{ start_time | minutes_to_time }}    End: {{ end_time | minutes_to_time }}

  • - {%- endfor %} - {% endif %}
{% endif %} From a325a34d6679b2b2bb69ac810fd0a782bb326b8f Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Mon, 23 Nov 2020 11:33:36 +0100 Subject: [PATCH 4/5] Adding test with unbalance between time before and after lunch (for calculator) --- cara/tests/apps/calculator/test_model_generator.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cara/tests/apps/calculator/test_model_generator.py b/cara/tests/apps/calculator/test_model_generator.py index 9f354d45..57779c6b 100644 --- a/cara/tests/apps/calculator/test_model_generator.py +++ b/cara/tests/apps/calculator/test_model_generator.py @@ -145,6 +145,17 @@ def test_coffee_lunch_breaks(baseline_form): np.testing.assert_allclose(baseline_form.exposed_present_interval().present_times, correct, rtol=1e-14) +def test_coffee_lunch_breaks_unbalance(baseline_form): + baseline_form.coffee_duration = 30 + baseline_form.coffee_breaks = 2 + baseline_form.activity_start = 9 * 60 + baseline_form.activity_finish = 13 * 60 + 30 + baseline_form.lunch_start = 12 * 60 + 30 + baseline_form.lunch_finish = 13 * 60 + 30 + correct = ((9, 9+50/60), (10+20/60, 11+10/60), (11+40/60, 12+30/60) ) + np.testing.assert_allclose(baseline_form.exposed_present_interval().present_times, correct, rtol=1e-14) + + def test_coffee_breaks(baseline_form): baseline_form.coffee_duration = 10 baseline_form.coffee_breaks = 4 From 8c093e6d43b570040089351bb97c2dc4ee0cee8e Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Mon, 23 Nov 2020 11:35:10 +0100 Subject: [PATCH 5/5] Different splitting for coffee breaks w.r.t lunch, as a function of the fraction of the total time spent before lunch (from P. Elson / G. Azzopardi) --- cara/apps/calculator/model_generator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index bd63f625..720e7179 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -256,7 +256,10 @@ class FormData: if not self.coffee_breaks: return () if self.lunch_option: - n_morning_breaks = self.coffee_breaks // 2 + time_before_lunch = self.lunch_start - self.activity_start + time_after_lunch = self.activity_finish - self.lunch_finish + before_lunch_frac = time_before_lunch / (time_before_lunch + time_after_lunch) + n_morning_breaks = round(self.coffee_breaks * before_lunch_frac) breaks = ( self._compute_breaks_in_interval( self.activity_start, self.lunch_start, n_morning_breaks