Merge branch 'feature/server_side_validation' into 'master'

Server side validation improvements

Closes #132

See merge request cara/cara!381
This commit is contained in:
Andre Henriques 2022-08-24 16:40:57 +02:00
commit 2d83cfe3b6
2 changed files with 114 additions and 0 deletions

View file

@ -185,6 +185,10 @@ class FormData:
return form_dict
def validate(self):
# Validate number of infected <= number of total people
if self.infected_people > self.total_people:
raise ValueError('Number of infected people cannot be more than number of total people.')
# Validate time intervals selected by user
time_intervals = [
['exposed_start', 'exposed_finish'],
@ -202,6 +206,43 @@ class FormData:
raise ValueError(
f"{start_name} must be less than {end_name}. Got {start} and {end}.")
def validate_lunch(start, finish):
lunch_start = getattr(self, f'{population}_lunch_start')
lunch_finish = getattr(self, f'{population}_lunch_finish')
return (start <= lunch_start <= finish and
start <= lunch_finish <= finish)
def get_lunch_mins(population):
lunch_mins = 0
if getattr(self, f'{population}_lunch_option'):
lunch_mins = getattr(self, f'{population}_lunch_finish') - getattr(self, f'{population}_lunch_start')
return lunch_mins
def get_coffee_mins(population):
coffee_mins = 0
if getattr(self, f'{population}_coffee_break_option') != 'coffee_break_0':
coffee_mins = COFFEE_OPTIONS_INT[getattr(self, f'{population}_coffee_break_option')] * getattr(self, f'{population}_coffee_duration')
return coffee_mins
def get_activity_mins(population):
return getattr(self, f'{population}_finish') - getattr(self, f'{population}_start')
populations = ['exposed', 'infected'] if self.infected_dont_have_breaks_with_exposed else ['exposed']
for population in populations:
# Validate lunch time within the activity times.
if (getattr(self, f'{population}_lunch_option') and
not validate_lunch(getattr(self, f'{population}_start'), getattr(self, f'{population}_finish'))
):
raise ValueError(
f"{population} lunch break must be within presence times."
)
# Length of breaks < length of activity
if (get_lunch_mins(population) + get_coffee_mins(population)) >= get_activity_mins(population):
raise ValueError(
f"Length of breaks >= Length of {population} presence."
)
validation_tuples = [('activity_type', ACTIVITY_TYPES),
('exposed_coffee_break_option', COFFEE_OPTIONS_INT),
('infected_coffee_break_option', COFFEE_OPTIONS_INT),
@ -229,6 +270,11 @@ class FormData:
"window_opening_regime cannot be 'not-applicable' if "
"ventilation_type is 'natural_ventilation'"
)
if (self.window_opening_regime == 'windows_open_periodically' and
self.windows_duration > self.windows_frequency):
raise ValueError(
'Duration cannot be bigger than frequency.'
)
if (self.ventilation_type == 'mechanical_ventilation'
and self.mechanical_ventilation_type == 'not-applicable'):

View file

@ -167,6 +167,13 @@ def test_ventilation_window_hepa(baseline_form: model_generator.FormData):
assert ventilation == baseline_vent
def test_infected_less_than_total_people(baseline_form: model_generator.FormData):
baseline_form.total_people = 10
baseline_form.infected_people = 11
with pytest.raises(ValueError, match='Number of infected people cannot be more than number of total people.'):
baseline_form.validate()
def present_times(interval: models.Interval) -> models.BoundarySequence_t:
assert isinstance(interval, models.SpecificInterval)
return interval.present_times
@ -255,6 +262,59 @@ def test_exposed_present_lunch_end_before_beginning(baseline_form: model_generat
baseline_form.validate()
@pytest.mark.parametrize(
"exposed_lunch_start, exposed_lunch_finish",
[
[8, 14], # lunch_start before the presence begining
[19, 20], # lunch_start after the presence finishing
[7, 8], # lunch_finish before the presence begining
[9, 20], # lunch_finish after the presence finishing
],
)
def test_exposed_presence_lunch_break(baseline_form: model_generator.FormData, exposed_lunch_start, exposed_lunch_finish):
baseline_form.exposed_lunch_start = minutes_since_midnight(exposed_lunch_start * 60)
baseline_form.exposed_lunch_finish = minutes_since_midnight(exposed_lunch_finish * 60)
with pytest.raises(ValueError, match='exposed lunch break must be within presence times.'):
baseline_form.validate()
@pytest.mark.parametrize(
"infected_lunch_start, infected_lunch_finish",
[
[8, 14], # lunch_start before the presence begining
[19, 20], # lunch_start after the presence finishing
[7, 8], # lunch_finish before the presence begining
[9, 20], # lunch_finish after the presence finishing
],
)
def test_infected_presence_lunch_break(baseline_form: model_generator.FormData, infected_lunch_start, infected_lunch_finish):
baseline_form.infected_lunch_start = minutes_since_midnight(infected_lunch_start * 60)
baseline_form.infected_lunch_finish = minutes_since_midnight(infected_lunch_finish * 60)
with pytest.raises(ValueError, match='infected lunch break must be within presence times.'):
baseline_form.validate()
def test_exposed_breaks_length(baseline_form: model_generator.FormData):
baseline_form.exposed_coffee_break_option = 'coffee_break_4'
baseline_form.exposed_coffee_duration = 30
baseline_form.exposed_start = minutes_since_midnight(10 * 60)
baseline_form.exposed_finish = minutes_since_midnight(11 * 60)
baseline_form.exposed_lunch_option = False
with pytest.raises(ValueError, match='Length of breaks >= Length of exposed presence.'):
baseline_form.validate()
def test_infected_breaks_length(baseline_form: model_generator.FormData):
baseline_form.infected_start = minutes_since_midnight(9 * 60)
baseline_form.infected_finish = minutes_since_midnight(12 * 60)
baseline_form.infected_lunch_start = minutes_since_midnight(10 * 60)
baseline_form.infected_lunch_finish = minutes_since_midnight(11 * 60)
baseline_form.infected_coffee_break_option = 'coffee_break_4'
baseline_form.infected_coffee_duration = 30
with pytest.raises(ValueError, match='Length of breaks >= Length of infected presence.'):
baseline_form.validate()
@pytest.fixture
def coffee_break_between_1045_and_1115(baseline_form: model_generator.FormData):
baseline_form.exposed_coffee_break_option = 'coffee_break_1'
@ -435,6 +495,14 @@ def test_key_validation_natural_ventilation_window_opening_regime_na(baseline_fo
model_generator.FormData.from_dict(baseline_form_data)
def test_natural_ventilation_window_opening_periodically(baseline_form: model_generator.FormData):
baseline_form.window_opening_regime = 'windows_open_periodically'
baseline_form.windows_duration = 20
baseline_form.windows_frequency = 10
with pytest.raises(ValueError, match='Duration cannot be bigger than frequency.'):
baseline_form.validate()
def test_key_validation_mech_ventilation_type_na(baseline_form_data):
baseline_form_data['ventilation_type'] = 'mechanical_ventilation'
baseline_form_data['mechanical_ventilation_type'] = 'not-applicable'