From 7d8657a88723e2830a0221115f9323c614754c68 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 15 Nov 2023 16:10:51 +0000 Subject: [PATCH 01/53] Initial commit --- pyEpiabm/pyEpiabm/core/cell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index a8225c92..63ac8097 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -32,6 +32,7 @@ def __init__(self, loc: typing.Tuple[float, float] = (0, 0)): """ self.location = loc self.id = hash(self) + print(self.id) self.microcells = [] self.persons = [] self.places = [] From 3eea9115d922fa22ed1d23cb68a204ea72dc2eb0 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 15 Nov 2023 17:20:19 +0000 Subject: [PATCH 02/53] Added household id --- pyEpiabm/pyEpiabm/core/cell.py | 3 +++ pyEpiabm/pyEpiabm/core/household.py | 14 ++++++++++++++ pyEpiabm/pyEpiabm/core/microcell.py | 6 +++++- .../pyEpiabm/routine/file_population_config.py | 6 +++--- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index 63ac8097..354fa48e 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -198,3 +198,6 @@ def find_nearby_cells(self, other_cells): self.nearby_cell_distances[cell2.id] = distance # Dict of near neighbours, cells which are closer than the # cutoff for cross-cell infection + + +print(Cell().id) diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index 7f057771..27ebff40 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -29,6 +29,7 @@ def __init__(self, microcell, loc: typing.Tuple[float, float], Household's base infectiousness """ + self.id = "" self.persons = [] self.susceptible_persons = [] self.location = loc @@ -104,3 +105,16 @@ def remove_household(self): """ self.microcell.households.remove(self) self.cell.households.remove(self) + + def set_id(self, id: str): + """Updates ID of household (i.e. for input from file). + Format of ID - for example 3.1.2 represents household 2 within microcell 1 + within cell 3. + + Parameters + ---------- + id : str + Identity of household + + """ + self.id = id diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 3aade19d..6ef0aca5 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -123,7 +123,7 @@ def add_place(self, n: int, loc: typing.Tuple[float, float], self.cell.places.append(p) self.places.append(p) - def add_household(self, people: list): + def add_household(self, people: list, household_id: int): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. @@ -131,10 +131,14 @@ def add_household(self, people: list): ---------- people : list List of :class:`People` to add to household + household_id : int + Integer representing this specific household within the microcell """ if len(people) != 0: household = Household(self, loc=self.location) + cell_id = self.cell.id + household.set_id(str(cell_id) + "." + str(self.id) + "." + str(household_id)) for person in people: household.add_person(person) else: diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 9741a444..80a92ca9 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -195,14 +195,14 @@ def add_households(microcell: Microcell, household_number: int): people_number = len(people_list) household_split = np.random.multinomial(people_number, q, size=1)[0] - for j in range(household_number): - people_in_household = household_split[j] + for household_id in range(household_number): + people_in_household = household_split[household_id] household_people = [] for i in range(people_in_household): person_choice = people_list[0] people_list.remove(person_choice) household_people.append(person_choice) - microcell.add_household(household_people) + microcell.add_household(household_people, household_id) @staticmethod @log_exceptions() From 34db412a2cc3519d10ef703e2bbbb838175ffe9b Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 15 Nov 2023 17:59:17 +0000 Subject: [PATCH 03/53] Added set_id --- pyEpiabm/pyEpiabm/core/person.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 43a742ba..7e27203b 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -54,6 +54,7 @@ def __init__(self, microcell, age_group=None): self.key_worker = False self.date_positive = None self.is_vaccinated = False + self.id = "" self.set_random_age(age_group) @@ -223,3 +224,17 @@ def remove_person(self): self.microcell.cell.persons.remove(self) self.microcell.persons.remove(self) self.household.persons.remove(self) + + def set_id(self, id: str): + """Updates ID of person (i.e. for input from file). + ID format: 4.3.2.1 represents cell 4, microcell 3 within this cell, + household 2 within this microcell, and person 1 within this + household + + Parameters + ---------- + id : str + Identity of person + + """ + self.id = id \ No newline at end of file From 3f25185bd78d7397b7568a273815bb5b84fe0032 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 15 Nov 2023 18:00:42 +0000 Subject: [PATCH 04/53] Added ID for person and households --- pyEpiabm/pyEpiabm/core/microcell.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 6ef0aca5..fdf78e06 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -139,8 +139,11 @@ def add_household(self, people: list, household_id: int): household = Household(self, loc=self.location) cell_id = self.cell.id household.set_id(str(cell_id) + "." + str(self.id) + "." + str(household_id)) - for person in people: - household.add_person(person) + for i in range(len(people)): + household.add_person(people[i]) + people[i].set_id(household.id + "." + str(i)) + + else: logging.info("Cannot create an empty household") From 12e0e9b52e250966c7259956c83aecc55873ba4e Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 16 Nov 2023 16:34:11 +0000 Subject: [PATCH 05/53] Added some tests and a regex check --- pyEpiabm/pyEpiabm/core/household.py | 7 +++++-- pyEpiabm/pyEpiabm/core/microcell.py | 1 - pyEpiabm/pyEpiabm/core/person.py | 7 +++++-- .../tests/test_unit/test_core/test_household.py | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index 27ebff40..6be9a28a 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -4,6 +4,7 @@ import typing from numbers import Number +import re from pyEpiabm.property import InfectionStatus @@ -109,7 +110,7 @@ def remove_household(self): def set_id(self, id: str): """Updates ID of household (i.e. for input from file). Format of ID - for example 3.1.2 represents household 2 within microcell 1 - within cell 3. + within cell 3. The id will only be changed if it has the correct expression. Parameters ---------- @@ -117,4 +118,6 @@ def set_id(self, id: str): Identity of household """ - self.id = id + # May want to set upper limit on the number of digits + if re.match("\\d+\\.\\d+\\.\\d+", id): + self.id = id diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index fdf78e06..ca5be15a 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -143,7 +143,6 @@ def add_household(self, people: list, household_id: int): household.add_person(people[i]) people[i].set_id(household.id + "." + str(i)) - else: logging.info("Cannot create an empty household") diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 7e27203b..20a29df1 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -3,6 +3,7 @@ # import random +import re from pyEpiabm.property import InfectionStatus @@ -229,7 +230,7 @@ def set_id(self, id: str): """Updates ID of person (i.e. for input from file). ID format: 4.3.2.1 represents cell 4, microcell 3 within this cell, household 2 within this microcell, and person 1 within this - household + household. The id will only be changed if there is a match. Parameters ---------- @@ -237,4 +238,6 @@ def set_id(self, id: str): Identity of person """ - self.id = id \ No newline at end of file + # May want to set upper limit on the number of digits + if re.match("\\d+\\.\\d+\\.\\d+\\.\\d+", id): + self.id = id diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index 04ffb98f..3c60ae3c 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -53,6 +53,20 @@ def test_remove_susceptible_person(self): self.assertEqual(len(subject.susceptible_persons), 0) self.assertEqual(subject.susceptible_persons, []) + def test_set_id(self): + subject = pe.Household(self.microcell, (1, 1)) + self.assertEqual(subject.id, "") + subject.set_id("a.b.c") + self.assertEqual(subject.id, "") + subject.set_id("0.0.") + self.assertEqual(subject.id, "") + subject.set_id("0.0.0.0") + self.assertEqual(subject.id, "") + subject.set_id("0.0.0") + self.assertEqual(subject.id, "0.0.0") + subject.set_id("10.7.20") + self.assertEqual(subject.id, "10.7.20") + if __name__ == '__main__': unittest.main() From ffdb83f660541465dc3a3dc5a6d0b89f83c9d559 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Thu, 16 Nov 2023 16:37:13 +0000 Subject: [PATCH 06/53] Merge conflict --- pyEpiabm/pyEpiabm/core/microcell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index fdf78e06..ca5be15a 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -143,7 +143,6 @@ def add_household(self, people: list, household_id: int): household.add_person(people[i]) people[i].set_id(household.id + "." + str(i)) - else: logging.info("Cannot create an empty household") From 830988c7ce12ab2348b5d029d9877ec0fb8d331e Mon Sep 17 00:00:00 2001 From: Kit Gallagher Date: Thu, 16 Nov 2023 18:30:38 +0000 Subject: [PATCH 07/53] Resolved circular import errors --- pyEpiabm/pyEpiabm/core/_compartment_counter.py | 6 +++--- pyEpiabm/pyEpiabm/core/cell.py | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/_compartment_counter.py b/pyEpiabm/pyEpiabm/core/_compartment_counter.py index 5049e640..91d76077 100644 --- a/pyEpiabm/pyEpiabm/core/_compartment_counter.py +++ b/pyEpiabm/pyEpiabm/core/_compartment_counter.py @@ -7,7 +7,7 @@ from pyEpiabm.property import InfectionStatus -import pyEpiabm.core +from .parameters import Parameters class _CompartmentCounter: @@ -28,9 +28,9 @@ def __init__(self, identifier: str): # Identifier self._identifier = identifier # Number of age groups - if pyEpiabm.core.Parameters.instance().use_ages: + if Parameters.instance().use_ages: self.nb_age_groups =\ - len(pyEpiabm.core.Parameters.instance().age_proportions) + len(Parameters.instance().age_proportions) else: self.nb_age_groups = 1 diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index 354fa48e..276d9ca1 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -7,11 +7,11 @@ from queue import Queue from numbers import Number -from pyEpiabm.core import Parameters from pyEpiabm.property import InfectionStatus from pyEpiabm.utility import DistanceFunctions from .microcell import Microcell +from .parameters import Parameters from .person import Person from ._compartment_counter import _CompartmentCounter @@ -198,6 +198,3 @@ def find_nearby_cells(self, other_cells): self.nearby_cell_distances[cell2.id] = distance # Dict of near neighbours, cells which are closer than the # cutoff for cross-cell infection - - -print(Cell().id) From 7322f07a33a86af90aa6098c2e66bf2064cb3a14 Mon Sep 17 00:00:00 2001 From: Kit Gallagher Date: Thu, 16 Nov 2023 19:00:01 +0000 Subject: [PATCH 08/53] Ensure unique cell ids for toy pop --- pyEpiabm/pyEpiabm/core/population.py | 3 ++- .../tests/test_unit/test_core/test_population.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/population.py b/pyEpiabm/pyEpiabm/core/population.py index 2887e95e..54285319 100644 --- a/pyEpiabm/pyEpiabm/core/population.py +++ b/pyEpiabm/pyEpiabm/core/population.py @@ -42,9 +42,10 @@ def add_cells(self, n): Number of empty :class:`Cell` s to add """ + base_num = len(self.cells) for i in range(n): self.cells.append(Cell()) - self.cells[i].set_id(i) + self.cells[i + base_num].set_id(i + base_num) def total_people(self): """Returns the total number of people in the population. diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py index 0b710bd9..53375ec4 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py @@ -19,11 +19,21 @@ def test_repr(self): self.assertEqual(repr(self.population), "Population with 0 cells.") - def test_add_cells(self, n=4): + def test_add_cells(self, n=4, m=3): population = pe.Population() self.assertEqual(len(population.cells), 0) + population.add_cells(n) self.assertEqual(len(population.cells), n) + self.assertEqual(population.cells[-1].id, n-1) + + population.add_cells(m) + self.assertEqual(len(population.cells), n+m) + self.assertEqual(population.cells[-1].id, n+m-1) + + # Check all cell IDs are unique + cell_ids = [cell.id for cell in population.cells] + self.assertEqual(len(cell_ids), len(set(cell_ids))) def test_total_people(self): self.assertEqual(self.population.total_people(), 0) From da3669ed2e21820a1adf97882a6e721ac39a941e Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Thu, 16 Nov 2023 21:13:18 +0000 Subject: [PATCH 09/53] Removed print statement --- pyEpiabm/pyEpiabm/core/cell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index 276d9ca1..d3e24632 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -32,7 +32,6 @@ def __init__(self, loc: typing.Tuple[float, float] = (0, 0)): """ self.location = loc self.id = hash(self) - print(self.id) self.microcells = [] self.persons = [] self.places = [] From bed0a99dd41f5fef257d53d2739a4cc4f2c9b380 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Thu, 16 Nov 2023 21:49:56 +0000 Subject: [PATCH 10/53] Updated ID definition --- pyEpiabm/pyEpiabm/core/microcell.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index ca5be15a..3349d235 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -33,12 +33,12 @@ def __init__(self, cell): Microcell's parent :class:`Cell` instance """ - self.id = hash(self) self.persons = [] self.places = [] self.households = [] self.cell = cell self.location = cell.location + self.id = str(self.cell.id) + "." + str(len(self.cell.microcells)) self.compartment_counter = _CompartmentCounter( f"Microcell {id(self)}") @@ -123,7 +123,7 @@ def add_place(self, n: int, loc: typing.Tuple[float, float], self.cell.places.append(p) self.places.append(p) - def add_household(self, people: list, household_id: int): + def add_household(self, people: list): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. @@ -131,17 +131,12 @@ def add_household(self, people: list, household_id: int): ---------- people : list List of :class:`People` to add to household - household_id : int - Integer representing this specific household within the microcell """ if len(people) != 0: household = Household(self, loc=self.location) - cell_id = self.cell.id - household.set_id(str(cell_id) + "." + str(self.id) + "." + str(household_id)) - for i in range(len(people)): - household.add_person(people[i]) - people[i].set_id(household.id + "." + str(i)) + for person in people: + household.add_person(person) else: logging.info("Cannot create an empty household") From 4be6a4ee9552d587ca5137afc58160aa03176a3d Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Thu, 16 Nov 2023 21:50:34 +0000 Subject: [PATCH 11/53] Updated test for ID --- pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index ca5d3c70..bf8cef91 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -23,7 +23,7 @@ def test_repr(self): "Microcell with 0 people at location (0, 0).") def test_set_id(self): - self.assertEqual(self.microcell.id, hash(self.microcell)) + self.assertEqual(self.microcell.id, str(self.cell.id) + "." + str(len(self.cell.microcells))) self.microcell.set_id(2.0) self.assertEqual(self.microcell.id, 2.0) From 38b302c9b1c2192b8e5a76eedf135aabb7cda88f Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 17 Nov 2023 15:58:48 +0000 Subject: [PATCH 12/53] Updated household id --- pyEpiabm/pyEpiabm/core/household.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index 6be9a28a..0eaf9df9 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -30,7 +30,6 @@ def __init__(self, microcell, loc: typing.Tuple[float, float], Household's base infectiousness """ - self.id = "" self.persons = [] self.susceptible_persons = [] self.location = loc @@ -39,6 +38,7 @@ def __init__(self, microcell, loc: typing.Tuple[float, float], self.cell = microcell.cell self.microcell = microcell self.isolation_location = False + self.id = self.microcell.id + "." + str(len(self.microcell.households)) if not (len(loc) == 2 and isinstance(loc[0], Number) and isinstance(loc[1], Number)): @@ -119,5 +119,5 @@ def set_id(self, id: str): """ # May want to set upper limit on the number of digits - if re.match("\\d+\\.\\d+\\.\\d+", id): + if re.match("^\\d+\\.\\d+\\.\\d+$", id): self.id = id From 8226f4684b2e9229495125abe2a27b6aa1632b04 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 17 Nov 2023 15:59:33 +0000 Subject: [PATCH 13/53] Updated set_id test --- .../pyEpiabm/tests/test_unit/test_core/test_household.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index 3c60ae3c..e60c5b48 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -55,13 +55,14 @@ def test_remove_susceptible_person(self): def test_set_id(self): subject = pe.Household(self.microcell, (1, 1)) - self.assertEqual(subject.id, "") + init_id = str(self.microcell.id) + "." + str(len(self.microcell.households)-1) + self.assertEqual(subject.id, init_id) subject.set_id("a.b.c") - self.assertEqual(subject.id, "") + self.assertEqual(subject.id, init_id) subject.set_id("0.0.") - self.assertEqual(subject.id, "") + self.assertEqual(subject.id, init_id) subject.set_id("0.0.0.0") - self.assertEqual(subject.id, "") + self.assertEqual(subject.id, init_id) subject.set_id("0.0.0") self.assertEqual(subject.id, "0.0.0") subject.set_id("10.7.20") From 2f4f2db4781b7380d19bf531881b57bc159d5302 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Fri, 17 Nov 2023 16:36:27 +0000 Subject: [PATCH 14/53] Updated regex --- pyEpiabm/pyEpiabm/core/household.py | 2 +- pyEpiabm/pyEpiabm/core/person.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index 6be9a28a..db629a58 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -119,5 +119,5 @@ def set_id(self, id: str): """ # May want to set upper limit on the number of digits - if re.match("\\d+\\.\\d+\\.\\d+", id): + if re.match("^\\d+\\.\\d+\\.\\d+$", id): self.id = id diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 20a29df1..01b5240e 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -239,5 +239,5 @@ def set_id(self, id: str): """ # May want to set upper limit on the number of digits - if re.match("\\d+\\.\\d+\\.\\d+\\.\\d+", id): + if re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", id): self.id = id From 5f9782fe0daca203621295112329445b52e10365 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 17 Nov 2023 19:18:19 +0000 Subject: [PATCH 15/53] self.id added --- pyEpiabm/pyEpiabm/core/person.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 20a29df1..9e65b21a 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -55,7 +55,7 @@ def __init__(self, microcell, age_group=None): self.key_worker = False self.date_positive = None self.is_vaccinated = False - self.id = "" + self.id = self.microcell.id + "." + "." + str(len(self.microcell.persons)) self.set_random_age(age_group) @@ -239,5 +239,5 @@ def set_id(self, id: str): """ # May want to set upper limit on the number of digits - if re.match("\\d+\\.\\d+\\.\\d+\\.\\d+", id): + if re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", id): self.id = id From f4406b2dc9ef4488a63f409e5a4bc0a36c2cf3cf Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 17 Nov 2023 19:19:05 +0000 Subject: [PATCH 16/53] Updated set_id test --- .../tests/test_unit/test_core/test_person.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py index bf3541e1..d38add9e 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py @@ -110,6 +110,18 @@ def test_vaccinate(self): self.assertTrue(self.person.is_vaccinated) self.assertEqual(self.person.date_vaccinated, 5) + def test_set_id(self): + init_id = self.microcell.id + "." + "." + str(len(self.microcell.persons)-1) + self.assertEqual(self.person.id, init_id) + self.person.set_id("0.1") + self.assertEqual(self.person.id, init_id) + self.person.set_id("1234") + self.assertEqual(self.person.id, init_id) + self.person.set_id("0.0.0.0.5") + self.assertEqual(self.person.id, init_id) + self.person.set_id("2.3.4.5") + self.assertEqual(self.person.id, "2.3.4.5") + if __name__ == '__main__': unittest.main() From 0a98755854deab802fbd61df1bd4fd6f18e6ae05 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 17 Nov 2023 19:24:56 +0000 Subject: [PATCH 17/53] Update add_household --- pyEpiabm/pyEpiabm/core/microcell.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 3349d235..b19a91a4 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -135,8 +135,10 @@ def add_household(self, people: list): """ if len(people) != 0: household = Household(self, loc=self.location) - for person in people: + for i in range(len(people)): + person = people[i] household.add_person(person) + person.set_id(household.id + "." + str(i)) else: logging.info("Cannot create an empty household") From a572114bfe680b44d8c139a526376a0f3fa962c5 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 17 Nov 2023 19:25:31 +0000 Subject: [PATCH 18/53] Updated add_household test --- pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index bf8cef91..f1d0f217 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -74,6 +74,9 @@ def test_add_household(self): self.assertEqual(len(self.microcell.households), 1) household = self.microcell.households[0] self.assertEqual(len(household.persons), 1) + for i in range(len(household.persons)): + person = self.microcell.persons[i] + self.assertEqual(person.id, household.id + "." + str(i)) @mock.patch('logging.info') def test_logging(self, mock_log): From 6d906776210d85a77d3895f16cb2d6dca76a2784 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Sun, 19 Nov 2023 20:21:29 +0000 Subject: [PATCH 19/53] Changed cell id to a string to match the other types, and fixed errors involving ids --- pyEpiabm/pyEpiabm/core/cell.py | 17 +++++++++--- pyEpiabm/pyEpiabm/core/household.py | 7 +++++ pyEpiabm/pyEpiabm/core/microcell.py | 16 ++++++++--- pyEpiabm/pyEpiabm/core/person.py | 9 ++++++- pyEpiabm/pyEpiabm/core/population.py | 2 +- .../routine/file_population_config.py | 27 +++++++++++++------ .../tests/test_unit/test_core/test_cell.py | 10 ++++--- .../test_unit/test_core/test_household.py | 10 +++---- .../test_unit/test_core/test_microcell.py | 6 ++--- .../tests/test_unit/test_core/test_person.py | 10 +++---- .../test_unit/test_core/test_population.py | 4 +-- .../test_file_population_config.py | 24 ++++++++++------- .../test_sweep/test_spatial_sweep.py | 2 +- 13 files changed, 95 insertions(+), 49 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index d3e24632..aa20a4fd 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -6,6 +6,7 @@ import numpy as np from queue import Queue from numbers import Number +import re from pyEpiabm.property import InfectionStatus from pyEpiabm.utility import DistanceFunctions @@ -31,7 +32,7 @@ def __init__(self, loc: typing.Tuple[float, float] = (0, 0)): """ self.location = loc - self.id = hash(self) + self.id = str(hash(self)) self.microcells = [] self.persons = [] self.places = [] @@ -70,16 +71,24 @@ def add_microcells(self, n): for _ in range(n): self.microcells.append(Microcell(self)) - def set_id(self, id: float): + def set_id(self, id: str): """Updates ID of cell (i.e. for input from file). Parameters ---------- - id : float + id : str Identity of cell """ - self.id = id + # Ensure id is a string + if not isinstance(id, str): + raise TypeError("id must be of type string") + + # May want to set upper limit on the number of digits + if re.match("^\\d+$", id): + self.id = id + else: + raise ValueError("id must take the correct form") def enqueue_person(self, person: Person): """Add person to queue for processing at end of iteration, provided diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index 0eaf9df9..ce42297b 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -118,6 +118,13 @@ def set_id(self, id: str): Identity of household """ + + # Ensure id is a string + if not isinstance(id, str): + raise TypeError("id must be of type string") + # May want to set upper limit on the number of digits if re.match("^\\d+\\.\\d+\\.\\d+$", id): self.id = id + else: + raise ValueError("id must take the correct form") diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index b19a91a4..4fccaf79 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -5,6 +5,7 @@ import typing from numbers import Number import logging +import re from pyEpiabm.property import InfectionStatus @@ -38,7 +39,7 @@ def __init__(self, cell): self.households = [] self.cell = cell self.location = cell.location - self.id = str(self.cell.id) + "." + str(len(self.cell.microcells)) + self.id = self.cell.id + "." + str(len(self.cell.microcells)) self.compartment_counter = _CompartmentCounter( f"Microcell {id(self)}") @@ -59,11 +60,20 @@ def set_id(self, id): Parameters ---------- - id : float + id : str Identity of microcell """ - self.id = id + + # Ensure id is a string + if not isinstance(id, str): + raise TypeError("id must be of type string") + + # Ensure that the id is of the correct form + if re.match("^\\d+\\.\\d+$", id): + self.id = id + else: + raise ValueError("id must take the correct form") def add_person(self, person): """Adds :class:`Person` with given :class:`InfectionStatus` and given diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 9e65b21a..7b87716a 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -238,6 +238,13 @@ def set_id(self, id: str): Identity of person """ + + # Ensure id is a string + if not isinstance(id, str): + raise TypeError("id must be of type string") + # May want to set upper limit on the number of digits - if re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", id): + if re.match("^\\d+\\.\\d+\\.\\d*\\.\\d+$", id): self.id = id + else: + raise ValueError("id must take the correct form") diff --git a/pyEpiabm/pyEpiabm/core/population.py b/pyEpiabm/pyEpiabm/core/population.py index 54285319..2fc005be 100644 --- a/pyEpiabm/pyEpiabm/core/population.py +++ b/pyEpiabm/pyEpiabm/core/population.py @@ -45,7 +45,7 @@ def add_cells(self, n): base_num = len(self.cells) for i in range(n): self.cells.append(Cell()) - self.cells[i + base_num].set_id(i + base_num) + self.cells[i + base_num].set_id(str(i + base_num)) def total_people(self): """Returns the total number of people in the population. diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 80a92ca9..e01cd1ab 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -84,8 +84,12 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): current_cell = None # Iterate through lines (one per microcell) for _, line in input.iterrows(): + + # Converting from float to string + cell_id_csv = str(int(line["cell"])) + microcell_id_csv = str(int(line["microcell"])) # Check if cell exists, or create it - cell = FilePopulationFactory.find_cell(new_pop, line["cell"], + cell = FilePopulationFactory.find_cell(new_pop, cell_id_csv, current_cell) if current_cell != cell: current_cell = cell @@ -96,13 +100,20 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): # Raise error if microcell exists, then create new one for microcell in cell.microcells: - if microcell.id == line["microcell"]: + + # The microcell id is a string separated by periods, so we + # must find the part which matches the csv input + id_parts = microcell.id.split(".") + if id_parts[-1] == microcell_id_csv: raise ValueError(f"Duplicate microcells {microcell.id}" + f" in cell {cell.id}") new_microcell = Microcell(cell) cell.microcells.append(new_microcell) - new_microcell.set_id(line["microcell"]) + + # Either keep the following line (sets id according to data frame input), or + # remove it (sets id according to current number of microcells in cell.microcells) + new_microcell.set_id(cell_id_csv + "." + microcell_id_csv) for column in input.columns.values: if hasattr(InfectionStatus, column): @@ -150,7 +161,7 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): return new_pop @staticmethod - def find_cell(population: Population, cell_id: float, current_cell: Cell): + def find_cell(population: Population, cell_id: str, current_cell: Cell): """Returns cell with given ID in population, creates one if current cell has another ID. As input is sorted on cell no cell will exist with that ID. @@ -159,7 +170,7 @@ def find_cell(population: Population, cell_id: float, current_cell: Cell): ---------- population : Population Population containing target cell - cell_id : float + cell_id : str ID for target cell current_cell : Cell or None Cell object of current cell @@ -195,14 +206,14 @@ def add_households(microcell: Microcell, household_number: int): people_number = len(people_list) household_split = np.random.multinomial(people_number, q, size=1)[0] - for household_id in range(household_number): - people_in_household = household_split[household_id] + for j in range(household_number): + people_in_household = household_split[j] household_people = [] for i in range(people_in_household): person_choice = people_list[0] people_list.remove(person_choice) household_people.append(person_choice) - microcell.add_household(household_people, household_id) + microcell.add_household(household_people) @staticmethod @log_exceptions() diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py index 0f9a9bf6..233767ca 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py @@ -15,7 +15,7 @@ def setUp(self) -> None: def test__init__(self): self.assertEqual(self.cell.location[0], 0) self.assertEqual(self.cell.location[1], 0) - self.assertEqual(self.cell.id, hash(self.cell)) + self.assertEqual(self.cell.id, str(hash(self.cell))) self.assertEqual(self.cell.microcells, []) self.assertEqual(self.cell.persons, []) self.assertEqual(self.cell.places, []) @@ -38,9 +38,11 @@ def test_set_location(self): self.assertRaises(ValueError, pe.Cell, ('1', 1)) def test_set_id(self): - self.assertEqual(self.cell.id, hash(self.cell)) - self.cell.set_id(2.0) - self.assertEqual(self.cell.id, 2.0) + self.assertEqual(self.cell.id, str(hash(self.cell))) + self.assertRaises(TypeError, self.cell.set_id, 2.0) + self.assertRaises(ValueError, self.cell.set_id, "2.0") + self.cell.set_id("2") + self.assertEqual(self.cell.id, "2") def test_add_microcells(self, n=4): self.assertEqual(len(self.cell.microcells), 0) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index e60c5b48..5cdd5f05 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -57,16 +57,14 @@ def test_set_id(self): subject = pe.Household(self.microcell, (1, 1)) init_id = str(self.microcell.id) + "." + str(len(self.microcell.households)-1) self.assertEqual(subject.id, init_id) - subject.set_id("a.b.c") - self.assertEqual(subject.id, init_id) - subject.set_id("0.0.") - self.assertEqual(subject.id, init_id) - subject.set_id("0.0.0.0") - self.assertEqual(subject.id, init_id) subject.set_id("0.0.0") self.assertEqual(subject.id, "0.0.0") subject.set_id("10.7.20") self.assertEqual(subject.id, "10.7.20") + self.assertRaises(TypeError, subject.set_id, 2.0) + self.assertRaises(ValueError, subject.set_id, "a.b.c") + self.assertRaises(ValueError, subject.set_id, "0.0.") + self.assertRaises(ValueError, subject.set_id, "0.0.0.0") if __name__ == '__main__': diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index f1d0f217..b7220e20 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -23,9 +23,9 @@ def test_repr(self): "Microcell with 0 people at location (0, 0).") def test_set_id(self): - self.assertEqual(self.microcell.id, str(self.cell.id) + "." + str(len(self.cell.microcells))) - self.microcell.set_id(2.0) - self.assertEqual(self.microcell.id, 2.0) + self.assertEqual(self.microcell.id, self.cell.id + "." + str(len(self.cell.microcells))) + self.assertRaises(TypeError, self.microcell.set_id, 2.0) + self.assertRaises(ValueError, self.microcell.set_id, "1.1.") def test_add_person(self): self.assertEqual(len(self.microcell.persons), 0) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py index d38add9e..fa0eee18 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py @@ -113,14 +113,12 @@ def test_vaccinate(self): def test_set_id(self): init_id = self.microcell.id + "." + "." + str(len(self.microcell.persons)-1) self.assertEqual(self.person.id, init_id) - self.person.set_id("0.1") - self.assertEqual(self.person.id, init_id) - self.person.set_id("1234") - self.assertEqual(self.person.id, init_id) - self.person.set_id("0.0.0.0.5") - self.assertEqual(self.person.id, init_id) self.person.set_id("2.3.4.5") self.assertEqual(self.person.id, "2.3.4.5") + self.assertRaises(TypeError, self.person.set_id, 2.0) + self.assertRaises(ValueError, self.person.set_id, "0.1") + self.assertRaises(ValueError, self.person.set_id, "1234") + self.assertRaises(ValueError, self.person.set_id, "0.0.0.0.5") if __name__ == '__main__': diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py index 53375ec4..5bcc106a 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_population.py @@ -25,11 +25,11 @@ def test_add_cells(self, n=4, m=3): population.add_cells(n) self.assertEqual(len(population.cells), n) - self.assertEqual(population.cells[-1].id, n-1) + self.assertEqual(population.cells[-1].id, str(n-1)) population.add_cells(m) self.assertEqual(len(population.cells), n+m) - self.assertEqual(population.cells[-1].id, n+m-1) + self.assertEqual(population.cells[-1].id, str(n+m-1)) # Check all cell IDs are unique cell_ids = [cell.id for cell in population.cells] diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index 09d7f604..447a82ea 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -61,9 +61,10 @@ def test_make_pop(self, mock_read): # Test cell_wise values for i in range(2): self.assertEqual(test_pop.cells[i].id, - self.input.get('cell')[i]) - self.assertEqual(test_pop.cells[i].microcells[0].id, - self.input.get('microcell')[i]) + str(int(self.input.get('cell')[i]))) + id_parts = test_pop.cells[i].microcells[0].id.split(".") + self.assertEqual(id_parts[-1], + str(int(self.input.get('microcell')[i]))) self.assertEqual(test_pop.cells[i].location[0], self.input.get('location_x')[i]) self.assertEqual(test_pop.cells[i].location[1], @@ -133,15 +134,15 @@ def test_duplicate_microcell(self, mock_read, mock_log): def test_find_cell(self): pop = pe.Population() pop.add_cells(2) - pop.cells[1].set_id(42) + pop.cells[1].set_id("42") - target_cell = FilePopulationFactory.find_cell(pop, 42, pop.cells[1]) - self.assertEqual(target_cell.id, 42) + target_cell = FilePopulationFactory.find_cell(pop, "42", pop.cells[1]) + self.assertEqual(target_cell.id, "42") self.assertEqual(hash(target_cell), hash(pop.cells[1])) self.assertEqual(len(pop.cells), 2) # No new cell created - new_cell = FilePopulationFactory.find_cell(pop, 43, pop.cells[1]) - self.assertEqual(new_cell.id, 43) + new_cell = FilePopulationFactory.find_cell(pop, "43", pop.cells[1]) + self.assertEqual(new_cell.id, "43") self.assertEqual(len(pop.cells), 3) # New cell created @patch("pandas.read_csv") @@ -217,8 +218,11 @@ def test_print_population(self, mock_copy, mock_read): actual_df = mock_copy.call_args.args[0] if version.parse(pd.__version__) >= version.parse("1.4.0"): expected_df = self.df.copy() - expected = expected_df.drop('household_number', axis=1) - actual = actual_df.drop('household_number', axis=1) + + # The type of the id is converted from float to string, so we + # drop the cell and microcell columns + expected = expected_df.drop(['cell', 'microcell', 'household_number'], axis=1) + actual = actual_df.drop(['cell', 'microcell', 'household_number'], axis=1) pd.testing.assert_frame_equal(actual, expected, check_dtype=False) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_spatial_sweep.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_spatial_sweep.py index d80e9861..53af2afa 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_spatial_sweep.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_spatial_sweep.py @@ -93,7 +93,7 @@ def test_nearby_cells(self, mock_dist): test_sweep = SpatialSweep() mock_dist.return_value = 2 test_sweep.bind_population(test_pop) - self.assertEqual(self.cell_inf.nearby_cell_distances, {1: 2}) + self.assertEqual(self.cell_inf.nearby_cell_distances, {"1": 2}) mock_dist.assert_called_with(self.cell_inf.location, self.cell_susc.location) self.assertEqual(mock_dist.call_count, From 839a71347f3370b0321e32c9b6a69e482a0e527f Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 10:45:43 +0000 Subject: [PATCH 20/53] Fixed line lengths --- pyEpiabm/pyEpiabm/core/household.py | 5 +++-- pyEpiabm/pyEpiabm/core/person.py | 17 +++++++++-------- .../pyEpiabm/routine/file_population_config.py | 5 +++-- .../tests/test_unit/test_core/test_household.py | 3 ++- .../tests/test_unit/test_core/test_microcell.py | 3 ++- .../tests/test_unit/test_core/test_person.py | 3 ++- .../test_routine/test_file_population_config.py | 6 ++++-- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index ce42297b..68ffd5bd 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -109,8 +109,9 @@ def remove_household(self): def set_id(self, id: str): """Updates ID of household (i.e. for input from file). - Format of ID - for example 3.1.2 represents household 2 within microcell 1 - within cell 3. The id will only be changed if it has the correct expression. + Format of ID - for example 3.1.2 represents household 2 within + microcell 1 within cell 3. The id will only be changed if it has the + correct expression. Parameters ---------- diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 7b87716a..f36b2bc9 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -55,7 +55,8 @@ def __init__(self, microcell, age_group=None): self.key_worker = False self.date_positive = None self.is_vaccinated = False - self.id = self.microcell.id + "." + "." + str(len(self.microcell.persons)) + self.id = self.microcell.id + "." + "." + \ + str(len(self.microcell.persons)) self.set_random_age(age_group) @@ -90,7 +91,7 @@ def is_symptomatic(self): """ return Person.is_infectious(self) and self.infection_status != \ - InfectionStatus.InfectASympt + InfectionStatus.InfectASympt def is_infectious(self): """Query if the person is currently infectious. @@ -112,7 +113,7 @@ def is_susceptible(self): Whether person is currently susceptible """ - return self.infection_status == InfectionStatus.\ + return self.infection_status == InfectionStatus. \ Susceptible def __repr__(self): @@ -141,10 +142,10 @@ def update_status(self, self.infection_status = new_status if self.infection_status == InfectionStatus.Susceptible and \ - self.household is not None: + self.household is not None: self.household.add_susceptible_person(self) if self.infection_status == InfectionStatus.Exposed and \ - self.household is not None: + self.household is not None: self.household.remove_susceptible_person(self) def add_place(self, place, person_group: int = 0): @@ -197,7 +198,7 @@ def is_place_closed(self, closure_place_type): """ if (hasattr(self.microcell, 'closure_start_time')) and ( - self.microcell.closure_start_time is not None): + self.microcell.closure_start_time is not None): for place_type in self.place_types: if place_type.value in closure_place_type: return True @@ -216,10 +217,10 @@ def remove_person(self): Used to remove travellers from the population. """ - self.microcell.cell.compartment_counter.\ + self.microcell.cell.compartment_counter. \ _increment_compartment(-1, self.infection_status, self.age_group) - self.microcell.compartment_counter.\ + self.microcell.compartment_counter. \ _increment_compartment(-1, self.infection_status, self.age_group) self.microcell.cell.persons.remove(self) diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index e01cd1ab..45762700 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -111,8 +111,9 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): new_microcell = Microcell(cell) cell.microcells.append(new_microcell) - # Either keep the following line (sets id according to data frame input), or - # remove it (sets id according to current number of microcells in cell.microcells) + # Either keep the following line (sets id according to data frame + # input), or remove it (sets id according to current number of + # microcells in cell.microcells) new_microcell.set_id(cell_id_csv + "." + microcell_id_csv) for column in input.columns.values: diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index 5cdd5f05..c7db4ad5 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -55,7 +55,8 @@ def test_remove_susceptible_person(self): def test_set_id(self): subject = pe.Household(self.microcell, (1, 1)) - init_id = str(self.microcell.id) + "." + str(len(self.microcell.households)-1) + init_id = str(self.microcell.id) + "." + \ + str(len(self.microcell.households)-1) self.assertEqual(subject.id, init_id) subject.set_id("0.0.0") self.assertEqual(subject.id, "0.0.0") diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index b7220e20..25f800a8 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -23,7 +23,8 @@ def test_repr(self): "Microcell with 0 people at location (0, 0).") def test_set_id(self): - self.assertEqual(self.microcell.id, self.cell.id + "." + str(len(self.cell.microcells))) + self.assertEqual(self.microcell.id, self.cell.id + "." + + str(len(self.cell.microcells))) self.assertRaises(TypeError, self.microcell.set_id, 2.0) self.assertRaises(ValueError, self.microcell.set_id, "1.1.") diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py index fa0eee18..d415ca32 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py @@ -111,7 +111,8 @@ def test_vaccinate(self): self.assertEqual(self.person.date_vaccinated, 5) def test_set_id(self): - init_id = self.microcell.id + "." + "." + str(len(self.microcell.persons)-1) + init_id = self.microcell.id + "." + "." + \ + str(len(self.microcell.persons) - 1) self.assertEqual(self.person.id, init_id) self.person.set_id("2.3.4.5") self.assertEqual(self.person.id, "2.3.4.5") diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index 447a82ea..5974a3ea 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -221,8 +221,10 @@ def test_print_population(self, mock_copy, mock_read): # The type of the id is converted from float to string, so we # drop the cell and microcell columns - expected = expected_df.drop(['cell', 'microcell', 'household_number'], axis=1) - actual = actual_df.drop(['cell', 'microcell', 'household_number'], axis=1) + expected = expected_df.drop(['cell', 'microcell', + 'household_number'], axis=1) + actual = actual_df.drop(['cell', 'microcell', + 'household_number'], axis=1) pd.testing.assert_frame_equal(actual, expected, check_dtype=False) From 666afea8c6bf1f8de68798de6a4cf2131401da44 Mon Sep 17 00:00:00 2001 From: abbie-evans <147088150+abbie-evans@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:23:37 +0000 Subject: [PATCH 21/53] Import arrangement Co-authored-by: Kit Gallagher <60467371+KCGallagher@users.noreply.github.com> --- pyEpiabm/pyEpiabm/core/cell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index aa20a4fd..c1ea21bc 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -4,9 +4,9 @@ import typing import numpy as np +import re from queue import Queue from numbers import Number -import re from pyEpiabm.property import InfectionStatus from pyEpiabm.utility import DistanceFunctions From c718a9f96920b39d3d1e285295947ad8c960ca7e Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 22 Nov 2023 13:58:10 +0000 Subject: [PATCH 22/53] Updated set_id tests --- pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py | 1 + pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index c7db4ad5..b945d081 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -65,6 +65,7 @@ def test_set_id(self): self.assertRaises(TypeError, subject.set_id, 2.0) self.assertRaises(ValueError, subject.set_id, "a.b.c") self.assertRaises(ValueError, subject.set_id, "0.0.") + self.assertRaises(ValueError, subject.set_id, "123") self.assertRaises(ValueError, subject.set_id, "0.0.0.0") diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index 25f800a8..763e6c88 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -25,8 +25,11 @@ def test_repr(self): def test_set_id(self): self.assertEqual(self.microcell.id, self.cell.id + "." + str(len(self.cell.microcells))) + self.microcell.set_id("2.3") + self.assertEqual(self.microcell.id, "2.3") self.assertRaises(TypeError, self.microcell.set_id, 2.0) self.assertRaises(ValueError, self.microcell.set_id, "1.1.") + self.assertRaises(ValueError, self.microcell.set_id, "12") def test_add_person(self): self.assertEqual(len(self.microcell.persons), 0) From 3df11876bfcbef23f17fa2565f17ba0701bd9b50 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 14:02:32 +0000 Subject: [PATCH 23/53] Added checks for duplicates --- pyEpiabm/pyEpiabm/core/cell.py | 7 ++++--- pyEpiabm/pyEpiabm/core/household.py | 20 ++++++++++++++------ pyEpiabm/pyEpiabm/core/microcell.py | 19 +++++++++++++------ pyEpiabm/pyEpiabm/core/person.py | 20 ++++++++++++++------ 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index aa20a4fd..f7adf745 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -82,13 +82,14 @@ def set_id(self, id: str): """ # Ensure id is a string if not isinstance(id, str): - raise TypeError("id must be of type string") + raise TypeError("Provided id must be a string") - # May want to set upper limit on the number of digits + # This regex will match on any string which has 1 or more digits if re.match("^\\d+$", id): self.id = id else: - raise ValueError("id must take the correct form") + raise ValueError(f"Invalid id: {id}. id must be of the form 'i' " + f"where i is an integer") def enqueue_person(self, person: Person): """Add person to queue for processing at end of iteration, provided diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index 68ffd5bd..b4b16694 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -122,10 +122,18 @@ def set_id(self, id: str): # Ensure id is a string if not isinstance(id, str): - raise TypeError("id must be of type string") + raise TypeError("Provided id must be a string") - # May want to set upper limit on the number of digits - if re.match("^\\d+\\.\\d+\\.\\d+$", id): - self.id = id - else: - raise ValueError("id must take the correct form") + # This regex will match on any string which takes the form "i.j.k" + # where i, j and k are integers + if not re.match("^\\d+\\.\\d+\\.\\d+$", id): + raise ValueError(f"Invalid id: {id}. id must be of the form " + f"'i.j.k' where i, j, k are integers") + + # Finally, check for duplicates + household_ids = [household.id + for household in self.microcell.households] + if id in household_ids: + raise ValueError(f"Duplicate id: {id}.") + + self.id = id diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 4fccaf79..88ba56b2 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -67,13 +67,20 @@ def set_id(self, id): # Ensure id is a string if not isinstance(id, str): - raise TypeError("id must be of type string") + raise TypeError("Provided id must be a string") - # Ensure that the id is of the correct form - if re.match("^\\d+\\.\\d+$", id): - self.id = id - else: - raise ValueError("id must take the correct form") + # This regex will match on any string which takes the form "i.j" where + # i and j are integers + if not re.match("^\\d+\\.\\d+$", id): + raise ValueError(f"Invalid id: {id}. id must be of the form 'i.j' " + f"where i, j are integers") + + # Finally, check for duplicates + microcell_ids = [microcell.id for microcell in self.cell.microcells] + if id in microcell_ids: + raise ValueError(f"Duplicate id: {id}.") + + self.id = id def add_person(self, person): """Adds :class:`Person` with given :class:`InfectionStatus` and given diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index f36b2bc9..22003be3 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -242,10 +242,18 @@ def set_id(self, id: str): # Ensure id is a string if not isinstance(id, str): - raise TypeError("id must be of type string") + raise TypeError("Provided id must be a string") - # May want to set upper limit on the number of digits - if re.match("^\\d+\\.\\d+\\.\\d*\\.\\d+$", id): - self.id = id - else: - raise ValueError("id must take the correct form") + # This regex will match on any string which takes the form "i.j.k.l" + # where i, j, k and l are integers (k can be empty) + if not re.match("^\\d+\\.\\d+\\.\\d*\\.\\d+$", id): + raise ValueError(f"Invalid id: {id}. id must be of the form " + f"'i.j.k.l' where i, j, k, l are integers (k" + f"can be empty)") + + # Finally, check for duplicates + person_ids = [person.id for person in self.household.persons] + if id in person_ids: + raise ValueError(f"Duplicate id: {id}.") + + self.id = id From 9e2bef6e2083763499829fe6e4dd71be00a36072 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 14:11:41 +0000 Subject: [PATCH 24/53] Style changes --- pyEpiabm/pyEpiabm/core/cell.py | 2 +- pyEpiabm/pyEpiabm/core/household.py | 4 ++-- pyEpiabm/pyEpiabm/core/microcell.py | 2 +- pyEpiabm/pyEpiabm/core/person.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index f76f98f9..f7ac95c7 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -72,7 +72,7 @@ def add_microcells(self, n): self.microcells.append(Microcell(self)) def set_id(self, id: str): - """Updates ID of cell (i.e. for input from file). + """Updates id of current cell (i.e. for input from file). Parameters ---------- diff --git a/pyEpiabm/pyEpiabm/core/household.py b/pyEpiabm/pyEpiabm/core/household.py index b4b16694..a2bec542 100644 --- a/pyEpiabm/pyEpiabm/core/household.py +++ b/pyEpiabm/pyEpiabm/core/household.py @@ -108,8 +108,8 @@ def remove_household(self): self.cell.households.remove(self) def set_id(self, id: str): - """Updates ID of household (i.e. for input from file). - Format of ID - for example 3.1.2 represents household 2 within + """Updates id of current household (i.e. for input from file). + Format of id - for example 3.1.2 represents household 2 within microcell 1 within cell 3. The id will only be changed if it has the correct expression. diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 88ba56b2..20d411a0 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -56,7 +56,7 @@ def __repr__(self): f" at location {self.location}." def set_id(self, id): - """Updates ID of microcell (i.e. for input from file). + """Updates id of current microcell (i.e. for input from file). Parameters ---------- diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 22003be3..874eb53f 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -228,8 +228,8 @@ def remove_person(self): self.household.persons.remove(self) def set_id(self, id: str): - """Updates ID of person (i.e. for input from file). - ID format: 4.3.2.1 represents cell 4, microcell 3 within this cell, + """Updates id of current person (i.e. for input from file). + id format: 4.3.2.1 represents cell 4, microcell 3 within this cell, household 2 within this microcell, and person 1 within this household. The id will only be changed if there is a match. From e46a784e699d26e54b3459085441010804fbfca8 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 14:23:27 +0000 Subject: [PATCH 25/53] Fixed error and changed make_pop --- pyEpiabm/pyEpiabm/core/person.py | 2 +- .../pyEpiabm/routine/file_population_config.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 874eb53f..c75a1010 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -252,7 +252,7 @@ def set_id(self, id: str): f"can be empty)") # Finally, check for duplicates - person_ids = [person.id for person in self.household.persons] + person_ids = [person.id for person in self.microcell.persons] if id in person_ids: raise ValueError(f"Duplicate id: {id}.") diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 45762700..d38b2898 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -87,7 +87,8 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): # Converting from float to string cell_id_csv = str(int(line["cell"])) - microcell_id_csv = str(int(line["microcell"])) + microcell_id_csv = cell_id_csv + "." + str(int(line["microcell"])) + # Check if cell exists, or create it cell = FilePopulationFactory.find_cell(new_pop, cell_id_csv, current_cell) @@ -99,14 +100,10 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): cell.set_location(location) # Raise error if microcell exists, then create new one - for microcell in cell.microcells: - - # The microcell id is a string separated by periods, so we - # must find the part which matches the csv input - id_parts = microcell.id.split(".") - if id_parts[-1] == microcell_id_csv: - raise ValueError(f"Duplicate microcells {microcell.id}" - + f" in cell {cell.id}") + microcell_ids = [microcell.id for microcell in cell.microcells] + if microcell_id_csv in microcell_ids: + raise ValueError(f"Duplicate microcells {microcell_id_csv}" + + f" in cell {cell.id}") new_microcell = Microcell(cell) cell.microcells.append(new_microcell) @@ -114,7 +111,7 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): # Either keep the following line (sets id according to data frame # input), or remove it (sets id according to current number of # microcells in cell.microcells) - new_microcell.set_id(cell_id_csv + "." + microcell_id_csv) + new_microcell.set_id(microcell_id_csv) for column in input.columns.values: if hasattr(InfectionStatus, column): From a983def64e125dcaa1a9103d128e84fb81dd19c6 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 22 Nov 2023 14:48:31 +0000 Subject: [PATCH 26/53] Added tests for uniqueness of id --- .../pyEpiabm/tests/test_unit/test_core/test_household.py | 7 +++++-- .../pyEpiabm/tests/test_unit/test_core/test_microcell.py | 5 +++++ pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index b945d081..ad8bc870 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -58,8 +58,6 @@ def test_set_id(self): init_id = str(self.microcell.id) + "." + \ str(len(self.microcell.households)-1) self.assertEqual(subject.id, init_id) - subject.set_id("0.0.0") - self.assertEqual(subject.id, "0.0.0") subject.set_id("10.7.20") self.assertEqual(subject.id, "10.7.20") self.assertRaises(TypeError, subject.set_id, 2.0) @@ -67,6 +65,11 @@ def test_set_id(self): self.assertRaises(ValueError, subject.set_id, "0.0.") self.assertRaises(ValueError, subject.set_id, "123") self.assertRaises(ValueError, subject.set_id, "0.0.0.0") + subject.set_id("1.1.1") + self.microcell.households.append(subject) + new_household = pe.Household(self.microcell, (1, 2)) + self.cell.microcells.append(new_household) + self.assertRaises(ValueError, new_household.set_id, "1.1.1") if __name__ == '__main__': diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index 763e6c88..408a4889 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -30,6 +30,11 @@ def test_set_id(self): self.assertRaises(TypeError, self.microcell.set_id, 2.0) self.assertRaises(ValueError, self.microcell.set_id, "1.1.") self.assertRaises(ValueError, self.microcell.set_id, "12") + self.microcell.set_id("0.0") + self.cell.microcells.append(self.microcell) + new_microcell = pe.Microcell(self.cell) + self.cell.microcells.append(new_microcell) + self.assertRaises(ValueError, new_microcell.set_id, "0.0") def test_add_person(self): self.assertEqual(len(self.microcell.persons), 0) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py index d415ca32..7c24f88c 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_person.py @@ -120,6 +120,11 @@ def test_set_id(self): self.assertRaises(ValueError, self.person.set_id, "0.1") self.assertRaises(ValueError, self.person.set_id, "1234") self.assertRaises(ValueError, self.person.set_id, "0.0.0.0.5") + self.person.set_id("1.1.1.1") + self.microcell.persons.append(self.person) + new_person = pe.Person(self.microcell) + self.microcell.persons.append(new_person) + self.assertRaises(ValueError, new_person.set_id, "1.1.1.1") if __name__ == '__main__': From 182cdb65f38e8c0c403c2f8a322c2c3dd7cdaf5b Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 15:10:58 +0000 Subject: [PATCH 27/53] Fixes of duplicate ids --- pyEpiabm/pyEpiabm/intervention/travel_isolation.py | 7 +++++++ pyEpiabm/pyEpiabm/routine/file_population_config.py | 4 ---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index f0d1e592..a7eb77bd 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -75,6 +75,13 @@ def __call__(self, time): selected_household = random.choice( existing_households) selected_household.add_person(person) + + # Have to update the person's id to + # account for their new household + person.set_id(selected_household.id + + "." + + len(selected_household + .persons)) else: person.household.isolation_location = \ False diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index d38b2898..a9fe79fa 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -107,10 +107,6 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): new_microcell = Microcell(cell) cell.microcells.append(new_microcell) - - # Either keep the following line (sets id according to data frame - # input), or remove it (sets id according to current number of - # microcells in cell.microcells) new_microcell.set_id(microcell_id_csv) for column in input.columns.values: From 18a666d3333b94d647c3ec44f074e05a1e32d560 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 15:13:25 +0000 Subject: [PATCH 28/53] Fixes of duplicate ids --- pyEpiabm/pyEpiabm/intervention/travel_isolation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index a7eb77bd..7e04bcaf 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -80,8 +80,8 @@ def __call__(self, time): # account for their new household person.set_id(selected_household.id + "." + - len(selected_household - .persons)) + str(len(selected_household + .persons))) else: person.household.isolation_location = \ False From 6a8ddd61365842e9b87e7823c5807a1c73055284 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 15:45:08 +0000 Subject: [PATCH 29/53] Checked that the actual dataframe in test_file_population_config matches the expected --- pyEpiabm/pyEpiabm/core/person.py | 2 +- .../test_routine/test_file_population_config.py | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index c75a1010..69619cf3 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -231,7 +231,7 @@ def set_id(self, id: str): """Updates id of current person (i.e. for input from file). id format: 4.3.2.1 represents cell 4, microcell 3 within this cell, household 2 within this microcell, and person 1 within this - household. The id will only be changed if there is a match. + household. The id will only be changed if it is of the correct format. Parameters ---------- diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index 5974a3ea..5d8ad175 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -216,15 +216,13 @@ def test_print_population(self, mock_copy, mock_read): FilePopulationFactory.print_population(test_pop, 'output.csv') actual_df = mock_copy.call_args.args[0] + actual_df['cell'] = pd.Series([1.0, 2.0]) + actual_df['microcell'] = pd.Series([1.0, 1.0]) if version.parse(pd.__version__) >= version.parse("1.4.0"): expected_df = self.df.copy() - # The type of the id is converted from float to string, so we - # drop the cell and microcell columns - expected = expected_df.drop(['cell', 'microcell', - 'household_number'], axis=1) - actual = actual_df.drop(['cell', 'microcell', - 'household_number'], axis=1) + expected = expected_df.drop(['household_number'], axis=1) + actual = actual_df.drop(['household_number'], axis=1) pd.testing.assert_frame_equal(actual, expected, check_dtype=False) From ecea0bd4839bb92b3c503df6a963c0e1b5062c36 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 15:49:34 +0000 Subject: [PATCH 30/53] Pandas version check --- .../test_unit/test_routine/test_file_population_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index 5d8ad175..c8d639e2 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -216,13 +216,13 @@ def test_print_population(self, mock_copy, mock_read): FilePopulationFactory.print_population(test_pop, 'output.csv') actual_df = mock_copy.call_args.args[0] - actual_df['cell'] = pd.Series([1.0, 2.0]) - actual_df['microcell'] = pd.Series([1.0, 1.0]) if version.parse(pd.__version__) >= version.parse("1.4.0"): expected_df = self.df.copy() expected = expected_df.drop(['household_number'], axis=1) actual = actual_df.drop(['household_number'], axis=1) + actual_df['cell'] = pd.Series([1.0, 2.0]) + actual_df['microcell'] = pd.Series([1.0, 1.0]) pd.testing.assert_frame_equal(actual, expected, check_dtype=False) From e21a57dc068798747c36348cf3f78b698d763caa Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 15:57:15 +0000 Subject: [PATCH 31/53] Fixed error --- .../test_unit/test_routine/test_file_population_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index c8d639e2..a01b15a1 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -221,8 +221,8 @@ def test_print_population(self, mock_copy, mock_read): expected = expected_df.drop(['household_number'], axis=1) actual = actual_df.drop(['household_number'], axis=1) - actual_df['cell'] = pd.Series([1.0, 2.0]) - actual_df['microcell'] = pd.Series([1.0, 1.0]) + actual['cell'] = pd.Series([1.0, 2.0]) + actual['microcell'] = pd.Series([1.0, 1.0]) pd.testing.assert_frame_equal(actual, expected, check_dtype=False) From b8bde2c23bceaa79663946520641b3cf0b656722 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 16:02:47 +0000 Subject: [PATCH 32/53] Added duplicates test for cells --- pyEpiabm/pyEpiabm/core/cell.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/cell.py b/pyEpiabm/pyEpiabm/core/cell.py index f7ac95c7..93869609 100644 --- a/pyEpiabm/pyEpiabm/core/cell.py +++ b/pyEpiabm/pyEpiabm/core/cell.py @@ -71,13 +71,15 @@ def add_microcells(self, n): for _ in range(n): self.microcells.append(Microcell(self)) - def set_id(self, id: str): + def set_id(self, id: str, cells: typing.List): """Updates id of current cell (i.e. for input from file). Parameters ---------- id : str Identity of cell + cells : list + List of all current cells """ # Ensure id is a string @@ -85,12 +87,17 @@ def set_id(self, id: str): raise TypeError("Provided id must be a string") # This regex will match on any string which has 1 or more digits - if re.match("^\\d+$", id): - self.id = id - else: + if not re.match("^\\d+$", id): raise ValueError(f"Invalid id: {id}. id must be of the form 'i' " f"where i is an integer") + # Finally, check for duplicates + cell_ids = [cell.id for cell in cells] + if id in cell_ids: + raise ValueError(f"Duplicate id: {id}.") + + self.id = id + def enqueue_person(self, person: Person): """Add person to queue for processing at end of iteration, provided they are not already recovered (and so may be infected). From 8027168f7a5c587475347e9a9360a9703165a79d Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 22 Nov 2023 16:03:53 +0000 Subject: [PATCH 33/53] Added duplicates test for cells --- pyEpiabm/pyEpiabm/core/population.py | 2 +- pyEpiabm/pyEpiabm/routine/file_population_config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/population.py b/pyEpiabm/pyEpiabm/core/population.py index 2fc005be..bf0fc368 100644 --- a/pyEpiabm/pyEpiabm/core/population.py +++ b/pyEpiabm/pyEpiabm/core/population.py @@ -45,7 +45,7 @@ def add_cells(self, n): base_num = len(self.cells) for i in range(n): self.cells.append(Cell()) - self.cells[i + base_num].set_id(str(i + base_num)) + self.cells[i + base_num].set_id(str(i + base_num), self.cells) def total_people(self): """Returns the total number of people in the population. diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index a9fe79fa..71266c27 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -179,7 +179,7 @@ def find_cell(population: Population, cell_id: str, current_cell: Cell): return current_cell new_cell = Cell() population.cells.append(new_cell) - new_cell.set_id(cell_id) + new_cell.set_id(cell_id, population.cells) return new_cell @staticmethod From 705d1146bd275c6725e113a135a2a84791d057a6 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 22 Nov 2023 18:56:18 +0000 Subject: [PATCH 34/53] Update set_id test for duplicates --- .../pyEpiabm/tests/test_unit/test_core/test_cell.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py index 233767ca..d62720b3 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py @@ -38,11 +38,18 @@ def test_set_location(self): self.assertRaises(ValueError, pe.Cell, ('1', 1)) def test_set_id(self): + cells = [] + cell1 = pe.Cell() self.assertEqual(self.cell.id, str(hash(self.cell))) - self.assertRaises(TypeError, self.cell.set_id, 2.0) - self.assertRaises(ValueError, self.cell.set_id, "2.0") - self.cell.set_id("2") + self.assertRaises(TypeError, self.cell.set_id, id=2.0, cells=[cell1]) + self.assertRaises(ValueError, self.cell.set_id, id="2.0", cells=[cell1]) + self.cell.set_id(id="2", cells=[cell1]) self.assertEqual(self.cell.id, "2") + cell1.set_id(id="1", cells=[cell1]) + cells.append(cell1) + new_cell = pe.Cell((1,1)) + cells.append(new_cell) + self.assertRaises(ValueError, new_cell.set_id, id="1", cells=[cell1]) def test_add_microcells(self, n=4): self.assertEqual(len(self.cell.microcells), 0) From 1cbc7bbcf2028616f0c41faa0b9baa67cc49f384 Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 22 Nov 2023 19:14:29 +0000 Subject: [PATCH 35/53] Added argument cells for use of cells set_id --- .../tests/test_unit/test_routine/test_file_population_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index a01b15a1..c784ae90 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -134,7 +134,7 @@ def test_duplicate_microcell(self, mock_read, mock_log): def test_find_cell(self): pop = pe.Population() pop.add_cells(2) - pop.cells[1].set_id("42") + pop.cells[1].set_id("42", cells=pop.cells) target_cell = FilePopulationFactory.find_cell(pop, "42", pop.cells[1]) self.assertEqual(target_cell.id, "42") From c7532d57c9a0fd80fe13f154cd3692e5e445b53d Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 22 Nov 2023 19:31:29 +0000 Subject: [PATCH 36/53] Attempt fix of indent error --- pyEpiabm/pyEpiabm/core/person.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index 69619cf3..ce46fc1b 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -142,7 +142,7 @@ def update_status(self, self.infection_status = new_status if self.infection_status == InfectionStatus.Susceptible and \ - self.household is not None: + self.household is not None: self.household.add_susceptible_person(self) if self.infection_status == InfectionStatus.Exposed and \ self.household is not None: From ec769246a1bcac8dcf32c5cb1fb532550ddddddd Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Thu, 23 Nov 2023 07:54:42 +0000 Subject: [PATCH 37/53] Style fixes --- pyEpiabm/pyEpiabm/core/person.py | 10 +++++----- .../pyEpiabm/tests/test_unit/test_core/test_cell.py | 5 +++-- .../tests/test_unit/test_core/test_household.py | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/person.py b/pyEpiabm/pyEpiabm/core/person.py index ce46fc1b..84374ab6 100644 --- a/pyEpiabm/pyEpiabm/core/person.py +++ b/pyEpiabm/pyEpiabm/core/person.py @@ -56,7 +56,7 @@ def __init__(self, microcell, age_group=None): self.date_positive = None self.is_vaccinated = False self.id = self.microcell.id + "." + "." + \ - str(len(self.microcell.persons)) + str(len(self.microcell.persons)) self.set_random_age(age_group) @@ -91,7 +91,7 @@ def is_symptomatic(self): """ return Person.is_infectious(self) and self.infection_status != \ - InfectionStatus.InfectASympt + InfectionStatus.InfectASympt def is_infectious(self): """Query if the person is currently infectious. @@ -142,10 +142,10 @@ def update_status(self, self.infection_status = new_status if self.infection_status == InfectionStatus.Susceptible and \ - self.household is not None: + self.household is not None: self.household.add_susceptible_person(self) if self.infection_status == InfectionStatus.Exposed and \ - self.household is not None: + self.household is not None: self.household.remove_susceptible_person(self) def add_place(self, place, person_group: int = 0): @@ -198,7 +198,7 @@ def is_place_closed(self, closure_place_type): """ if (hasattr(self.microcell, 'closure_start_time')) and ( - self.microcell.closure_start_time is not None): + self.microcell.closure_start_time is not None): for place_type in self.place_types: if place_type.value in closure_place_type: return True diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py index d62720b3..581dbb77 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_cell.py @@ -42,12 +42,13 @@ def test_set_id(self): cell1 = pe.Cell() self.assertEqual(self.cell.id, str(hash(self.cell))) self.assertRaises(TypeError, self.cell.set_id, id=2.0, cells=[cell1]) - self.assertRaises(ValueError, self.cell.set_id, id="2.0", cells=[cell1]) + self.assertRaises(ValueError, self.cell.set_id, id="2.0", + cells=[cell1]) self.cell.set_id(id="2", cells=[cell1]) self.assertEqual(self.cell.id, "2") cell1.set_id(id="1", cells=[cell1]) cells.append(cell1) - new_cell = pe.Cell((1,1)) + new_cell = pe.Cell((1, 1)) cells.append(new_cell) self.assertRaises(ValueError, new_cell.set_id, id="1", cells=[cell1]) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py index ad8bc870..e702d5dd 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_household.py @@ -55,8 +55,8 @@ def test_remove_susceptible_person(self): def test_set_id(self): subject = pe.Household(self.microcell, (1, 1)) - init_id = str(self.microcell.id) + "." + \ - str(len(self.microcell.households)-1) + init_id = str(self.microcell.id) + "." \ + + str(len(self.microcell.households)-1) self.assertEqual(subject.id, init_id) subject.set_id("10.7.20") self.assertEqual(subject.id, "10.7.20") From 48a98d2f604414a9f0cf8f3915e3c31f1bd34848 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 23 Nov 2023 09:36:55 +0000 Subject: [PATCH 38/53] Changed csv read so that cell and microcell ids are read as integers, not floats --- .../pyEpiabm/intervention/travel_isolation.py | 2 ++ .../routine/file_population_config.py | 11 ++++---- .../test_file_population_config.py | 28 ++++++++++++------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index 7e04bcaf..e046a018 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -82,6 +82,8 @@ def __call__(self, time): "." + str(len(selected_household .persons))) + # Here we should keep track of this + # person's previous id else: person.household.isolation_location = \ False diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 71266c27..32feb3f7 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -62,7 +62,8 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): logging.info(f"Set population random seed to: {random_seed}") # Read file into pandas dataframe - input = pd.read_csv(input_file) + input = pd.read_csv(input_file, dtype={"cell": int, + "microcell": int}) loc_given = ("location_x" and "location_y" in input.columns.values) # Sort csv on cell and microcell ID input = input.sort_values(by=["cell", "microcell"]) @@ -83,11 +84,11 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): # Store current cell current_cell = None # Iterate through lines (one per microcell) - for _, line in input.iterrows(): - + for line in input.itertuples(): + line = line._asdict() # Converting from float to string - cell_id_csv = str(int(line["cell"])) - microcell_id_csv = cell_id_csv + "." + str(int(line["microcell"])) + cell_id_csv = str(line["cell"]) + microcell_id_csv = cell_id_csv + "." + str(line["microcell"]) # Check if cell exists, or create it cell = FilePopulationFactory.find_cell(new_pop, cell_id_csv, diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index c784ae90..ac49c8a2 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -13,7 +13,7 @@ class TestPopConfig(TestPyEpiabm): """ def setUp(self) -> None: - self.input = {'cell': [1.0, 2.0], 'microcell': [1.0, 1.0], + self.input = {'cell': [1, 2], 'microcell': [1, 1], 'location_x': [0.0, 1.0], 'location_y': [0.0, 1.0], 'household_number': [1, 1], 'place_number': [1, 1], 'Susceptible': [8, 9], 'InfectMild': [2, 3]} @@ -37,7 +37,9 @@ def test_make_pop(self, mock_read): mock_read.return_value = self.df test_pop = FilePopulationFactory.make_pop('test_input.csv') - mock_read.assert_called_once_with('test_input.csv') + mock_read.assert_called_once_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) total_people = 0 total_infectious = 0 @@ -61,10 +63,10 @@ def test_make_pop(self, mock_read): # Test cell_wise values for i in range(2): self.assertEqual(test_pop.cells[i].id, - str(int(self.input.get('cell')[i]))) + str(self.input.get('cell')[i])) id_parts = test_pop.cells[i].microcells[0].id.split(".") self.assertEqual(id_parts[-1], - str(int(self.input.get('microcell')[i]))) + str(self.input.get('microcell')[i])) self.assertEqual(test_pop.cells[i].location[0], self.input.get('location_x')[i]) self.assertEqual(test_pop.cells[i].location[1], @@ -95,7 +97,9 @@ def test_invalid_input(self, mock_read, mock_log): FilePopulationFactory.make_pop('test_input.csv') mock_log.assert_called_once_with("ValueError in FilePopulation" + "Factory.make_pop()") - mock_read.assert_called_once_with('test_input.csv') + mock_read.assert_called_once_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) @patch("pandas.read_csv") def test_disorderd_input(self, mock_read): @@ -103,7 +107,7 @@ def test_disorderd_input(self, mock_read): and microcells. """ # Read in disordered data - dict_extra = {'cell': [1.0, 3.0], 'microcell': [2.0, 1.0], + dict_extra = {'cell': [1, 3], 'microcell': [2, 1], 'location_x': [0.0, 2.0], 'location_y': [0.0, 2.0], 'household_number': [1, 1], 'place_number': [1, 1], 'Susceptible': [8, 9], 'InfectMild': [2, 3]} @@ -129,7 +133,9 @@ def test_duplicate_microcell(self, mock_read, mock_log): FilePopulationFactory.make_pop('test_input.csv') mock_log.assert_called_once_with("ValueError in FilePopulation" + "Factory.make_pop()") - mock_read.assert_called_once_with('test_input.csv') + mock_read.assert_called_once_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) def test_find_cell(self): pop = pe.Population() @@ -180,7 +186,9 @@ def test_random_seed_param(self, mock_read, mock_random, mock_read.return_value = self.df FilePopulationFactory.make_pop('test_input.csv', random_seed=n) - mock_read.assert_called_once_with('test_input.csv') + mock_read.assert_called_once_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) mock_random.assert_called_once_with(n) mock_np_random.assert_called_once_with(n) @@ -221,8 +229,8 @@ def test_print_population(self, mock_copy, mock_read): expected = expected_df.drop(['household_number'], axis=1) actual = actual_df.drop(['household_number'], axis=1) - actual['cell'] = pd.Series([1.0, 2.0]) - actual['microcell'] = pd.Series([1.0, 1.0]) + actual['cell'] = pd.Series([1, 2]) + actual['microcell'] = pd.Series([1, 1]) pd.testing.assert_frame_equal(actual, expected, check_dtype=False) From 7d8b8b08439fa2149e549eb5a46fb348324ce5c0 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 23 Nov 2023 18:22:38 +0000 Subject: [PATCH 39/53] Fixed some errors --- .../test_func/functional_testing_class.py | 2 +- .../test_func/test_closure_functional.py | 24 +++++++++++---- .../test_func/test_distancing_functional.py | 12 ++++++-- .../test_func/test_isolation_functional.py | 24 +++++++++++---- .../test_func/test_quarantine_functional.py | 30 +++++++++++-------- .../tests/test_func/test_sim_functional.py | 12 +++++--- 6 files changed, 71 insertions(+), 33 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_func/functional_testing_class.py b/pyEpiabm/pyEpiabm/tests/test_func/functional_testing_class.py index 7dc77956..a12cba97 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/functional_testing_class.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/functional_testing_class.py @@ -30,7 +30,7 @@ def setUpPopulation(self): """Can be called in setUp to create a default population, to test the impact of interventions. """ - self.pop_params = {'cell': [1.0, 2.0], 'microcell': [1.0, 1.0], + self.pop_params = {'cell': [1, 2], 'microcell': [1, 1], 'location_x': [0.0, 1.0], 'location_y': [0.0, 1.0], 'household_number': [1, 1], 'Susceptible': [800, 900], 'InfectMild': [10, 0], diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py index 5a7a9742..c6155aff 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py @@ -54,7 +54,9 @@ def test_closure_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -83,7 +85,9 @@ def test_no_closure_type(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -112,7 +116,9 @@ def test_closure_type_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -141,7 +147,9 @@ def test_spatial_params_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -172,7 +180,9 @@ def test_microcell_threshold_extreme(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -200,7 +210,9 @@ def test_microcell_threshold_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py index dffbed91..f91f12ba 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py @@ -60,7 +60,9 @@ def test_distancing_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -88,7 +90,9 @@ def test_spatial_enhanced_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -117,7 +121,9 @@ def test_prob_lower(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py index 2a768094..30eaa1b5 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py @@ -54,7 +54,9 @@ def test_isolation_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -82,7 +84,9 @@ def test_threshold_num(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -109,7 +113,9 @@ def test_delay_days(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc().sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -137,7 +143,9 @@ def test_duration_days(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -164,7 +172,9 @@ def test_isolation_prob(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -191,7 +201,9 @@ def test_isolation_effectiveness(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py index aa2dcc79..b20059d1 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py @@ -17,6 +17,7 @@ class TestQuarantineFunctional(TestFunctional): household quarantine intervention simulations with known results/properties to ensure code functions as desired. """ + def setUp(self) -> None: TestFunctional.setUpPopulation() @@ -32,17 +33,18 @@ def setUp(self) -> None: "isolation_house_effectiveness": 1}, "household_quarantine": { - "start_time": 0, - "policy_duration": 365, - "case_threshold": 0, - "quarantine_delay": 0, - "quarantine_duration": 10, - "quarantine_house_compliant": 1.0, - "quarantine_individual_compliant": 1.0, - "quarantine_house_effectiveness": 1.1, - "quarantine_spatial_effectiveness": 0.1, - "quarantine_place_effectiveness": [0.1, 0.1, 0.1, 0.1, 0.1, 0.1] - } + "start_time": 0, + "policy_duration": 365, + "case_threshold": 0, + "quarantine_delay": 0, + "quarantine_duration": 10, + "quarantine_house_compliant": 1.0, + "quarantine_individual_compliant": 1.0, + "quarantine_house_effectiveness": 1.1, + "quarantine_spatial_effectiveness": 0.1, + "quarantine_place_effectiveness": [0.1, 0.1, 0.1, 0.1, 0.1, + 0.1] + } } def test_quarantine_present(self, mock_read, mock_csv): @@ -68,12 +70,14 @@ def test_quarantine_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group HelperFunc().compare_susceptible_groups( - pop_isolation.cells, pop_quarantine.cells) + pop_isolation.cells, pop_quarantine.cells) if __name__ == '__main__': diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py index ec2112d9..bb6fdcc1 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py @@ -174,7 +174,7 @@ def test_segmented_infection(self, *mocks): outside their household (or microcell) without a spatial sweep. """ mock_read, mock_csv = mocks[:2] - file_input = {'cell': [1.0, 2.0], 'microcell': [1.0, 1.0], + file_input = {'cell': [1, 2], 'microcell': [1, 1], 'location_x': [0.0, 1.0], 'location_y': [0.0, 1.0], 'household_number': [1, 1], 'Susceptible': [8, 9], 'InfectMild': [2, 0]} @@ -189,7 +189,9 @@ def test_segmented_infection(self, *mocks): self.file_params, sweep_list) - mock_read.assert_called_once_with('test_input.csv') + mock_read.assert_called_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) mock_csv.assert_called_once() cell_data_0 = pop.cells[0].compartment_counter.retrieve() @@ -209,7 +211,7 @@ def test_small_cutoff(self, *mocks): outside their cell when the cut-off is sufficiently small. """ mock_read, mock_csv = mocks[:2] - file_input = {'cell': [1.0, 2.0], 'microcell': [1.0, 1.0], + file_input = {'cell': [1, 2], 'microcell': [1, 1], 'location_x': [0.0, 1.0], 'location_y': [0.0, 1.0], 'household_number': [1, 1], 'Susceptible': [8, 9], 'InfectMild': [2, 0]} @@ -224,7 +226,9 @@ def test_small_cutoff(self, *mocks): self.file_params, sweep_list) - mock_read.assert_called_once_with('test_input.csv') + mock_read.assert_called_once_with('test_input.csv', + dtype={"cell": int, + "microcell": int}) mock_csv.assert_called_once() cell_data_0 = pop.cells[0].compartment_counter.retrieve() From e3eb42cab4319b8af11acb251531fc613ff95b4f Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Fri, 24 Nov 2023 18:34:04 +0000 Subject: [PATCH 40/53] Updated error message --- pyEpiabm/pyEpiabm/routine/file_population_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 32feb3f7..1b397ba8 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -103,8 +103,8 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): # Raise error if microcell exists, then create new one microcell_ids = [microcell.id for microcell in cell.microcells] if microcell_id_csv in microcell_ids: - raise ValueError(f"Duplicate microcells {microcell_id_csv}" - + f" in cell {cell.id}") + raise ValueError(f"Duplicate microcells: {microcell_id_csv}" + + f" already exists in cell {cell.id}") new_microcell = Microcell(cell) cell.microcells.append(new_microcell) From da8895a9b34aa1404e6aadb05057337ee656fd98 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Mon, 27 Nov 2023 08:44:00 +0000 Subject: [PATCH 41/53] Removed parameter chaining in the mock_read --- .../routine/file_population_config.py | 4 +-- .../test_func/test_closure_functional.py | 27 +++++++------------ .../test_func/test_distancing_functional.py | 15 +++++------ .../test_func/test_isolation_functional.py | 27 +++++++------------ .../test_func/test_quarantine_functional.py | 7 ++--- .../tests/test_func/test_sim_functional.py | 11 ++++---- .../test_file_population_config.py | 19 +++++-------- 7 files changed, 42 insertions(+), 68 deletions(-) diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 1b397ba8..c5a05fbe 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -62,8 +62,8 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): logging.info(f"Set population random seed to: {random_seed}") # Read file into pandas dataframe - input = pd.read_csv(input_file, dtype={"cell": int, - "microcell": int}) + input = pd.read_csv(filepath_or_buffer=input_file, dtype={"cell": int, + "microcell": int}) loc_given = ("location_x" and "location_y" in input.columns.values) # Sort csv on cell and microcell ID input = input.sort_values(by=["cell", "microcell"]) diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py index c6155aff..e7fa9142 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_closure_functional.py @@ -33,6 +33,9 @@ def setUp(self) -> None: } } + self.read_params = {"filepath_or_buffer": 'test_input.csv', + "dtype": {"cell": int, "microcell": int}} + def test_closure_present(self, mock_read, mock_csv): """Place closure functional test to ensure more people will be susceptible when place closure intervention is present. @@ -54,9 +57,7 @@ def test_closure_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -85,9 +86,7 @@ def test_no_closure_type(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -116,9 +115,7 @@ def test_closure_type_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -147,9 +144,7 @@ def test_spatial_params_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -180,9 +175,7 @@ def test_microcell_threshold_extreme(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -210,9 +203,7 @@ def test_microcell_threshold_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py index f91f12ba..eab00dd4 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_distancing_functional.py @@ -39,6 +39,9 @@ def setUp(self) -> None: } } + self.read_params = {"filepath_or_buffer": 'test_input.csv', + "dtype": {"cell": int, "microcell": int}} + def test_distancing_present(self, mock_read, mock_csv): """Social distancing functional test to ensure more people will be susceptible when social distancing intervention is present. @@ -60,9 +63,7 @@ def test_distancing_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -90,9 +91,7 @@ def test_spatial_enhanced_large(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -121,9 +120,7 @@ def test_prob_lower(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py index 30eaa1b5..dc62ed4d 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_isolation_functional.py @@ -33,6 +33,9 @@ def setUp(self) -> None: "isolation_house_effectiveness": 0} } + self.read_params = {"filepath_or_buffer": 'test_input.csv', + "dtype": {"cell": int, "microcell": int}} + def test_isolation_present(self, mock_read, mock_csv): """Case isolation functional test to ensure more people will be susceptible when case isolation intervention is present. @@ -54,9 +57,7 @@ def test_isolation_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -84,9 +85,7 @@ def test_threshold_num(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -113,9 +112,7 @@ def test_delay_days(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc().sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -143,9 +140,7 @@ def test_duration_days(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -172,9 +167,7 @@ def test_isolation_prob(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group @@ -201,9 +194,7 @@ def test_isolation_effectiveness(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py index b20059d1..e29049e9 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_quarantine_functional.py @@ -47,6 +47,9 @@ def setUp(self) -> None: } } + self.read_params = {"filepath_or_buffer": 'test_input.csv', + "dtype": {"cell": int, "microcell": int}} + def test_quarantine_present(self, mock_read, mock_csv): """Household quarantine functional test to ensure more people will be susceptible when household quarantine intervention is present. @@ -70,9 +73,7 @@ def test_quarantine_present(self, mock_read, mock_csv): "test_input.csv", self.sim_params, self.file_params, HelperFunc.sweep_list_initialise()) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) self.assertEqual(mock_csv.call_count, 2) # Compare number of susceptible individuals for each age group diff --git a/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py b/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py index bb6fdcc1..5905be5e 100644 --- a/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py +++ b/pyEpiabm/pyEpiabm/tests/test_func/test_sim_functional.py @@ -33,6 +33,9 @@ def setUp(self) -> None: "spatial_output": False, "age_stratified": True} + self.read_params = {"filepath_or_buffer": 'test_input.csv', + "dtype": {"cell": int, "microcell": int}} + @staticmethod def toy_simulation(pop_params, sim_params, file_params): # Create a population based on the parameters given. @@ -189,9 +192,7 @@ def test_segmented_infection(self, *mocks): self.file_params, sweep_list) - mock_read.assert_called_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_with(**self.read_params) mock_csv.assert_called_once() cell_data_0 = pop.cells[0].compartment_counter.retrieve() @@ -226,9 +227,7 @@ def test_small_cutoff(self, *mocks): self.file_params, sweep_list) - mock_read.assert_called_once_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_once_with(**self.read_params) mock_csv.assert_called_once() cell_data_0 = pop.cells[0].compartment_counter.retrieve() diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py index ac49c8a2..74f8c720 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_routine/test_file_population_config.py @@ -20,6 +20,9 @@ def setUp(self) -> None: self.df = pd.DataFrame(self.input) pe.Parameters.instance().household_size_distribution = [] + self.read_params = {"filepath_or_buffer": 'test_input.csv', + "dtype": {"cell": int, "microcell": int}} + @patch('logging.exception') def test_make_pop_no_file(self, mock_log): """Tests for when no file is specified. @@ -37,9 +40,7 @@ def test_make_pop(self, mock_read): mock_read.return_value = self.df test_pop = FilePopulationFactory.make_pop('test_input.csv') - mock_read.assert_called_once_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_once_with(**self.read_params) total_people = 0 total_infectious = 0 @@ -97,9 +98,7 @@ def test_invalid_input(self, mock_read, mock_log): FilePopulationFactory.make_pop('test_input.csv') mock_log.assert_called_once_with("ValueError in FilePopulation" + "Factory.make_pop()") - mock_read.assert_called_once_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_once_with(**self.read_params) @patch("pandas.read_csv") def test_disorderd_input(self, mock_read): @@ -133,9 +132,7 @@ def test_duplicate_microcell(self, mock_read, mock_log): FilePopulationFactory.make_pop('test_input.csv') mock_log.assert_called_once_with("ValueError in FilePopulation" + "Factory.make_pop()") - mock_read.assert_called_once_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_once_with(**self.read_params) def test_find_cell(self): pop = pe.Population() @@ -186,9 +183,7 @@ def test_random_seed_param(self, mock_read, mock_random, mock_read.return_value = self.df FilePopulationFactory.make_pop('test_input.csv', random_seed=n) - mock_read.assert_called_once_with('test_input.csv', - dtype={"cell": int, - "microcell": int}) + mock_read.assert_called_once_with(**self.read_params) mock_random.assert_called_once_with(n) mock_np_random.assert_called_once_with(n) From 89806cb2707e6c582e2c21dcd446a3263506046c Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 29 Nov 2023 19:23:49 +0000 Subject: [PATCH 42/53] Modified add_household so that person id is only set if person does not already have household --- pyEpiabm/pyEpiabm/core/microcell.py | 6 +++++- .../pyEpiabm/intervention/travel_isolation.py | 8 -------- .../routine/file_population_config.py | 19 +++++++++---------- .../test_unit/test_core/test_microcell.py | 7 +++++++ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 20d411a0..afea4b98 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -155,7 +155,11 @@ def add_household(self, people: list): for i in range(len(people)): person = people[i] household.add_person(person) - person.set_id(household.id + "." + str(i)) + + # If the person already has a household, then do not change + # their id + if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id): + person.set_id(household.id + "." + str(i)) else: logging.info("Cannot create an empty household") diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index e046a018..bf85760a 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -76,14 +76,6 @@ def __call__(self, time): existing_households) selected_household.add_person(person) - # Have to update the person's id to - # account for their new household - person.set_id(selected_household.id + - "." + - str(len(selected_household - .persons))) - # Here we should keep track of this - # person's previous id else: person.household.isolation_location = \ False diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index c5a05fbe..425b9a40 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -85,10 +85,9 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): current_cell = None # Iterate through lines (one per microcell) for line in input.itertuples(): - line = line._asdict() # Converting from float to string - cell_id_csv = str(line["cell"]) - microcell_id_csv = cell_id_csv + "." + str(line["microcell"]) + cell_id_csv = str(line.cell) + microcell_id_csv = cell_id_csv + "." + str(line.microcell) # Check if cell exists, or create it cell = FilePopulationFactory.find_cell(new_pop, cell_id_csv, @@ -97,7 +96,7 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): current_cell = cell if loc_given: - location = (line["location_x"], line["location_y"]) + location = (line.location_x, line.location_y) cell.set_location(location) # Raise error if microcell exists, then create new one @@ -113,7 +112,7 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): for column in input.columns.values: if hasattr(InfectionStatus, column): value = getattr(InfectionStatus, column) - for _ in range(int(line[column])): + for _ in range(int(getattr(line, column))): person = Person(new_microcell) person.set_random_age() new_microcell.add_person(person) @@ -129,14 +128,14 @@ def make_pop(input_file: str, random_seed: int = None, time: float = 0): # Add households and places to microcell if len(Parameters.instance().household_size_distribution) == 0: - if (('household_number' in line) and - (line["household_number"]) > 0): - households = int(line["household_number"]) + if (hasattr(line, 'household_number') and + line.household_number > 0): + households = int(line.household_number) FilePopulationFactory.add_households(new_microcell, households) - if ('place_number' in line) and (line["place_number"]) > 0: - new_microcell.add_place(int(line["place_number"]), + if hasattr(line, 'place_number') and line.place_number > 0: + new_microcell.add_place(int(line.place_number), cell.location, random.choice(list(PlaceType))) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index 408a4889..95259301 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -87,6 +87,13 @@ def test_add_household(self): person = self.microcell.persons[i] self.assertEqual(person.id, household.id + "." + str(i)) + # Check that if the person already has a household, then we cannot + # change their id when they move to a new one + person = self.microcell.persons[0] + household.persons.remove(person) + self.microcell.add_household([person]) + self.assertEqual(person.id, household.id + ".0") + @mock.patch('logging.info') def test_logging(self, mock_log): self.microcell.add_household(self.microcell.persons) From 8345e24b67e1ae805e12acf592868f8a1c7274ee Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 29 Nov 2023 20:29:23 +0000 Subject: [PATCH 43/53] Added change_id parameter to the add_household function so that we can control when the id of a person is changed --- pyEpiabm/pyEpiabm/core/microcell.py | 26 +++++++++----- .../pyEpiabm/intervention/travel_isolation.py | 2 +- .../routine/file_population_config.py | 2 +- .../pyEpiabm/routine/toy_population_config.py | 2 +- .../pyEpiabm/sweep/initial_household_sweep.py | 3 +- .../test_unit/test_core/test_microcell.py | 36 ++++++++++++------- .../test_travel_isolation.py | 2 +- .../test_initial_household_sweep.py | 9 ++--- .../test_unit/test_sweep/test_travel_sweep.py | 12 ++++--- 9 files changed, 60 insertions(+), 34 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index afea4b98..e55962fc 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -25,6 +25,7 @@ class Microcell: An instance of :class:`Cell` """ + def __init__(self, cell): """Constructor Method. @@ -53,7 +54,7 @@ def __repr__(self): """ return f"Microcell with {len(self.persons)} people" + \ - f" at location {self.location}." + f" at location {self.location}." def set_id(self, id): """Updates id of current microcell (i.e. for input from file). @@ -140,7 +141,7 @@ def add_place(self, n: int, loc: typing.Tuple[float, float], self.cell.places.append(p) self.places.append(p) - def add_household(self, people: list): + def add_household(self, people: list, change_id: bool): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. @@ -148,6 +149,9 @@ def add_household(self, people: list): ---------- people : list List of :class:`People` to add to household + change_id : bool + Boolean representing whether we wish to set the id of the people + of the household when the function is called or not """ if len(people) != 0: @@ -158,17 +162,21 @@ def add_household(self, people: list): # If the person already has a household, then do not change # their id - if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id): + if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id) and \ + change_id: person.set_id(household.id + "." + str(i)) + else: + logging.info(f"Person {person.id} has moved to household" + f"{household.id}") else: logging.info("Cannot create an empty household") def notify_person_status_change( - self, - old_status: InfectionStatus, - new_status: InfectionStatus, - age_group) -> None: + self, + old_status: InfectionStatus, + new_status: InfectionStatus, + age_group) -> None: """Notify Microcell that a person's status has changed. Parameters @@ -201,8 +209,8 @@ def set_location(self, loc: typing.Tuple[float, float]): def count_icu(self): return sum(map(lambda person: person.infection_status == - InfectionStatus.InfectICU, self.persons)) + InfectionStatus.InfectICU, self.persons)) def count_infectious(self): return sum(map(lambda person: person.is_infectious() is - True, self.persons)) + True, self.persons)) diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index bf85760a..8930cf8a 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -94,7 +94,7 @@ def __call__(self, time): person.household.persons.remove(person) # Put in new household person.microcell.add_household([ - person]) + person], change_id=False) person.household.isolation_location = \ True else: diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 425b9a40..6e8de039 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -207,7 +207,7 @@ def add_households(microcell: Microcell, household_number: int): person_choice = people_list[0] people_list.remove(person_choice) household_people.append(person_choice) - microcell.add_household(household_people) + microcell.add_household(household_people, change_id=False) @staticmethod @log_exceptions() diff --git a/pyEpiabm/pyEpiabm/routine/toy_population_config.py b/pyEpiabm/pyEpiabm/routine/toy_population_config.py index bcdf389d..3cdefe3c 100644 --- a/pyEpiabm/pyEpiabm/routine/toy_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/toy_population_config.py @@ -136,7 +136,7 @@ def add_households(population: Population, household_number: int): person_choice = people_list[0] people_list.remove(person_choice) household_people.append(person_choice) - microcell.add_household(household_people) + microcell.add_household(household_people, change_id=True) @staticmethod def add_places(population: Population, place_number: float): diff --git a/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py b/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py index 73d677d4..d7377afa 100644 --- a/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py +++ b/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py @@ -68,7 +68,8 @@ def household_allocation(self, population: Population): people_in_household = [] for i in range(k, k + m): people_in_household.append(microcell.persons[i]) - microcell.add_household(people_in_household) + microcell.add_household(people_in_household, + change_id=True) k += m def one_person_household_age(self, person: Person): diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index 95259301..0d08cc6c 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -79,24 +79,36 @@ def test_setup(self, n=5): def test_add_household(self): self.microcell.add_people(1) - self.microcell.add_household(self.microcell.persons) + self.microcell.add_household(self.microcell.persons, change_id=True) self.assertEqual(len(self.microcell.households), 1) - household = self.microcell.households[0] - self.assertEqual(len(household.persons), 1) - for i in range(len(household.persons)): + original_household = self.microcell.households[0] + self.assertEqual(len(original_household.persons), 1) + for i in range(len(original_household.persons)): person = self.microcell.persons[i] - self.assertEqual(person.id, household.id + "." + str(i)) + self.assertEqual(person.id, original_household.id + "." + str(i)) - # Check that if the person already has a household, then we cannot - # change their id when they move to a new one - person = self.microcell.persons[0] - household.persons.remove(person) - self.microcell.add_household([person]) - self.assertEqual(person.id, household.id + ".0") + # Now add another original_household without changing + # the id of the person + self.microcell.add_household(self.microcell.persons, change_id=False) + self.assertEqual(original_household.persons[0].id, + original_household.id + ".0") @mock.patch('logging.info') def test_logging(self, mock_log): - self.microcell.add_household(self.microcell.persons) + self.microcell.add_household(self.microcell.persons, change_id=True) + mock_log.assert_called_once() + + @mock.patch('logging.info') + def test_logging_duplicates(self, mock_log): + # Check that if the person already has a household, then we cannot + # change their id when they move to a new one + self.microcell.add_people(1) + self.microcell.add_household(self.microcell.persons, change_id=True) + old_household = self.microcell.households[0] + person = self.microcell.persons[0] + old_household.persons.remove(person) + self.microcell.add_household([person], change_id=True) + self.assertEqual(person.id, old_household.id + ".0") mock_log.assert_called_once() def test_report(self): diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py index 2e0f277c..49612af6 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py @@ -83,7 +83,7 @@ def test___call__(self, mock_random): 1, status=InfectionStatus.InfectASympt, age_group=7) person_introduced2 = self._microcell.persons[3] self._population.travellers.append(person_introduced2) - self._microcell.add_household([person_introduced2]) + self._microcell.add_household([person_introduced2], change_id=False) person_introduced2.travel_end_time = 40 self.travelisolation(time=21) self.assertTrue(person_introduced2.household.isolation_location) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py index 0daa8ed6..c8738760 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py @@ -384,10 +384,11 @@ def test_call(self, mock_log): test_sweep = pe.sweep.InitialHouseholdSweep() test_sweep.bind_population(self.test_population) microcell = self.test_population.cells[0].microcells[0] - microcell.add_household([self.person1]) - microcell.add_household([self.person2, self.person3]) - microcell.add_household([self.person4, self.person5, self.person6]) - microcell.add_household([]) + microcell.add_household([self.person1], change_id=True) + microcell.add_household([self.person2, self.person3], change_id=True) + microcell.add_household([self.person4, self.person5, self.person6], + change_id=True) + microcell.add_household([], change_id=True) for cell in self.test_population.cells: for person in cell.persons: diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py index b9138bdc..f921a700 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py @@ -30,10 +30,14 @@ def setUp(self) -> None: self.microcell2 = self.cell.microcells[1] self.microcell1.add_people(15) self.microcell2.add_people(5) - self.microcell1.add_household(self.microcell1.persons.copy()[:10]) - self.microcell1.add_household(self.microcell1.persons.copy()[10:]) - self.microcell2.add_household(self.microcell1.persons.copy()[:3]) - self.microcell2.add_household(self.microcell1.persons.copy()[3:]) + self.microcell1.add_household(self.microcell1.persons.copy()[:10], + change_id=True) + self.microcell1.add_household(self.microcell1.persons.copy()[10:], + change_id=True) + self.microcell2.add_household(self.microcell1.persons.copy()[:3], + change_id=True) + self.microcell2.add_household(self.microcell1.persons.copy()[3:], + change_id=True) # By default all susceptible, make 2 infectious self.initial_infected_person1 = self.microcell1.persons[0] self.initial_infected_person1.update_status(InfectionStatus(4)) From f2074a7cef644e93bc0fb48da236eec4ef2aa1f8 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Wed, 29 Nov 2023 20:33:00 +0000 Subject: [PATCH 44/53] Error fix --- pyEpiabm/pyEpiabm/sweep/travel_sweep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/sweep/travel_sweep.py b/pyEpiabm/pyEpiabm/sweep/travel_sweep.py index c45eab04..33fea5e9 100644 --- a/pyEpiabm/pyEpiabm/sweep/travel_sweep.py +++ b/pyEpiabm/pyEpiabm/sweep/travel_sweep.py @@ -151,7 +151,7 @@ def assign_microcell_and_household(self, number_individuals_introduced): microcells) with highest population density are selected. The individuals are assigned to one of these microcells by a uniform random choice between them. Individuals can end up in the same microcell. - Next, individuals are assigned to an wxisting or a new household within + Next, individuals are assigned to an existing or a new household within the selected microcell. Parameters @@ -195,7 +195,7 @@ def assign_microcell_and_household(self, number_individuals_introduced): selected_household.add_person(person) else: # Create new household - selected_microcell.add_household([person]) + selected_microcell.add_household([person], change_id=False) def check_leaving_individuals(self, time, person): """Check if individuals travel_end_time is reached. If interventions From d32616606f46cdb94d572b0b67d8c05bad71a40b Mon Sep 17 00:00:00 2001 From: Abbie Evans Date: Wed, 29 Nov 2023 21:45:59 +0000 Subject: [PATCH 45/53] Style fixes --- pyEpiabm/pyEpiabm/core/microcell.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index e55962fc..71cfc377 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -163,7 +163,7 @@ def add_household(self, people: list, change_id: bool): # If the person already has a household, then do not change # their id if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id) and \ - change_id: + change_id: person.set_id(household.id + "." + str(i)) else: logging.info(f"Person {person.id} has moved to household" @@ -176,7 +176,7 @@ def notify_person_status_change( self, old_status: InfectionStatus, new_status: InfectionStatus, - age_group) -> None: + age_group) -> None: """Notify Microcell that a person's status has changed. Parameters @@ -209,8 +209,8 @@ def set_location(self, loc: typing.Tuple[float, float]): def count_icu(self): return sum(map(lambda person: person.infection_status == - InfectionStatus.InfectICU, self.persons)) + InfectionStatus.InfectICU, self.persons)) def count_infectious(self): return sum(map(lambda person: person.is_infectious() is - True, self.persons)) + True, self.persons)) From 14cc1d2a850b070ef848de54ba451039df0bc586 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 30 Nov 2023 11:07:36 +0000 Subject: [PATCH 46/53] Updated add_household to have a default value --- pyEpiabm/pyEpiabm/core/microcell.py | 14 +++++++------- pyEpiabm/pyEpiabm/routine/toy_population_config.py | 2 +- pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py | 3 +-- .../test_sweep/test_initial_household_sweep.py | 9 ++++----- .../test_unit/test_sweep/test_travel_sweep.py | 12 ++++-------- 5 files changed, 17 insertions(+), 23 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index e55962fc..806497e5 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -141,7 +141,7 @@ def add_place(self, n: int, loc: typing.Tuple[float, float], self.cell.places.append(p) self.places.append(p) - def add_household(self, people: list, change_id: bool): + def add_household(self, people: list, change_id: bool = True): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. @@ -162,12 +162,12 @@ def add_household(self, people: list, change_id: bool): # If the person already has a household, then do not change # their id - if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id) and \ - change_id: - person.set_id(household.id + "." + str(i)) - else: - logging.info(f"Person {person.id} has moved to household" - f"{household.id}") + if change_id: + if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id): + person.set_id(household.id + "." + str(i)) + else: + logging.info(f"Person {person.id} has moved to " + f"household {household.id}") else: logging.info("Cannot create an empty household") diff --git a/pyEpiabm/pyEpiabm/routine/toy_population_config.py b/pyEpiabm/pyEpiabm/routine/toy_population_config.py index 3cdefe3c..bcdf389d 100644 --- a/pyEpiabm/pyEpiabm/routine/toy_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/toy_population_config.py @@ -136,7 +136,7 @@ def add_households(population: Population, household_number: int): person_choice = people_list[0] people_list.remove(person_choice) household_people.append(person_choice) - microcell.add_household(household_people, change_id=True) + microcell.add_household(household_people) @staticmethod def add_places(population: Population, place_number: float): diff --git a/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py b/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py index d7377afa..73d677d4 100644 --- a/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py +++ b/pyEpiabm/pyEpiabm/sweep/initial_household_sweep.py @@ -68,8 +68,7 @@ def household_allocation(self, population: Population): people_in_household = [] for i in range(k, k + m): people_in_household.append(microcell.persons[i]) - microcell.add_household(people_in_household, - change_id=True) + microcell.add_household(people_in_household) k += m def one_person_household_age(self, person: Person): diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py index c8738760..0daa8ed6 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_initial_household_sweep.py @@ -384,11 +384,10 @@ def test_call(self, mock_log): test_sweep = pe.sweep.InitialHouseholdSweep() test_sweep.bind_population(self.test_population) microcell = self.test_population.cells[0].microcells[0] - microcell.add_household([self.person1], change_id=True) - microcell.add_household([self.person2, self.person3], change_id=True) - microcell.add_household([self.person4, self.person5, self.person6], - change_id=True) - microcell.add_household([], change_id=True) + microcell.add_household([self.person1]) + microcell.add_household([self.person2, self.person3]) + microcell.add_household([self.person4, self.person5, self.person6]) + microcell.add_household([]) for cell in self.test_population.cells: for person in cell.persons: diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py index f921a700..b9138bdc 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py @@ -30,14 +30,10 @@ def setUp(self) -> None: self.microcell2 = self.cell.microcells[1] self.microcell1.add_people(15) self.microcell2.add_people(5) - self.microcell1.add_household(self.microcell1.persons.copy()[:10], - change_id=True) - self.microcell1.add_household(self.microcell1.persons.copy()[10:], - change_id=True) - self.microcell2.add_household(self.microcell1.persons.copy()[:3], - change_id=True) - self.microcell2.add_household(self.microcell1.persons.copy()[3:], - change_id=True) + self.microcell1.add_household(self.microcell1.persons.copy()[:10]) + self.microcell1.add_household(self.microcell1.persons.copy()[10:]) + self.microcell2.add_household(self.microcell1.persons.copy()[:3]) + self.microcell2.add_household(self.microcell1.persons.copy()[3:]) # By default all susceptible, make 2 infectious self.initial_infected_person1 = self.microcell1.persons[0] self.initial_infected_person1.update_status(InfectionStatus(4)) From 63aad2336b81e04a61a8d8bd9071d5f4c575f993 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 30 Nov 2023 16:03:01 +0000 Subject: [PATCH 47/53] Made changes travel_sweep to ensure that the ids of travellers are set --- pyEpiabm/pyEpiabm/intervention/travel_isolation.py | 2 +- pyEpiabm/pyEpiabm/sweep/travel_sweep.py | 4 +++- .../test_unit/test_intervention/test_travel_isolation.py | 3 ++- .../pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py | 5 +++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index 8930cf8a..bf85760a 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -94,7 +94,7 @@ def __call__(self, time): person.household.persons.remove(person) # Put in new household person.microcell.add_household([ - person], change_id=False) + person]) person.household.isolation_location = \ True else: diff --git a/pyEpiabm/pyEpiabm/sweep/travel_sweep.py b/pyEpiabm/pyEpiabm/sweep/travel_sweep.py index 33fea5e9..9e7c8ba7 100644 --- a/pyEpiabm/pyEpiabm/sweep/travel_sweep.py +++ b/pyEpiabm/pyEpiabm/sweep/travel_sweep.py @@ -193,9 +193,11 @@ def assign_microcell_and_household(self, number_individuals_introduced): [h for h in selected_microcell.households if not h.isolation_location]) selected_household.add_person(person) + person.set_id(selected_household.id + "." + + str(len(selected_household.persons) - 1)) else: # Create new household - selected_microcell.add_household([person], change_id=False) + selected_microcell.add_household([person]) def check_leaving_individuals(self, time, person): """Check if individuals travel_end_time is reached. If interventions diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py index 49612af6..912d8fd5 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py @@ -82,8 +82,9 @@ def test___call__(self, mock_random): self._microcell.add_people( 1, status=InfectionStatus.InfectASympt, age_group=7) person_introduced2 = self._microcell.persons[3] + person_introduced2.set_id("0.0.2.0") self._population.travellers.append(person_introduced2) - self._microcell.add_household([person_introduced2], change_id=False) + self._microcell.add_household([person_introduced2]) person_introduced2.travel_end_time = 40 self.travelisolation(time=21) self.assertTrue(person_introduced2.household.isolation_location) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py index b9138bdc..9fb0f8bd 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_sweep/test_travel_sweep.py @@ -131,12 +131,13 @@ def test_assign_microcell_and_household(self): self.travelsweep(time=1) self.assertEqual(len(self.microcell1.persons), 16) self.assertEqual(len(self.microcell1.households), 3) + self.assertEqual(self.microcell1.persons[-1].id, "0.0.2.0") def test_remove_leaving_individual(self): """Remove individuals introduced after their travel_end_time has passed and check if they are not in isolation and/or quarantine. If so, keep them in the population until isolation_start_time and/or - quaratine_start_time has also passed. Check if the population size + quarantine_start_time has also passed. Check if the population size is as expected at the considered time points. """ @@ -152,7 +153,7 @@ def test_remove_leaving_individual(self): # Remove after end time and isolation and quarantine over self.travelsweep.travel_params['ratio_introduce_cases'] = 0.5 self.travelsweep.travel_params['duration_travel_stay'] = [2, 2] - # Introduce individual staying untill day 16 + # Introduce individual staying until day 16 self.travelsweep(time=14) introduced_person = self.cell.persons[-1] introduced_person.travel_isolation_start_time = 18 From 15978f3eb74e4b9fef499f07a92bfcd8e629c11d Mon Sep 17 00:00:00 2001 From: abbie-evans <147088150+abbie-evans@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:13:24 +0000 Subject: [PATCH 48/53] Update for loop in add_household Co-authored-by: Kit Gallagher <60467371+KCGallagher@users.noreply.github.com> --- pyEpiabm/pyEpiabm/core/microcell.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index de834cf4..898c8781 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -156,8 +156,7 @@ def add_household(self, people: list, change_id: bool = True): """ if len(people) != 0: household = Household(self, loc=self.location) - for i in range(len(people)): - person = people[i] + for i, person in enumerate(people): household.add_person(person) # If the person already has a household, then do not change From ea0a512fff46e6029d70b89ed417a72c6eb164e2 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 30 Nov 2023 17:57:18 +0000 Subject: [PATCH 49/53] Changed add_household to have an override_person_id boolean attribute to determine whether we want the id overridden or not --- pyEpiabm/pyEpiabm/core/microcell.py | 24 +++++++++---------- .../pyEpiabm/intervention/travel_isolation.py | 2 +- .../routine/file_population_config.py | 2 +- .../test_unit/test_core/test_microcell.py | 22 ++++++++--------- .../test_travel_isolation.py | 3 ++- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index de834cf4..a9392dce 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -141,7 +141,7 @@ def add_place(self, n: int, loc: typing.Tuple[float, float], self.cell.places.append(p) self.places.append(p) - def add_household(self, people: list, change_id: bool = True): + def add_household(self, people: list, override_person_id: bool = True): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. @@ -149,9 +149,9 @@ def add_household(self, people: list, change_id: bool = True): ---------- people : list List of :class:`People` to add to household - change_id : bool - Boolean representing whether we wish to set the id of the people - of the household when the function is called or not + override_person_id : bool + Boolean representing whether we wish to override the id of the + people of the household when the function is called or not """ if len(people) != 0: @@ -160,14 +160,14 @@ def add_household(self, people: list, change_id: bool = True): person = people[i] household.add_person(person) - # If the person already has a household, then do not change - # their id - if change_id: - if not re.match("^\\d+\\.\\d+\\.\\d+\\.\\d+$", person.id): - person.set_id(household.id + "." + str(i)) - else: - logging.info(f"Person {person.id} has moved to " - f"household {household.id}") + if override_person_id: + person.set_id(household.id + "." + str(i)) + else: + # If the person already has a household, then do not change + # their id + logging.info(f"Person {person.id} has moved to " + f"household {household.id} but has " + f"not moved household") else: logging.info("Cannot create an empty household") diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index bf85760a..c07b32c0 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -94,7 +94,7 @@ def __call__(self, time): person.household.persons.remove(person) # Put in new household person.microcell.add_household([ - person]) + person], override_person_id=False) person.household.isolation_location = \ True else: diff --git a/pyEpiabm/pyEpiabm/routine/file_population_config.py b/pyEpiabm/pyEpiabm/routine/file_population_config.py index 6e8de039..425b9a40 100644 --- a/pyEpiabm/pyEpiabm/routine/file_population_config.py +++ b/pyEpiabm/pyEpiabm/routine/file_population_config.py @@ -207,7 +207,7 @@ def add_households(microcell: Microcell, household_number: int): person_choice = people_list[0] people_list.remove(person_choice) household_people.append(person_choice) - microcell.add_household(household_people, change_id=False) + microcell.add_household(household_people) @staticmethod @log_exceptions() diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index 0d08cc6c..e172e9e9 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -79,7 +79,8 @@ def test_setup(self, n=5): def test_add_household(self): self.microcell.add_people(1) - self.microcell.add_household(self.microcell.persons, change_id=True) + self.microcell.add_household(self.microcell.persons, + override_person_id=True) self.assertEqual(len(self.microcell.households), 1) original_household = self.microcell.households[0] self.assertEqual(len(original_household.persons), 1) @@ -89,26 +90,25 @@ def test_add_household(self): # Now add another original_household without changing # the id of the person - self.microcell.add_household(self.microcell.persons, change_id=False) + self.microcell.add_household(self.microcell.persons, + override_person_id=False) self.assertEqual(original_household.persons[0].id, original_household.id + ".0") @mock.patch('logging.info') def test_logging(self, mock_log): - self.microcell.add_household(self.microcell.persons, change_id=True) + # Check that the logger is called if there are no people in the + # household + self.microcell.add_household(self.microcell.persons, + override_person_id=True) mock_log.assert_called_once() @mock.patch('logging.info') def test_logging_duplicates(self, mock_log): - # Check that if the person already has a household, then we cannot - # change their id when they move to a new one + # Check that the logger is called if override_person_id is False self.microcell.add_people(1) - self.microcell.add_household(self.microcell.persons, change_id=True) - old_household = self.microcell.households[0] - person = self.microcell.persons[0] - old_household.persons.remove(person) - self.microcell.add_household([person], change_id=True) - self.assertEqual(person.id, old_household.id + ".0") + self.microcell.add_household(self.microcell.persons, + override_person_id=False) mock_log.assert_called_once() def test_report(self): diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py index 912d8fd5..607aa373 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py @@ -84,7 +84,8 @@ def test___call__(self, mock_random): person_introduced2 = self._microcell.persons[3] person_introduced2.set_id("0.0.2.0") self._population.travellers.append(person_introduced2) - self._microcell.add_household([person_introduced2]) + self._microcell.add_household([person_introduced2], + override_person_id=False) person_introduced2.travel_end_time = 40 self.travelisolation(time=21) self.assertTrue(person_introduced2.household.isolation_location) From 3c2f5ce4c235d9fd6c6db796845f1de55b765e8f Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 30 Nov 2023 18:08:47 +0000 Subject: [PATCH 50/53] Added further logging --- pyEpiabm/pyEpiabm/core/microcell.py | 2 +- pyEpiabm/pyEpiabm/intervention/travel_isolation.py | 6 +++++- .../test_unit/test_intervention/test_travel_isolation.py | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 18da5850..14f77177 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -166,7 +166,7 @@ def add_household(self, people: list, override_person_id: bool = True): # their id logging.info(f"Person {person.id} has moved to " f"household {household.id} but has " - f"not moved household") + f"not changed id") else: logging.info("Cannot create an empty household") diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index c07b32c0..fa36511a 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -1,7 +1,7 @@ # # Travel isolation Class # - +import logging import random from pyEpiabm.core import Parameters @@ -75,6 +75,10 @@ def __call__(self, time): selected_household = random.choice( existing_households) selected_household.add_person(person) + logging.info(f"Person {person.id} has " + f"finished isolating and " + f"has moved to household " + f"{selected_household.id}") else: person.household.isolation_location = \ diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py index 607aa373..0dc7e8c7 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py @@ -29,6 +29,7 @@ def setUp(self) -> None: self._microcell.add_people( 1, status=InfectionStatus.InfectASympt, age_group=5) self.person_introduced = self._microcell.persons[2] + self.person_introduced.set_id("0.0.0.2") self._population.travellers.append(self.person_introduced) self._microcell.households[0].add_person(self.person_introduced) self.person_introduced.travel_end_time = 25 @@ -58,7 +59,8 @@ def test__init__(self): self.params['hotel_isolate']) @mock.patch('random.random') - def test___call__(self, mock_random): + @mock.patch('logging.info') + def test___call__(self, mock_log, mock_random): mock_random.return_value = 0 # Before travel isolation starts self.assertFalse(hasattr( @@ -77,7 +79,7 @@ def test___call__(self, mock_random): self.assertIsNone(self.person_introduced.travel_isolation_start_time) self.assertEqual(len(self._microcell.households), 1) self.assertEqual(len(self.person_symp.household.persons), 3) - + mock_log.assert_called() # Introduce individual in single household self._microcell.add_people( 1, status=InfectionStatus.InfectASympt, age_group=7) From 4d13e6ca538df60524b75df0c05e933ea371dde8 Mon Sep 17 00:00:00 2001 From: mghosh00 <133675135+mghosh00@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:46:52 +0000 Subject: [PATCH 51/53] Update pyEpiabm/pyEpiabm/intervention/travel_isolation.py Co-authored-by: Kit Gallagher <60467371+KCGallagher@users.noreply.github.com> --- pyEpiabm/pyEpiabm/intervention/travel_isolation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index fa36511a..df13db33 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -96,7 +96,8 @@ def __call__(self, time): if len(person.household.persons) > 1: # Remove from old household person.household.persons.remove(person) - # Put in new household + # Move to temporary household + # N.B Person ID is not changed person.microcell.add_household([ person], override_person_id=False) person.household.isolation_location = \ From 84d5a46edd3801a1f55c68c4f6e64af33b21bf70 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 30 Nov 2023 19:00:50 +0000 Subject: [PATCH 52/53] Made changes --- pyEpiabm/pyEpiabm/core/microcell.py | 8 ++++---- .../pyEpiabm/intervention/travel_isolation.py | 2 +- .../test_unit/test_core/test_microcell.py | 18 +++++++++++------- .../test_intervention/test_travel_isolation.py | 11 +++++++++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/pyEpiabm/pyEpiabm/core/microcell.py b/pyEpiabm/pyEpiabm/core/microcell.py index 14f77177..8a26c4c5 100644 --- a/pyEpiabm/pyEpiabm/core/microcell.py +++ b/pyEpiabm/pyEpiabm/core/microcell.py @@ -141,7 +141,7 @@ def add_place(self, n: int, loc: typing.Tuple[float, float], self.cell.places.append(p) self.places.append(p) - def add_household(self, people: list, override_person_id: bool = True): + def add_household(self, people: list, update_person_id: bool = True): """Adds a default :class:`Household` to Microcell and fills it with a number of :class:`Person` s. @@ -149,8 +149,8 @@ def add_household(self, people: list, override_person_id: bool = True): ---------- people : list List of :class:`People` to add to household - override_person_id : bool - Boolean representing whether we wish to override the id of the + update_person_id : bool + Boolean representing whether we wish to update the id of the people of the household when the function is called or not """ @@ -159,7 +159,7 @@ def add_household(self, people: list, override_person_id: bool = True): for i, person in enumerate(people): household.add_person(person) - if override_person_id: + if update_person_id: person.set_id(household.id + "." + str(i)) else: # If the person already has a household, then do not change diff --git a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py index fa36511a..41259c36 100644 --- a/pyEpiabm/pyEpiabm/intervention/travel_isolation.py +++ b/pyEpiabm/pyEpiabm/intervention/travel_isolation.py @@ -98,7 +98,7 @@ def __call__(self, time): person.household.persons.remove(person) # Put in new household person.microcell.add_household([ - person], override_person_id=False) + person], update_person_id=False) person.household.isolation_location = \ True else: diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py index e172e9e9..4298d3be 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_core/test_microcell.py @@ -80,7 +80,7 @@ def test_setup(self, n=5): def test_add_household(self): self.microcell.add_people(1) self.microcell.add_household(self.microcell.persons, - override_person_id=True) + update_person_id=True) self.assertEqual(len(self.microcell.households), 1) original_household = self.microcell.households[0] self.assertEqual(len(original_household.persons), 1) @@ -91,7 +91,7 @@ def test_add_household(self): # Now add another original_household without changing # the id of the person self.microcell.add_household(self.microcell.persons, - override_person_id=False) + update_person_id=False) self.assertEqual(original_household.persons[0].id, original_household.id + ".0") @@ -100,16 +100,20 @@ def test_logging(self, mock_log): # Check that the logger is called if there are no people in the # household self.microcell.add_household(self.microcell.persons, - override_person_id=True) - mock_log.assert_called_once() + update_person_id=True) + mock_log.assert_called_once_with("Cannot create an empty household") @mock.patch('logging.info') def test_logging_duplicates(self, mock_log): - # Check that the logger is called if override_person_id is False + # Check that the logger is called if update_person_id is False self.microcell.add_people(1) self.microcell.add_household(self.microcell.persons, - override_person_id=False) - mock_log.assert_called_once() + update_person_id=False) + person = self.microcell.persons[0] + household = self.microcell.households[0] + mock_log.assert_called_once_with(f"Person {person.id} has moved to " + f"household {household.id} but has " + f"not changed id") def test_report(self): self.microcell.add_people(5) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py index 0dc7e8c7..622e9c8e 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py @@ -79,7 +79,14 @@ def test___call__(self, mock_log, mock_random): self.assertIsNone(self.person_introduced.travel_isolation_start_time) self.assertEqual(len(self._microcell.households), 1) self.assertEqual(len(self.person_symp.household.persons), 3) - mock_log.assert_called() + mock_log.assert_has_calls([mock.call(f"Person 0.0.0.2 has moved to " + f"household 0.0.1 but has " + f"not changed id"), + mock.call(f"Person 0.0.0.2 has " + f"finished isolating and " + f"has moved to household " + f"0.0.0")]) + self.assertEqual(mock_log.call_count, 2) # Introduce individual in single household self._microcell.add_people( 1, status=InfectionStatus.InfectASympt, age_group=7) @@ -87,7 +94,7 @@ def test___call__(self, mock_log, mock_random): person_introduced2.set_id("0.0.2.0") self._population.travellers.append(person_introduced2) self._microcell.add_household([person_introduced2], - override_person_id=False) + update_person_id=False) person_introduced2.travel_end_time = 40 self.travelisolation(time=21) self.assertTrue(person_introduced2.household.isolation_location) From 3edfb19f4032ddfee60a6cd7a24a89bd3e17cd45 Mon Sep 17 00:00:00 2001 From: Matthew Ghosh Date: Thu, 30 Nov 2023 19:04:15 +0000 Subject: [PATCH 53/53] Flake8 fix --- .../test_intervention/test_travel_isolation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py index 622e9c8e..6423a2ea 100644 --- a/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py +++ b/pyEpiabm/pyEpiabm/tests/test_unit/test_intervention/test_travel_isolation.py @@ -79,13 +79,13 @@ def test___call__(self, mock_log, mock_random): self.assertIsNone(self.person_introduced.travel_isolation_start_time) self.assertEqual(len(self._microcell.households), 1) self.assertEqual(len(self.person_symp.household.persons), 3) - mock_log.assert_has_calls([mock.call(f"Person 0.0.0.2 has moved to " - f"household 0.0.1 but has " - f"not changed id"), - mock.call(f"Person 0.0.0.2 has " - f"finished isolating and " - f"has moved to household " - f"0.0.0")]) + mock_log.assert_has_calls([mock.call("Person 0.0.0.2 has moved to " + + "household 0.0.1 but has " + + "not changed id"), + mock.call("Person 0.0.0.2 has " + + "finished isolating and " + + "has moved to household " + + "0.0.0")]) self.assertEqual(mock_log.call_count, 2) # Introduce individual in single household self._microcell.add_people(