From 907a8a845ed051297b5997d74e7ca0147d0e842f Mon Sep 17 00:00:00 2001 From: Margherita Molaro <48129834+marghe-molaro@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:56:58 +0100 Subject: [PATCH 1/6] Fix running_total_footprint in mode 2 (#1386) * Fix running_total_footprint in mode 2 * Corrected --- src/tlo/methods/healthsystem.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tlo/methods/healthsystem.py b/src/tlo/methods/healthsystem.py index fe57444feb..5f34f14b4a 100644 --- a/src/tlo/methods/healthsystem.py +++ b/src/tlo/methods/healthsystem.py @@ -2425,8 +2425,7 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None: ) # Update today's footprint based on actual call and squeeze factor - self.module.running_total_footprint -= original_call - self.module.running_total_footprint += updated_call + self.module.running_total_footprint.update(updated_call) # Write to the log self.module.record_hsi_event( From ad9d928a6e4002e552b1261f1c6677d0e149e064 Mon Sep 17 00:00:00 2001 From: Margherita Molaro <48129834+marghe-molaro@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:57:25 +0100 Subject: [PATCH 2/6] Allow for use_funded_or_actual_staffing to be changed mid-simulation (#1388) * Allow for use_funded_or_actual_staffing to be changed mid-simulatio * linting * refactoring to allow control on use_funded_or_actual to behave as expectd (being a property, with built-in setter/getter). --------- Co-authored-by: Tim Hallett <39991060+tbhallett@users.noreply.github.com> --- .../ResourceFile_HealthSystem_parameters.csv | 4 +- src/tlo/methods/healthsystem.py | 71 ++++++++++++++----- tests/test_healthsystem.py | 3 + 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/resources/healthsystem/ResourceFile_HealthSystem_parameters.csv b/resources/healthsystem/ResourceFile_HealthSystem_parameters.csv index 6ca37170a7..8c670a914d 100644 --- a/resources/healthsystem/ResourceFile_HealthSystem_parameters.csv +++ b/resources/healthsystem/ResourceFile_HealthSystem_parameters.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5df67165565fc88987f848e43363616e2ea4135de7c74d131e785ddc0178f123 -size 706 +oid sha256:f1cb38ba76c5673855e2e17e28ad1d36b5cec07d5d7872a7d6bc8aafde0f7009 +size 828 diff --git a/src/tlo/methods/healthsystem.py b/src/tlo/methods/healthsystem.py index 5f34f14b4a..8099346ddf 100644 --- a/src/tlo/methods/healthsystem.py +++ b/src/tlo/methods/healthsystem.py @@ -236,6 +236,9 @@ class HealthSystem(Module): 'year_cons_availability_switch': Parameter( Types.INT, "Year in which consumable availability switch is enforced. The change happens" "on 1st January of that year.)"), + 'year_use_funded_or_actual_staffing_switch': Parameter( + Types.INT, "Year in which switch for `use_funded_or_actual_staffing` is enforced. (The change happens" + "on 1st January of that year.)"), 'priority_rank': Parameter( Types.DICT, "Data on the priority ranking of each of the Treatment_IDs to be adopted by " " the queueing system under different policies, where the lower the number the higher" @@ -323,7 +326,10 @@ class HealthSystem(Module): Types.INT, 'Mode considered after a mode switch in year_mode_switch.'), 'cons_availability_postSwitch': Parameter( Types.STRING, 'Consumables availability after switch in `year_cons_availability_switch`. Acceptable values' - 'are the same as those for Parameter `cons_availability`.') + 'are the same as those for Parameter `cons_availability`.'), + 'use_funded_or_actual_staffing_postSwitch': Parameter( + Types.STRING, 'Staffing availability after switch in `year_use_funded_or_actual_staffing_switch`. ' + 'Acceptable values are the same as those for Parameter `use_funded_or_actual_staffing`.'), } PROPERTIES = { @@ -447,10 +453,9 @@ def __init__( assert isinstance(capabilities_coefficient, float) self.capabilities_coefficient = capabilities_coefficient - # Find which set of assumptions to use - those for the actual staff available or the funded staff available - if use_funded_or_actual_staffing is not None: - assert use_funded_or_actual_staffing in ['actual', 'funded', 'funded_plus'] + # Save argument for assumptions to use for 'use_funded_or_actual_staffing` self.arg_use_funded_or_actual_staffing = use_funded_or_actual_staffing + self._use_funded_or_actual_staffing = None # <-- this is the private internal store of the value that is used. # Define (empty) list of registered disease modules (filled in at `initialise_simulation`) self.recognised_modules_names = [] @@ -637,9 +642,15 @@ def pre_initialise_population(self): # Determine service_availability self.service_availability = self.get_service_availability() - self.process_human_resources_files( - use_funded_or_actual_staffing=self.get_use_funded_or_actual_staffing() - ) + # Process health system organisation files (Facilities, Appointment Types, Time Taken etc.) + self.process_healthsystem_organisation_files() + + # Set value for `use_funded_or_actual_staffing` and process Human Resources Files + # (Initially set value should be equal to what is specified by the parameter, but overwritten with what was + # provided in argument if an argument was specified -- provided for backward compatibility/debugging.) + self.use_funded_or_actual_staffing = self.parameters['use_funded_or_actual_staffing'] \ + if self.arg_use_funded_or_actual_staffing is None \ + else self.arg_use_funded_or_actual_staffing # Initialise the BedDays class self.bed_days = BedDays(hs_module=self, @@ -738,6 +749,16 @@ def initialise_simulation(self, sim): Date(self.parameters["year_equip_availability_switch"], 1, 1) ) + # Schedule an equipment availability switch + sim.schedule_event( + HealthSystemChangeParameters( + self, + parameters={ + 'use_funded_or_actual_staffing': self.parameters['use_funded_or_actual_staffing_postSwitch'] + } + ), + Date(self.parameters["year_use_funded_or_actual_staffing_switch"], 1, 1) + ) # Schedule a one-off rescaling of _daily_capabilities broken down by officer type and level. # This occurs on 1st January of the year specified in the parameters. @@ -809,8 +830,15 @@ def setup_priority_policy(self): if 'Tb' in self.sim.modules: self.list_fasttrack.append(('tb_diagnosed', 'FT_if_tbdiagnosed')) - def process_human_resources_files(self, use_funded_or_actual_staffing: str): - """Create the data-structures needed from the information read into the parameters.""" + def process_healthsystem_organisation_files(self): + """Create the data-structures needed from the information read into the parameters: + * self._facility_levels + * self._appointment_types + * self._appt_times + * self._appt_type_by_facLevel + * self._facility_by_facility_id + * self._facilities_for_each_district + """ # * Define Facility Levels self._facility_levels = set(self.parameters['Master_Facilities_List']['Facility_Level']) - {'5'} @@ -903,6 +931,10 @@ def process_human_resources_files(self, use_funded_or_actual_staffing: str): self._facility_by_facility_id = facilities_by_facility_id self._facilities_for_each_district = facilities_per_level_and_district + def setup_daily_capabilities(self, use_funded_or_actual_staffing): + """Set up `self._daily_capabilities` and `self._officers_with_availability`. + This is called when the value for `use_funded_or_actual_staffing` is set - at the beginning of the simulation + and when the assumption when the underlying assumption for `use_funded_or_actual_staffing` is updated""" # * Store 'DailyCapabilities' in correct format and using the specified underlying assumptions self._daily_capabilities = self.format_daily_capabilities(use_funded_or_actual_staffing) @@ -1142,13 +1174,17 @@ def get_mode_appt_constraints(self) -> int: if self.arg_mode_appt_constraints is None \ else self.arg_mode_appt_constraints - def get_use_funded_or_actual_staffing(self) -> str: - """Returns `use_funded_or_actual_staffing`. (Should be equal to what is specified by the parameter, but - overwrite with what was provided in argument if an argument was specified -- provided for backward - compatibility/debugging.)""" - return self.parameters['use_funded_or_actual_staffing'] \ - if self.arg_use_funded_or_actual_staffing is None \ - else self.arg_use_funded_or_actual_staffing + @property + def use_funded_or_actual_staffing(self) -> str: + """Returns value for `use_funded_or_actual_staffing`.""" + return self._use_funded_or_actual_staffing + + @use_funded_or_actual_staffing.setter + def use_funded_or_actual_staffing(self, use_funded_or_actual_staffing) -> str: + """Set value for `use_funded_or_actual_staffing` and update the daily_capabilities accordingly. """ + assert use_funded_or_actual_staffing in ['actual', 'funded', 'funded_plus'] + self._use_funded_or_actual_staffing = use_funded_or_actual_staffing + self.setup_daily_capabilities(self._use_funded_or_actual_staffing) def get_priority_policy_initial(self) -> str: """Returns `priority_policy`. (Should be equal to what is specified by the parameter, but @@ -2762,6 +2798,7 @@ class HealthSystemChangeParameters(Event, PopulationScopeEventMixin): * `cons_availability` * `beds_availability` * `equip_availability` + * `use_funded_or_actual_staffing` Note that no checking is done here on the suitability of values of each parameter.""" def __init__(self, module: HealthSystem, parameters: Dict): @@ -2788,6 +2825,8 @@ def apply(self, population): if 'equip_availability' in self._parameters: self.module.equipment.availability = self._parameters['equip_availability'] + if 'use_funded_or_actual_staffing' in self._parameters: + self.module.use_funded_or_actual_staffing = self._parameters['use_funded_or_actual_staffing'] class DynamicRescalingHRCapabilities(RegularEvent, PopulationScopeEventMixin): """ This event exists to scale the daily capabilities assumed at fixed time intervals""" diff --git a/tests/test_healthsystem.py b/tests/test_healthsystem.py index 801150fdcc..ae212a4f48 100644 --- a/tests/test_healthsystem.py +++ b/tests/test_healthsystem.py @@ -1347,6 +1347,7 @@ def test_HealthSystemChangeParameters(seed, tmpdir): 'cons_availability': 'all', 'beds_availability': 'default', 'equip_availability': 'default', + 'use_funded_or_actual_staffing': 'funded_plus', } new_parameters = { 'mode_appt_constraints': 2, @@ -1355,6 +1356,7 @@ def test_HealthSystemChangeParameters(seed, tmpdir): 'cons_availability': 'none', 'beds_availability': 'none', 'equip_availability': 'all', + 'use_funded_or_actual_staffing': 'actual', } class CheckHealthSystemParameters(RegularEvent, PopulationScopeEventMixin): @@ -1371,6 +1373,7 @@ def apply(self, population): _params['cons_availability'] = hs.consumables.availability _params['beds_availability'] = hs.bed_days.availability _params['equip_availability'] = hs.equipment.availability + _params['use_funded_or_actual_staffing'] = hs.use_funded_or_actual_staffing logger = logging.getLogger('tlo.methods.healthsystem') logger.info(key='CheckHealthSystemParameters', data=_params) From 27291aefc5afc4ef943b4786b182c490c0b0a9c4 Mon Sep 17 00:00:00 2001 From: Emmanuel Mnjowe <32415622+mnjowe@users.noreply.github.com> Date: Mon, 3 Jun 2024 19:10:27 +0200 Subject: [PATCH 3/6] Fix numpy array data type error on Windows (#1391) --- src/tlo/methods/equipment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/equipment.py b/src/tlo/methods/equipment.py index 916927d792..dd86f91108 100644 --- a/src/tlo/methods/equipment.py +++ b/src/tlo/methods/equipment.py @@ -115,7 +115,7 @@ def _get_equipment_availability_probabilities(self) -> pd.Series: calculation if the equipment availability change event occurs during the simulation. """ dat = self.data_availability.set_index( - [self.data_availability["Facility_ID"].astype(int), self.data_availability["Item_Code"].astype(int)] + [self.data_availability["Facility_ID"].astype(np.int64), self.data_availability["Item_Code"].astype(np.int64)] )["Pr_Available"] # Confirm that there is an estimate for every item_code at every facility_id From e8c94e43c8e031f5acdaa7c4f338552287757f4a Mon Sep 17 00:00:00 2001 From: Asif Tamuri Date: Tue, 4 Jun 2024 00:17:45 +0100 Subject: [PATCH 4/6] Allow users to pass command-line arguments to scenarios (#1373) Co-authored-by: Matt Graham --- src/tlo/cli.py | 10 ++++-- src/tlo/scenario.py | 44 ++++++++++++++++++++++-- src/tlo/util.py | 5 +++ tests/resources/scenario.py | 35 +++++++++++++++++++ tests/test_scenario.py | 68 +++++++++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 tests/resources/scenario.py create mode 100644 tests/test_scenario.py diff --git a/src/tlo/cli.py b/src/tlo/cli.py index 9e780911f9..c5b0c3f86d 100644 --- a/src/tlo/cli.py +++ b/src/tlo/cli.py @@ -44,17 +44,23 @@ def cli(ctx, config_file, verbose): ctx.obj["verbose"] = verbose -@cli.command() +@cli.command(context_settings=dict(ignore_unknown_options=True)) @click.argument("scenario_file", type=click.Path(exists=True)) @click.option("--draw-only", is_flag=True, help="Only generate draws; do not run the simulation") @click.option("--draw", "-d", nargs=2, type=int) @click.option("--output-dir", type=str) -def scenario_run(scenario_file, draw_only, draw: tuple, output_dir=None): +@click.argument('scenario_args', nargs=-1, type=click.UNPROCESSED) +def scenario_run(scenario_file, draw_only, draw: tuple, output_dir=None, scenario_args=None): """Run the specified scenario locally. SCENARIO_FILE is path to file containing a scenario class """ scenario = load_scenario(scenario_file) + + # if we have other scenario arguments, parse them + if scenario_args is not None: + scenario.parse_arguments(scenario_args) + config = scenario.save_draws(return_config=True) json_string = json.dumps(config, indent=2) diff --git a/src/tlo/scenario.py b/src/tlo/scenario.py index d92f0b4aef..e24858482f 100644 --- a/src/tlo/scenario.py +++ b/src/tlo/scenario.py @@ -60,12 +60,13 @@ def draw_parameters(self, draw_number, rng): """ import abc +import argparse import datetime import json import pickle from itertools import product from pathlib import Path, PurePosixPath -from typing import Optional +from typing import List, Optional import numpy as np @@ -125,6 +126,42 @@ def __init__( self.resources = resources_path self.rng = None self.scenario_path = None + self.arguments = None + + def parse_arguments(self, extra_arguments: List[str]) -> None: + """Base class command line arguments handling for scenarios. This should not be overridden by subclasses. + Subclasses can add argument handling to their classes by implementing the `add_arguments` method.""" + + self.arguments = extra_arguments + + parser = argparse.ArgumentParser() + + # add arguments from the subclass + self.add_arguments(parser) + + arguments = parser.parse_args(self.arguments) + + # set the arguments as attributes of the scenario + for key, value in vars(arguments).items(): + if value is not None: + if hasattr(self, key): + logger.info(key="message", data=f"Overriding attribute: {key}: {getattr(self, key)} -> {value}") + setattr(self, key, value) + + def add_arguments(self, parser: argparse.ArgumentParser) -> None: + """Add scenario-specific arguments that can be passed to scenario from the command line. + + This method is called to add scenario-specific arguments to the command line parser. The method should add + arguments to the parser using the `add_argument` method. Arguments that have a value of None are not set or + overridden. + + :param parser: An instance of `argparse.ArgumentParser` to which arguments should be added. + + Example:: + + parser.add_argument('--pop-size', type=int, default=20_000, help='Population size') + """ + pass @abc.abstractmethod def log_configuration(self, **kwargs): @@ -282,11 +319,13 @@ def get_run_config(self, scenario_path): return { "scenario_script_path": str(PurePosixPath(scenario_path)), "scenario_seed": self.scenario.seed, + "arguments": self.scenario.arguments, "runs_per_draw": self.runs_per_draw, "draws": self.draws, } - def save_config(self, config, output_path): + @staticmethod + def save_config(config, output_path): with open(output_path, "w") as f: f.write(json.dumps(config, indent=2)) @@ -297,6 +336,7 @@ def __init__(self, run_configuration_path): with open(run_configuration_path, "r") as f: self.run_config = json.load(f) self.scenario = ScenarioLoader(self.run_config["scenario_script_path"]).get_scenario() + self.scenario.parse_arguments(self.run_config["arguments"]) logger.info(key="message", data=f"Loaded scenario using {run_configuration_path}") logger.info(key="message", data=f"Found {self.number_of_draws} draws; {self.runs_per_draw} runs/draw") diff --git a/src/tlo/util.py b/src/tlo/util.py index cafd04f738..77924e4fa3 100644 --- a/src/tlo/util.py +++ b/src/tlo/util.py @@ -407,6 +407,11 @@ def random_date(start, end, rng): return start + DateOffset(days=rng.randint(0, (end - start).days)) +def str_to_pandas_date(date_string): + """Convert a string with the format YYYY-MM-DD to a pandas Timestamp (aka TLO Date) object.""" + return pd.to_datetime(date_string, format="%Y-%m-%d") + + def hash_dataframe(dataframe: pd.DataFrame): def coerce_lists_to_tuples(df: pd.DataFrame) -> pd.DataFrame: """Coerce columns in a pd.DataFrame that are lists to tuples. This step is needed before hashing a pd.DataFrame diff --git a/tests/resources/scenario.py b/tests/resources/scenario.py new file mode 100644 index 0000000000..204c322f11 --- /dev/null +++ b/tests/resources/scenario.py @@ -0,0 +1,35 @@ +from tlo import Date, logging +from tlo.scenario import BaseScenario + + +class TestScenario(BaseScenario): + def __init__(self): + super().__init__() + self.seed = 655123742 + self.start_date = Date(2010, 1, 1) + self.end_date = Date(2011, 1, 1) + self.pop_size = 2000 + self.number_of_draws = 5 + self.runs_per_draw = 1 + + def log_configuration(self): + return { + 'directory': None, + 'custom_levels': { + '*': logging.INFO, + } + } + + def modules(self): + return [] + + def add_arguments(self, parser): + parser.add_argument('--pop-size', type=int) + + def draw_parameters(self, draw_number, rng): + return { + 'Lifestyle': { + 'init_p_urban': rng.randint(10, 20) / 100.0, + 'init_p_high_sugar': 0.52, + }, + } diff --git a/tests/test_scenario.py b/tests/test_scenario.py new file mode 100644 index 0000000000..1feac8ef87 --- /dev/null +++ b/tests/test_scenario.py @@ -0,0 +1,68 @@ +import json +import os +from pathlib import Path + +import pytest + +from tlo.scenario import BaseScenario, SampleRunner, ScenarioLoader + + +@pytest.fixture +def scenario_path(): + return Path(f'{os.path.dirname(__file__)}/resources/scenario.py') + + +@pytest.fixture +def pop_size(): + return 100 + + +@pytest.fixture +def loaded_scenario(scenario_path): + return ScenarioLoader(scenario_path).get_scenario() + + +@pytest.fixture +def arguments(pop_size): + return ['--pop-size', str(pop_size)] + + +@pytest.fixture +def loaded_scenario_with_parsed_arguments(loaded_scenario, arguments): + loaded_scenario.parse_arguments(arguments) + return loaded_scenario + + +def test_load(loaded_scenario, scenario_path): + """Check we can load the scenario class from a file""" + assert isinstance(loaded_scenario, BaseScenario) + assert loaded_scenario.scenario_path == scenario_path + assert hasattr(loaded_scenario, "pop_size") # Default value set in initialiser + + +def test_parse_arguments(loaded_scenario_with_parsed_arguments, pop_size): + """Check we can parse arguments related to the scenario. pop-size is used by our scenario, + suspend-date is used in base class""" + assert loaded_scenario_with_parsed_arguments.pop_size == pop_size + + +def test_config(tmp_path, loaded_scenario_with_parsed_arguments, arguments): + """Create the run configuration and check we've got the right values in there.""" + config = loaded_scenario_with_parsed_arguments.save_draws(return_config=True) + assert config['scenario_seed'] == loaded_scenario_with_parsed_arguments.seed + assert config['arguments'] == arguments + assert len(config['draws']) == loaded_scenario_with_parsed_arguments.number_of_draws + + +def test_runner(tmp_path, loaded_scenario_with_parsed_arguments, pop_size): + """Check we can load the scenario from a configuration file.""" + config = loaded_scenario_with_parsed_arguments.save_draws(return_config=True) + config_path = tmp_path / 'scenario.json' + with open(config_path, 'w') as f: + f.write(json.dumps(config, indent=2)) + runner = SampleRunner(config_path) + scenario = runner.scenario + assert isinstance(scenario, BaseScenario) + assert scenario.__class__.__name__ == 'TestScenario' + assert scenario.pop_size == pop_size + assert runner.number_of_draws == loaded_scenario_with_parsed_arguments.number_of_draws From 1cab017c5e8499a48335e66d61ddde44d99452f3 Mon Sep 17 00:00:00 2001 From: Asif Tamuri Date: Tue, 4 Jun 2024 22:21:51 +0100 Subject: [PATCH 5/6] Default scenario extra arguments to empty list rather than None (#1397) --- src/tlo/scenario.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tlo/scenario.py b/src/tlo/scenario.py index e24858482f..aa1be42aa9 100644 --- a/src/tlo/scenario.py +++ b/src/tlo/scenario.py @@ -64,6 +64,7 @@ def draw_parameters(self, draw_number, rng): import datetime import json import pickle +from collections.abc import Iterable from itertools import product from pathlib import Path, PurePosixPath from typing import List, Optional @@ -126,12 +127,17 @@ def __init__( self.resources = resources_path self.rng = None self.scenario_path = None - self.arguments = None + self.arguments = [] def parse_arguments(self, extra_arguments: List[str]) -> None: """Base class command line arguments handling for scenarios. This should not be overridden by subclasses. Subclasses can add argument handling to their classes by implementing the `add_arguments` method.""" + if extra_arguments is None: + return + + assert isinstance(extra_arguments, Iterable), "Arguments must be a list of strings" + self.arguments = extra_arguments parser = argparse.ArgumentParser() @@ -336,7 +342,8 @@ def __init__(self, run_configuration_path): with open(run_configuration_path, "r") as f: self.run_config = json.load(f) self.scenario = ScenarioLoader(self.run_config["scenario_script_path"]).get_scenario() - self.scenario.parse_arguments(self.run_config["arguments"]) + if self.run_config["arguments"] is not None: + self.scenario.parse_arguments(self.run_config["arguments"]) logger.info(key="message", data=f"Loaded scenario using {run_configuration_path}") logger.info(key="message", data=f"Found {self.number_of_draws} draws; {self.runs_per_draw} runs/draw") From 6af737a820aa43f4d1bda9ce6edb058e52eeae30 Mon Sep 17 00:00:00 2001 From: sakshimohan <47076591+sakshimohan@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:23:02 +0100 Subject: [PATCH 6/6] Editing units of consumable required for each HSI (#1298) * add latest version of costing resource file * add latest version of costing resource file - with updated units for cost of consumables * update Gentamycin (units) * update item quantities for HIV prep and infant prep * update 1g to 1mg for Albendazole (Item code 52) * update units for gloves from 1 glove to "1 pair of gloves" * update item quantities for TB drugs add 3HP as new consumable for IPT * add conditions for each IPT recommendation * add prices for ARVs - and create placeholder for Isoniazid/Rifapentine * add Isoniazid/Rifapentine to `ResourceFile_Consumables_Items_and_Packages.csv` * add Isoniazid/Rifapentine to all relevant RFs - 1. `ResourceFile_Consumables_Items_and_Packages.csv` - list of item codes and names - 2. `ResourceFile_consumabes_matched.csv` - crosswalk between consumables in the TLO model and the OpenLMIS dataset to extract availability (used proxy 'Isoniazid, 100mg' OR 'Isoniazid, 300mg') - 3. `ResourceFile_Consumables_availability_small.csv` - Final availability estimates - updated by running `consumables_availability_estimation.py` - 4 `ResourceFile_Costing.xlsx` - cost of tablet based on external web report * Update to the functionality for requesting consumables across the MNH modules to allow for clearer requesting of units of consumables for costing (correct number of units not yet implemented for all consumables) * fixes to failing tests * linting * linting * change cotrimoxazole units to mg * merge in updated unit costs 3hp added and linked to consumables dataset * added dosage for amitriptyline antidepressant treatment * added dosage for epilepsy treatment * fix error in postnatal_supervisor.py leading to failing test * units added for care_of_women_during_pregnancy.py * units added for care_of_women_during_pregnancy.py * units added for labour.py * fix failing test. initial doses for newborn outcomes/postnatal supervisor * fix failing test. initial doses for newborn outcomes/postnatal supervisor * delete temporary ~ file created * update units of safety box to "1 disposed syringe (100 syringes per box)" * fix failing test. initial doses for newborn outcomes/postnatal supervisor * fixed error in labour. COPD consumable unites * remove consumable packages from diarrhoea.py * diarrhoea.py consumable units * cmd.py consumable units * rti.py consumable units * alri.py consumable units plus fixed error in diarrhoea.py * co: get item codes of consumables from item names and define number of units per case in the module (using new chosen units) * Add cystoscope, endoscope and prostate specific antigen test - add three consumables to `ResourceFile_Consumables_Items_and_Packages.csv` using the `generate_consumables_item_codes_and_packages.py` script - This is a replication of commit ff4d072 from PR #1341 * co: alternative consumables chosen by probs * co: 21 tablets per packet for both types of pills (informed by Emi) * co: update TODOs * cancer_cons: consumables updated and required units per case defined (as nmbs of chosen units) * [no ci] cancer_cons: rm outdated TODOs * [no ci] co: rm resolved TODOs * co: rm TODOs as opened an issue to resolve them (#1384) * cancers: missing arguments added * add availability data cancer consumables - Biopsy needle and Specimen container (This is based on assumptions made in `"05 - Resources/Module-healthsystem/consumables raw files/ResourceFile_hhfa_consumables.xlsx` * Revert "cancers: missing arguments added" This reverts commit 5e3d5c045e8451b56dfee833b31e4a041d3e06f9. * cancer_cons: rm get_consumable_item_codes_cancers() argument * cancer_cons: names corrections -- item & consumables set * add depn to health system to copd * remove packing with dict * refactoring and adding todo * fix misnamed tb consumable * roll back file added by accident * test_co & co: alternatives rolled back * roll back second output * simplify _get_cons_group as no longer changing dose by age * remove todo, now resolved * linting and removed completed todo * linting --------- Co-authored-by: sm2511 Co-authored-by: tdm32 Co-authored-by: joehcollins Co-authored-by: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Co-authored-by: Eva Janouskova --- resources/costing/ResourceFile_Costing.xlsx | 3 + ...rceFile_Consumables_Items_and_Packages.csv | 4 +- ...rceFile_Consumables_availability_small.csv | 4 +- .../ResourceFile_consumables_matched.csv | 4 +- .../consumables_availability_estimation.py | 12 +- ...ate_consumables_item_codes_and_packages.py | 53 +++- src/tlo/methods/alri.py | 139 +++------ src/tlo/methods/cancer_consumables.py | 111 ++++--- src/tlo/methods/cardio_metabolic_disorders.py | 41 ++- .../methods/care_of_women_during_pregnancy.py | 293 ++++++++++-------- src/tlo/methods/contraception.py | 62 +++- src/tlo/methods/copd.py | 31 +- src/tlo/methods/depression.py | 10 +- src/tlo/methods/diarrhoea.py | 36 ++- src/tlo/methods/epilepsy.py | 29 +- src/tlo/methods/hiv.py | 73 +++-- src/tlo/methods/labour.py | 289 +++++++++-------- src/tlo/methods/newborn_outcomes.py | 64 ++-- src/tlo/methods/postnatal_supervisor.py | 32 +- src/tlo/methods/pregnancy_helper_functions.py | 17 +- src/tlo/methods/rti.py | 63 ++-- src/tlo/methods/tb.py | 42 ++- ...al_health_helper_and_analysis_functions.py | 6 +- 23 files changed, 796 insertions(+), 622 deletions(-) create mode 100644 resources/costing/ResourceFile_Costing.xlsx diff --git a/resources/costing/ResourceFile_Costing.xlsx b/resources/costing/ResourceFile_Costing.xlsx new file mode 100644 index 0000000000..9f06132aaa --- /dev/null +++ b/resources/costing/ResourceFile_Costing.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2afa3649672e10c9741b26dc70fa0f4496af4fccfafbf8b7b70f3b90b291a4fb +size 1007463 diff --git a/resources/healthsystem/consumables/ResourceFile_Consumables_Items_and_Packages.csv b/resources/healthsystem/consumables/ResourceFile_Consumables_Items_and_Packages.csv index 8af8f070b2..0ee403abb0 100644 --- a/resources/healthsystem/consumables/ResourceFile_Consumables_Items_and_Packages.csv +++ b/resources/healthsystem/consumables/ResourceFile_Consumables_Items_and_Packages.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85e2c3ba8037e74490751fbb8384709dff1907c785c856f0394f40b4fc024da3 -size 253400 +oid sha256:4106c2e3ae068d40b115857885b673bec3e1114be5183c0a4ae0366560e2a5c9 +size 249391 diff --git a/resources/healthsystem/consumables/ResourceFile_Consumables_availability_small.csv b/resources/healthsystem/consumables/ResourceFile_Consumables_availability_small.csv index 54453cbc2f..25249531b2 100644 --- a/resources/healthsystem/consumables/ResourceFile_Consumables_availability_small.csv +++ b/resources/healthsystem/consumables/ResourceFile_Consumables_availability_small.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69a5143c0b7307c7bb48726aa73d6c2f61de2a69aeb445eec87494cf9d4a1041 -size 6087331 +oid sha256:c358a643e4def0e574b75f89f83d77f9c3366f668422e005150f4d69ebe8d7a7 +size 6169152 diff --git a/resources/healthsystem/consumables/ResourceFile_consumables_matched.csv b/resources/healthsystem/consumables/ResourceFile_consumables_matched.csv index 7754d65118..7ab675ecba 100644 --- a/resources/healthsystem/consumables/ResourceFile_consumables_matched.csv +++ b/resources/healthsystem/consumables/ResourceFile_consumables_matched.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbfe91222d3a2a32ed44a4be711b30c5323276a71df802f6c9249eb4c21f8d43 -size 90158 +oid sha256:b5b0f417681cbdd2489e2f9c6634b2825c32beb9637dc045b56e308c910a102c +size 90569 diff --git a/src/scripts/data_file_processing/healthsystem/consumables/consumable_resource_analyses_with_lmis/consumables_availability_estimation.py b/src/scripts/data_file_processing/healthsystem/consumables/consumable_resource_analyses_with_lmis/consumables_availability_estimation.py index 2495ea6d66..3615afd400 100644 --- a/src/scripts/data_file_processing/healthsystem/consumables/consumable_resource_analyses_with_lmis/consumables_availability_estimation.py +++ b/src/scripts/data_file_processing/healthsystem/consumables/consumable_resource_analyses_with_lmis/consumables_availability_estimation.py @@ -277,14 +277,10 @@ def custom_agg(x): return _collapsed_df # Hold out the dataframe with no naming inconsistencies -list_of_items_with_inconsistent_names_zipped = list( - zip(inconsistent_item_names_mapping.keys(), inconsistent_item_names_mapping.values())) -list_of_items_with_inconsistent_names = [ - item for sublist in list_of_items_with_inconsistent_names_zipped for item in sublist] -df_with_consistent_item_names = lmis_df_wide_flat[~lmis_df_wide_flat[('item',)].isin( - list_of_items_with_inconsistent_names)] -df_without_consistent_item_names = lmis_df_wide_flat[lmis_df_wide_flat[('item',)].isin( - list_of_items_with_inconsistent_names)] +list_of_items_with_inconsistent_names_zipped = set(zip(inconsistent_item_names_mapping.keys(), inconsistent_item_names_mapping.values())) +list_of_items_with_inconsistent_names = [item for sublist in list_of_items_with_inconsistent_names_zipped for item in sublist] +df_with_consistent_item_names = lmis_df_wide_flat[~lmis_df_wide_flat[('item',)].isin(list_of_items_with_inconsistent_names)] +df_without_consistent_item_names = lmis_df_wide_flat[lmis_df_wide_flat[('item',)].isin(list_of_items_with_inconsistent_names)] # Make inconsistently named drugs uniform across the dataframe df_without_consistent_item_names_corrected = rename_items_to_address_inconsistentencies( df_without_consistent_item_names, inconsistent_item_names_mapping) diff --git a/src/scripts/data_file_processing/healthsystem/consumables/processing_data_from_one_health/generate_consumables_item_codes_and_packages.py b/src/scripts/data_file_processing/healthsystem/consumables/processing_data_from_one_health/generate_consumables_item_codes_and_packages.py index 3fcbccf9e2..7ca04f763f 100644 --- a/src/scripts/data_file_processing/healthsystem/consumables/processing_data_from_one_health/generate_consumables_item_codes_and_packages.py +++ b/src/scripts/data_file_processing/healthsystem/consumables/processing_data_from_one_health/generate_consumables_item_codes_and_packages.py @@ -21,7 +21,8 @@ # Set local Dropbox source path_to_dropbox = Path( # <-- point to the TLO dropbox locally - '/Users/tbh03/Dropbox (SPH Imperial College)/Thanzi la Onse Theme 1 SHARE') + # '/Users/tbh03/Dropbox (SPH Imperial College)/Thanzi la Onse Theme 1 SHARE' + '/Users/sm2511/Dropbox/Thanzi La Onse') resourcefilepath = Path("./resources") path_for_new_resourcefiles = resourcefilepath / "healthsystem/consumables" @@ -245,7 +246,7 @@ def add_record(df: pd.DataFrame, record: Dict): """Add a row to the bottom of the dataframe, where the row is specified by a dict keyed by the target columns.""" - assert list(df.columns) == list(record.keys()) + assert set(df.columns) == set(record.keys()) return pd.concat([df, pd.DataFrame.from_records([record])], ignore_index=True) @@ -328,6 +329,54 @@ def add_record(df: pd.DataFrame, record: Dict): }, ) +cons = add_record( + cons, + { + 'Intervention_Cat': "Added by SM (Recommended by TM)", + 'Intervention_Pkg': "Isoniazid preventative therapy for HIV+ no TB", + 'Intervention_Pkg_Code': 82, + 'Items': "Isoniazid/Rifapentine", + 'Item_Code': 2678, + 'Expected_Units_Per_Case': 1.0, + 'Unit_Cost': 1.0 + }, +) + +cons = add_record( + cons, + { + 'Intervention_Cat': "Added by SM (Recommended by EJ)", + 'Intervention_Pkg': "Misc", + 'Intervention_Pkg_Code': -99, + 'Items': "Cystoscope", + 'Item_Code': 285, + 'Expected_Units_Per_Case': 1.0, + 'Unit_Cost': np.nan}, +) + +cons = add_record( + cons,{ + 'Intervention_Cat': "Added by SM (Recommended by EJ)", + 'Intervention_Pkg': "Misc", + 'Intervention_Pkg_Code': -99, + 'Items': "Endoscope", + 'Item_Code': 280, + 'Expected_Units_Per_Case': 1.0, + 'Unit_Cost': np.nan}, +) + +cons = add_record( + cons,{ + 'Intervention_Cat': "Added by SM (Recommended by EJ)", + 'Intervention_Pkg': "Misc", + 'Intervention_Pkg_Code': -99, + 'Items': "Prostate specific antigen test", + 'Item_Code': 281, + 'Expected_Units_Per_Case': 1.0, + 'Unit_Cost': np.nan}, +) + + # -------------- # -------------- # -------------- diff --git a/src/tlo/methods/alri.py b/src/tlo/methods/alri.py index 277726e0ff..61e9ae848b 100644 --- a/src/tlo/methods/alri.py +++ b/src/tlo/methods/alri.py @@ -21,7 +21,6 @@ """ from __future__ import annotations -import types from collections import defaultdict from itertools import chain from pathlib import Path @@ -1014,139 +1013,85 @@ def look_up_consumables(self): get_item_code = self.sim.modules['HealthSystem'].get_item_code_from_item_name - def get_dosage_for_age_in_months(age_in_whole_months: float, doses_by_age_in_months: Dict[int, float]): - """Returns the dose corresponding to age, using the lookup provided in `doses`. The format of `doses` is: - {: }.""" - - for upper_age_bound_in_months, _dose in sorted(doses_by_age_in_months.items()): - if age_in_whole_months < upper_age_bound_in_months: - return _dose - return _dose - - # # # # # # Dosages by age # # # # # # + # # # # # # Dosages by weight # # # # # # + # Assuming average weight of 0-5 is 12kg (abstraction). Doses sourced for WHO Pocket book of hospital care for + # children: Second edition 2014 # Antibiotic therapy ------------------- - # Antibiotics for non-severe pneumonia - oral amoxicillin for 5 days + # Antibiotics for non-severe pneumonia - oral amoxicillin for 5 days (40mg/kg BD - ((12*40)*2)*5 =4800mg) self.consumables_used_in_hsi['Amoxicillin_tablet_or_suspension_5days'] = { - get_item_code(item='Amoxycillin 250mg_1000_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {2: 0, 12: 0.006, 36: 0.012, np.inf: 0.018} - ), - get_item_code(item='Amoxycillin 125mg/5ml suspension, PFR_0.025_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {2: 0, 12: 1, 36: 2, np.inf: 3} - ), - } + get_item_code(item='Amoxycillin 250mg_1000_CMST'): 4800, + get_item_code(item='Amoxycillin 125mg/5ml suspension, PFR_0.025_CMST'): 192} # 25mg/ml - 4800/25 - # Antibiotics for non-severe pneumonia - oral amoxicillin for 3 days + # Antibiotics for non-severe pneumonia - oral amoxicillin for 3 days (40mg/kg BD - ((12*40)*2)*3 =2880mg) self.consumables_used_in_hsi['Amoxicillin_tablet_or_suspension_3days'] = { - get_item_code(item='Amoxycillin 250mg_1000_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {2: 0, 12: 0.01, 36: 0.02, np.inf: 0.03} - ), - get_item_code(item='Amoxycillin 125mg/5ml suspension, PFR_0.025_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {2: 0, 12: 1, 36: 2, np.inf: 3} - ), - } + get_item_code(item='Amoxycillin 250mg_1000_CMST'): 2880, + get_item_code(item='Amoxycillin 125mg/5ml suspension, PFR_0.025_CMST'): 115} # 25mg/ml - 2880/25 - # Antibiotics for non-severe pneumonia - oral amoxicillin for 7 days for young infants only + # Antibiotics for non-severe pneumonia - oral amoxicillin for 7 days for young infants only (40mg/kg BD - + # ((12*40)*2)*7 =6720mg) self.consumables_used_in_hsi['Amoxicillin_tablet_or_suspension_7days'] = { - get_item_code(item='Amoxycillin 250mg_1000_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {1: 0.004, 2: 0.006, np.inf: 0.01} - ), - get_item_code(item='Amoxycillin 125mg/5ml suspension, PFR_0.025_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {1: 0.4, 2: 0.5, np.inf: 1} - ), - } + get_item_code(item='Amoxycillin 250mg_1000_CMST'): 6720, + get_item_code(item='Amoxycillin 125mg/5ml suspension, PFR_0.025_CMST'): 269} # 25mg/ml - 6720/25 # Antibiotic therapy for severe pneumonia - ampicillin package + # Amp. dose - 50mg/KG QDS 5 days = (50*12)*4)*5 = 12_000mg + # Gent. dose -7.5mg/kg per day 5 days = (7.5*12)*5 = 450mg self.consumables_used_in_hsi['1st_line_IV_antibiotics'] = { - get_item_code(item='Ampicillin injection 500mg, PFR_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {1: 3.73, 2: 5.6, 4: 8, 12: 16, 36: 24, np.inf: 40} - ), - get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {1: 0.7, 2: 1.4, 4: 2.81, 12: 4.69, 36: 7.03, np.inf: 9.37} - ), + get_item_code(item='Ampicillin injection 500mg, PFR_each_CMST'): 24, # 500mg vial -12_000/500 + get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): 6, # 80mg/2ml = 450/8 get_item_code(item='Cannula iv (winged with injection pot) 16_each_CMST'): 1, get_item_code(item='Syringe, Autodisable SoloShot IX '): 1 } # # Antibiotic therapy for severe pneumonia - benzylpenicillin package when ampicillin is not available + # Benpen dose - 50_000IU/KG QDS 5 days = (50_000*12)*4)*5 = 12_000_000IU = 8g (approx) + # Gent. dose -7.5mg/kg per day 5 days = (7.5*12)*5 = 450mg self.consumables_used_in_hsi['Benzylpenicillin_gentamicin_therapy_for_severe_pneumonia'] = { - get_item_code(item='Benzylpenicillin 3g (5MU), PFR_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {1: 2, 2: 5, 4: 8, 12: 15, 36: 24, np.inf: 34} - ), - get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {1: 0.7, 2: 1.4, 4: 2.81, 12: 4.69, 36: 7.03, np.inf: 9.37} - ), + get_item_code(item='Benzylpenicillin 3g (5MU), PFR_each_CMST'): 8, + get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): 6, # 80mg/2ml = 450/8 get_item_code(item='Cannula iv (winged with injection pot) 16_each_CMST'): 1, get_item_code(item='Syringe, Autodisable SoloShot IX '): 1 } # Second line of antibiotics for severe pneumonia, if Staph not suspected + # Ceft. dose = 80mg/kg per day 5 days = (80*12)*5 = 4800mg self.consumables_used_in_hsi['Ceftriaxone_therapy_for_severe_pneumonia'] = { - get_item_code(item='Ceftriaxone 1g, PFR_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {4: 1.5, 12: 3, 36: 5, np.inf: 7} - ), + get_item_code(item='Ceftriaxone 1g, PFR_each_CMST'): 1, # smallest unit is 1g get_item_code(item='Cannula iv (winged with injection pot) 16_each_CMST'): 1, get_item_code(item='Syringe, Autodisable SoloShot IX '): 1 } # Second line of antibiotics for severe pneumonia, if Staph is suspected + # Flucox. dose = 50mg/kg QDS 7 days = ((50*12)*4)*7 = 16_800mg + # Oral flucox dose. = same self.consumables_used_in_hsi['2nd_line_Antibiotic_therapy_for_severe_staph_pneumonia'] = { - get_item_code(item='Flucloxacillin 250mg, vial, PFR_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {2: 21, 4: 22.4, 12: 37.3, 36: 67.2, 60: 93.3, np.inf: 140} - ), - get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {4: 2.81, 12: 4.69, 36: 7.03, 60: 9.37, np.inf: 13.6} - ), + get_item_code(item='Flucloxacillin 250mg, vial, PFR_each_CMST'): 16_800, + get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): 6, # 80mg/2ml = 450/8 get_item_code(item='Cannula iv (winged with injection pot) 16_each_CMST'): 1, get_item_code(item='Syringe, Autodisable SoloShot IX '): 1, - get_item_code(item='Flucloxacillin 250mg_100_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {4: 0.42, 36: 0.84, 60: 1.68, np.inf: 1.68} - ), - } + get_item_code(item='Flucloxacillin 250mg_100_CMST'): 16_800} # First dose of antibiotic before referral ------------------- - + # single dose of 7.5mg gent and 50mg/g amp. given # Referral process in iCCM for severe pneumonia, and at health centres for HIV exposed/infected self.consumables_used_in_hsi['First_dose_oral_amoxicillin_for_referral'] = { - get_item_code(item='Amoxycillin 250mg_1000_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {12: 0.001, 36: 0.002, np.inf: 0.003} - ), + get_item_code(item='Amoxycillin 250mg_1000_CMST'): 250, } # Referral process at health centres for severe cases self.consumables_used_in_hsi['First_dose_IM_antibiotics_for_referral'] = { - get_item_code(item='Ampicillin injection 500mg, PFR_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {4: 0.4, 12: 0.8, 36: 1.4, np.inf: 2} - ), - get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {4: 0.56, 12: 0.94, 36: 1.41, np.inf: 1.87} - ), + get_item_code(item='Ampicillin injection 500mg, PFR_each_CMST'): 2, # 2 x 500mg vial + get_item_code(item='Gentamicin Sulphate 40mg/ml, 2ml_each_CMST'): 2, # assuming single dose at referral get_item_code(item='Cannula iv (winged with injection pot) 16_each_CMST'): 1, get_item_code(item='Syringe, Autodisable SoloShot IX '): 1 } # Oxygen, pulse oximetry and x-ray ------------------- - # Oxygen for hypoxaemia + # Oxygen for hypoxaemia - 5/l per min (Approx) for 3 days ((24*60)*5)*3 self.consumables_used_in_hsi['Oxygen_Therapy'] = { - get_item_code(item='Oxygen, 1000 liters, primarily with oxygen cylinders'): 1, + get_item_code(item='Oxygen, 1000 liters, primarily with oxygen cylinders'): 21_600, } # Pulse oximetry @@ -1162,10 +1107,7 @@ def get_dosage_for_age_in_months(age_in_whole_months: float, doses_by_age_in_mon # Optional consumables ------------------- # Paracetamol self.consumables_used_in_hsi['Paracetamol_tablet'] = { - get_item_code(item='Paracetamol, tablet, 100 mg'): - lambda _age: get_dosage_for_age_in_months(int(_age * 12.0), - {36: 12, np.inf: 18} - ), + get_item_code(item='Paracetamol, tablet, 100 mg'): 240, # 20mg/kg } # Maintenance of fluids via nasograstric tube @@ -1178,11 +1120,6 @@ def get_dosage_for_age_in_months(age_in_whole_months: float, doses_by_age_in_mon get_item_code(item='Salbutamol sulphate 1mg/ml, 5ml_each_CMST'): 2 } - # Bronchodilator - oral - self.consumables_used_in_hsi['Oral_Brochodilator'] = { - get_item_code(item='Salbutamol, syrup, 2 mg/5 ml'): 1, - get_item_code(item='Salbutamol, tablet, 4 mg'): 1 - } def end_episode(self, person_id): """End the episode infection for a person (i.e. reset all properties to show no current infection or @@ -2420,11 +2357,7 @@ def _get_cons(self, _arg: Union[str, Tuple[str]]) -> bool: def _get_cons_group(self, item_group_str: str) -> bool: """True if _all_ of a group of consumables (identified by a string) is available.""" if item_group_str is not None: - return self.get_consumables( - item_codes={ - k: v(self._age_exact_years) if isinstance(v, types.LambdaType) else v - for k, v in self.module.consumables_used_in_hsi[item_group_str].items() - }) + return self.get_consumables(self.module.consumables_used_in_hsi[item_group_str]) else: raise ValueError('String for the group of consumables not provided') diff --git a/src/tlo/methods/cancer_consumables.py b/src/tlo/methods/cancer_consumables.py index 16a6f94f65..2649626b2f 100644 --- a/src/tlo/methods/cancer_consumables.py +++ b/src/tlo/methods/cancer_consumables.py @@ -3,94 +3,89 @@ """ from typing import Dict -from tlo import Module - -def get_consumable_item_codes_cancers(cancer_module: Module) -> Dict[str, int]: +def get_consumable_item_codes_cancers(self) -> Dict[str, int]: """ Returns dict the relevant item_codes for the consumables across the five cancer modules. This is intended to prevent repetition within module code. """ - def get_list_of_items(item_list): - item_lookup_fn = cancer_module.sim.modules['HealthSystem'].get_item_code_from_item_name - return list(map(item_lookup_fn, item_list)) + get_item_code = self.sim.modules['HealthSystem'].get_item_code_from_item_name cons_dict = dict() # Add items that are needed for all cancer modules - # todo: @Eva - add syringes, dressing - cons_dict['screening_biopsy_core'] = get_list_of_items(['Biopsy needle']) + cons_dict['screening_biopsy_core'] = \ + {get_item_code("Biopsy needle"): 1} cons_dict['screening_biopsy_optional'] = \ - get_list_of_items(['Specimen container', - 'Lidocaine, injection, 1 % in 20 ml vial', - 'Gauze, absorbent 90cm x 40m_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) + {get_item_code("Specimen container"): 1, + get_item_code("Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml"): 1, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 30, + get_item_code("Disposables gloves, powder free, 100 pieces per box"): 1, + get_item_code("Syringe, needle + swab"): 1} cons_dict['treatment_surgery_core'] = \ - get_list_of_items(['Halothane (fluothane)_250ml_CMST', - 'Scalpel blade size 22 (individually wrapped)_100_CMST']) + {get_item_code("Halothane (fluothane)_250ml_CMST"): 100, + get_item_code("Scalpel blade size 22 (individually wrapped)_100_CMST"): 1} cons_dict['treatment_surgery_optional'] = \ - get_list_of_items(['Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Paracetamol, tablet, 500 mg', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Suture pack', - 'Gauze, absorbent 90cm x 40m_each_CMST', - 'Cannula iv (winged with injection pot) 18_each_CMST']) + {get_item_code("Sodium chloride, injectable solution, 0,9 %, 500 ml"): 2000, + get_item_code("Paracetamol, tablet, 500 mg"): 8000, + get_item_code("Pethidine, 50 mg/ml, 2 ml ampoule"): 6, + get_item_code("Suture pack"): 1, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 30, + get_item_code("Cannula iv (winged with injection pot) 18_each_CMST"): 1} cons_dict['palliation'] = \ - get_list_of_items(['morphine sulphate 10 mg/ml, 1 ml, injection (nt)_10_IDA', - 'Diazepam, injection, 5 mg/ml, in 2 ml ampoule', - # N.B. This is not an exhaustive list of drugs required for palliation - ]) - - cons_dict['iv_drug_cons'] = \ - get_list_of_items(['Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box' - ]) + {get_item_code("morphine sulphate 10 mg/ml, 1 ml, injection (nt)_10_IDA"): 1, + get_item_code("Diazepam, injection, 5 mg/ml, in 2 ml ampoule"): 3, + get_item_code("Syringe, needle + swab"): 4} + # N.B. This is not an exhaustive list of drugs required for palliation - # Add items that are specific to each cancer module - if 'BreastCancer' == cancer_module.name: + cons_dict['treatment_chemotherapy'] = \ + {get_item_code("Cyclophosphamide, 1 g"): 16800} - # TODO: chemotharpy protocols??: TAC(Taxotere, Adriamycin, and Cyclophosphamide), AC (anthracycline and - # cyclophosphamide) +/-Taxane, TC (Taxotere and cyclophosphamide), CMF (cyclophosphamide, methotrexate, - # and fluorouracil), FEC-75 (5-Fluorouracil, Epirubicin, Cyclophosphamide). HER 2 +: Add Trastuzumab - - # only chemotherapy i consumable list which is also in suggested protocol is cyclo - cons_dict['treatment_chemotherapy'] = get_list_of_items(['Cyclophosphamide, 1 g']) + cons_dict['iv_drug_cons'] = \ + {get_item_code("Cannula iv (winged with injection pot) 18_each_CMST"): 1, + get_item_code("Giving set iv administration + needle 15 drops/ml_each_CMST"): 1, + get_item_code("Disposables gloves, powder free, 100 pieces per box"): 1, + get_item_code("Gauze, swabs 8-ply 10cm x 10cm_100_CMST"): 84} - elif 'ProstateCancer' == cancer_module.name: + # Add items that are specific to a particular cancer module + if 'ProstateCancer' == self.name: - # TODO: Prostate specific antigen test is listed in ResourceFile_Consumables_availability_and_usage but not - # ResourceFile_Consumables_Items_and_Package - # cons_dict['screening_psa_test_core'] = get_list_of_items(['Prostate specific antigen test']) + cons_dict['screening_psa_test_core'] = \ + {get_item_code("Prostate specific antigen test"): 1} cons_dict['screening_psa_test_optional'] = \ - get_list_of_items(['Blood collecting tube, 5 ml', - 'Disposables gloves, powder free, 100 pieces per box']) + {get_item_code("Blood collecting tube, 5 ml"): 1, + get_item_code("Disposables gloves, powder free, 100 pieces per box"): 1, + get_item_code("Gauze, swabs 8-ply 10cm x 10cm_100_CMST"): 1} - elif 'BladderCancer' == cancer_module.name: + elif 'BladderCancer' == self.name: # Note: bladder cancer is not in the malawi STG 2023 therefore no details on chemotherapy - # TODO: cytoscope is listed in ResourceFile_Consumables_availability_and_usage but not - # ResourceFile_Consumables_Items_and_Packages - # cons_dict['screening_cystoscopy_core'] = get_list_of_items(['Cytoscope']) - - cons_dict['screening_cystoscope_optional'] = get_list_of_items(['Specimen container']) + cons_dict['screening_cystoscopy_core'] = \ + {get_item_code("Cystoscope"): 1} - elif 'OesophagealCancer' == cancer_module.name: + cons_dict['screening_cystoscope_optional'] = \ + {get_item_code("Specimen container"): 1, + get_item_code("Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml"): 1, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 30, + get_item_code("Disposables gloves, powder free, 100 pieces per box"): 1, + get_item_code("Syringe, needle + swab"): 1} - # TODO: endoscope is listed in ResourceFile_Consumables_availability_and_usage but not - # ResourceFile_Consumables_Items_and_Packages - # cons_dict['screening_endoscope_core'] = get_list_of_items(['Endoscope']) + elif 'OesophagealCancer' == self.name: - cons_dict['screening_endoscope_optional'] =\ - get_list_of_items(['Specimen container', - 'Gauze, absorbent 90cm x 40m_each_CMST']) + cons_dict['screening_endoscope_core'] = \ + {get_item_code("Endoscope"): 1} - cons_dict['treatment_chemotherapy'] = get_list_of_items(['Cisplatin 50mg Injection']) + cons_dict['screening_endoscope_optional'] = \ + {get_item_code("Specimen container"): 1, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 30, + get_item_code("Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml"): 1, + get_item_code("Disposables gloves, powder free, 100 pieces per box"): 1, + get_item_code("Syringe, needle + swab"): 1} return cons_dict diff --git a/src/tlo/methods/cardio_metabolic_disorders.py b/src/tlo/methods/cardio_metabolic_disorders.py index 921b2e0f71..1d5f47ecb9 100644 --- a/src/tlo/methods/cardio_metabolic_disorders.py +++ b/src/tlo/methods/cardio_metabolic_disorders.py @@ -1600,10 +1600,23 @@ def apply(self, person_id, squeeze_factor): return self.sim.modules['HealthSystem'].get_blank_appt_footprint() assert person[f'nc_{self.condition}_ever_diagnosed'], "The person is not diagnosed and so should not be " \ "receiving an HSI." + + # Monthly doses of medications as follows. Diabetes - 1000mg metformin daily (1000*30.5), + # hypertension - 25mg hydrochlorothiazide daily (25*30.5), CKD 1 dialysis bag (estimate), + # lower back pain - 2400mg aspirin daily (2400*30.5), CIHD - 75mg aspirin daily (75*30.5) + dose = {'diabetes': 30_500, + 'hypertension': 610, + 'chronic_kidney_disease': 1, + 'chronic_lower_back_pain': 73_200, + 'chronic_ischemic_hd': 2288, + 'ever_stroke': 2288, + 'ever_heart_attack': 2288} + # Check availability of medication for condition - if self.get_consumables( - item_codes=self.module.parameters[f'{self.condition}_hsi'].get('medication_item_code').astype(int) - ): + if self.get_consumables(item_codes= + {self.module.parameters[f'{self.condition}_hsi'].get( + 'medication_item_code').astype(int): dose[self.condition]}): + # If medication is available, flag as being on medication df.at[person_id, f'nc_{self.condition}_on_medication'] = True # Determine if the medication will work to prevent death @@ -1669,10 +1682,21 @@ def apply(self, person_id, squeeze_factor): # Return the blank_appt_footprint() so that this HSI does not occupy any time resources return self.sim.modules['HealthSystem'].get_blank_appt_footprint() + # Monthly doses of medications as follows. Diabetes - 1000mg metformin daily (1000*30.5), + # hypertension - 25mg hydrochlorothiazide daily (25*30.5), CKD 1 dialysis bag (estimate), + # lower back pain - 2400mg aspirin daily (2400*30.5), CIHD - 75mg aspirin daily (75*30.5) + dose = {'diabetes': 30_500, + 'hypertension': 610, + 'chronic_kidney_disease': 1, + 'chronic_lower_back_pain': 73_200, + 'chronic_ischemic_hd': 2288, + 'ever_stroke': 2288, + 'ever_heart_attack': 2288} + # Check availability of medication for condition if self.get_consumables( - item_codes=self.module.parameters[f'{self.condition}_hsi'].get('medication_item_code').astype(int) - ): + item_codes={self.module.parameters[f'{self.condition}_hsi'].get('medication_item_code').astype(int) + : dose[self.condition]}): # Schedule their next HSI for a refill of medication, one month from now self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=self, @@ -1734,10 +1758,13 @@ def do_for_each_event_to_be_investigated(self, _ev): df.at[person_id, f'nc_{_ev}_date_diagnosis'] = self.sim.date df.at[person_id, f'nc_{_ev}_ever_diagnosed'] = True if self.module.parameters['prob_care_provided_given_seek_emergency_care'] > self.module.rng.random_sample(): + # If care is provided.... + dose = 20 if _ev == 'ever_stroke' else 40 + if self.get_consumables( - item_codes=self.module.parameters[f'{_ev}_hsi'].get( - 'emergency_medication_item_code').astype(int) + item_codes={self.module.parameters[f'{_ev}_hsi'].get( + 'emergency_medication_item_code').astype(int): dose} ): logger.debug(key='debug', data='Treatment will be provided.') df.at[person_id, f'nc_{_ev}_on_medication'] = True diff --git a/src/tlo/methods/care_of_women_during_pregnancy.py b/src/tlo/methods/care_of_women_during_pregnancy.py index bc22b86993..4025941254 100644 --- a/src/tlo/methods/care_of_women_during_pregnancy.py +++ b/src/tlo/methods/care_of_women_during_pregnancy.py @@ -1,5 +1,6 @@ from pathlib import Path +import numpy as np import pandas as pd from tlo import DateOffset, Module, Parameter, Property, Types, logging @@ -202,157 +203,160 @@ def get_and_store_pregnancy_item_codes(self): This function defines the required consumables for each intervention delivered during this module and stores them in a module level dictionary called within HSIs """ - get_list_of_items = pregnancy_helper_functions.get_list_of_items + ic = self.sim.modules['HealthSystem'].get_item_code_from_item_name + # First we store the item codes for the consumables for which their quantity varies for individuals based on + # length of pregnancy # ---------------------------------- BLOOD TEST EQUIPMENT --------------------------------------------------- self.item_codes_preg_consumables['blood_test_equipment'] = \ - get_list_of_items(self, ['Blood collecting tube, 5 ml', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) - + {ic('Blood collecting tube, 5 ml'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } # ---------------------------------- IV DRUG ADMIN EQUIPMENT ------------------------------------------------- self.item_codes_preg_consumables['iv_drug_equipment'] = \ - get_list_of_items(self, ['Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) + {ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } # -------------------------------------------- ECTOPIC PREGNANCY --------------------------------------------- self.item_codes_preg_consumables['ectopic_pregnancy_core'] = \ - get_list_of_items(self, ['Halothane (fluothane)_250ml_CMST']) + {ic('Halothane (fluothane)_250ml_CMST'): 100} self.item_codes_preg_consumables['ectopic_pregnancy_optional'] = \ - get_list_of_items(self, ['Scalpel blade size 22 (individually wrapped)_100_CMST', - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Paracetamol, tablet, 500 mg', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Suture pack', - 'Gauze, absorbent 90cm x 40m_each_CMST', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box' - ]) + {ic('Scalpel blade size 22 (individually wrapped)_100_CMST'): 1, + ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Pethidine, 50 mg/ml, 2 ml ampoule'): 6, + ic('Suture pack'): 1, + ic('Gauze, absorbent 90cm x 40m_each_CMST'): 30, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + } # ------------------------------------------- POST ABORTION CARE - GENERAL ----------------------------------- self.item_codes_preg_consumables['post_abortion_care_core'] = \ - get_list_of_items(self, ['Misoprostol, tablet, 200 mcg']) + {ic('Misoprostol, tablet, 200 mcg'): 600} self.item_codes_preg_consumables['post_abortion_care_optional'] = \ - get_list_of_items(self, ['Complete blood count', - 'Blood collecting tube, 5 ml', - 'Paracetamol, tablet, 500 mg', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box' - ]) + {ic('Complete blood count'): 1, + ic('Blood collecting tube, 5 ml'): 1, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Gauze, absorbent 90cm x 40m_each_CMST'): 30, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + } # ------------------------------------------- POST ABORTION CARE - SEPSIS ------------------------------------- self.item_codes_preg_consumables['post_abortion_care_sepsis_core'] = \ - get_list_of_items(self, ['Benzylpenicillin 3g (5MU), PFR_each_CMST', - 'Gentamycin, injection, 40 mg/ml in 2 ml vial']) + {ic('Benzathine benzylpenicillin, powder for injection, 2.4 million IU'): 8, + ic('Gentamycin, injection, 40 mg/ml in 2 ml vial'): 6, + } self.item_codes_preg_consumables['post_abortion_care_sepsis_optional'] = \ - get_list_of_items(self, ['Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Oxygen, 1000 liters, primarily with oxygen cylinders']) - - # ------------------------------------------- POST ABORTION CARE - SHOCK ------------------------------------- + {ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 23_040, + } + + # ------------------------------------------- POST ABORTION CARE - SHOCK ------------------------------------ self.item_codes_preg_consumables['post_abortion_care_shock'] = \ - get_list_of_items(self, ['Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Oxygen, 1000 liters, primarily with oxygen cylinders']) + {ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 23_040, + } self.item_codes_preg_consumables['post_abortion_care_shock_optional'] = \ - get_list_of_items(self, ['Cannula iv (winged with injection pot) 18_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Giving set iv administration + needle 15 drops/ml_each_CMST']) - + {ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + } # ---------------------------------- URINE DIPSTICK ---------------------------------------------------------- - self.item_codes_preg_consumables['urine_dipstick'] = get_list_of_items(self, ['Urine analysis']) + self.item_codes_preg_consumables['urine_dipstick'] = {ic('Urine analysis'): 1} # ---------------------------------- IRON AND FOLIC ACID ------------------------------------------------------ - self.item_codes_preg_consumables['iron_folic_acid'] = get_list_of_items( - self, ['Ferrous Salt + Folic Acid, tablet, 200 + 0.25 mg']) + # Dose changes at run time + self.item_codes_preg_consumables['iron_folic_acid'] = \ + {ic('Ferrous Salt + Folic Acid, tablet, 200 + 0.25 mg'): 1} # TODO: update con requested here # --------------------------------- BALANCED ENERGY AND PROTEIN ---------------------------------------------- - self.item_codes_preg_consumables['balanced_energy_protein'] = get_list_of_items( - self, ['Dietary supplements (country-specific)']) + # Dose changes at run time + self.item_codes_preg_consumables['balanced_energy_protein'] = \ + {ic('Dietary supplements (country-specific)'): 1} # --------------------------------- INSECTICIDE TREATED NETS ------------------------------------------------ - self.item_codes_preg_consumables['itn'] = get_list_of_items(self, ['Insecticide-treated net']) + self.item_codes_preg_consumables['itn'] = {ic('Insecticide-treated net'): 1} # --------------------------------- CALCIUM SUPPLEMENTS ----------------------------------------------------- - self.item_codes_preg_consumables['calcium'] = get_list_of_items(self, ['Calcium, tablet, 600 mg']) + self.item_codes_preg_consumables['calcium'] = {ic('Calcium, tablet, 600 mg'): 1} # -------------------------------- HAEMOGLOBIN TESTING ------------------------------------------------------- - self.item_codes_preg_consumables['hb_test'] = get_list_of_items(self, ['Haemoglobin test (HB)']) + self.item_codes_preg_consumables['hb_test'] = {ic('Haemoglobin test (HB)'): 1} # ------------------------------------------- ALBENDAZOLE ----------------------------------------------------- - self.item_codes_preg_consumables['albendazole'] = get_list_of_items(self, ['Albendazole 200mg_1000_CMST']) + self.item_codes_preg_consumables['albendazole'] = {ic('Albendazole 200mg_1000_CMST'): 400} # ------------------------------------------- HEP B TESTING --------------------------------------------------- - self.item_codes_preg_consumables['hep_b_test'] = get_list_of_items( - self, ['Hepatitis B test kit-Dertemine_100 tests_CMST']) + self.item_codes_preg_consumables['hep_b_test'] = {ic('Hepatitis B test kit-Dertemine_100 tests_CMST'): 1} # ------------------------------------------- SYPHILIS TESTING ------------------------------------------------ - self.item_codes_preg_consumables['syphilis_test'] = get_list_of_items( - self, ['Test, Rapid plasma reagin (RPR)']) + self.item_codes_preg_consumables['syphilis_test'] = {ic('Test, Rapid plasma reagin (RPR)'): 1} # ------------------------------------------- SYPHILIS TREATMENT ---------------------------------------------- - self.item_codes_preg_consumables['syphilis_treatment'] = get_list_of_items( - self, ['Benzathine benzylpenicillin, powder for injection, 2.4 million IU']) - - # ----------------------------------------------- IPTP -------------------------------------------------------- - self.item_codes_preg_consumables['iptp'] = get_list_of_items( - self, ['Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg']) + self.item_codes_preg_consumables['syphilis_treatment'] =\ + {ic('Benzathine benzylpenicillin, powder for injection, 2.4 million IU'): 1} # ----------------------------------------------- GDM TEST ---------------------------------------------------- - self.item_codes_preg_consumables['gdm_test'] = get_list_of_items(self, ['Blood glucose level test']) + self.item_codes_preg_consumables['gdm_test'] = {ic('Blood glucose level test'): 1} # ------------------------------------------ FULL BLOOD COUNT ------------------------------------------------- - self.item_codes_preg_consumables['full_blood_count'] = get_list_of_items(self, ['Complete blood count']) + self.item_codes_preg_consumables['full_blood_count'] = {ic('Complete blood count'): 1} # ---------------------------------------- BLOOD TRANSFUSION ------------------------------------------------- - self.item_codes_preg_consumables['blood_transfusion'] = get_list_of_items(self, ['Blood, one unit']) + self.item_codes_preg_consumables['blood_transfusion'] = {ic('Blood, one unit'): 2} # --------------------------------------- ORAL ANTIHYPERTENSIVES --------------------------------------------- - self.item_codes_preg_consumables['oral_antihypertensives'] = get_list_of_items( - self, ['Methyldopa 250mg_1000_CMST']) + # Dose changes at run time + self.item_codes_preg_consumables['oral_antihypertensives'] = {ic('Methyldopa 250mg_1000_CMST'): 1} # ------------------------------------- INTRAVENOUS ANTIHYPERTENSIVES --------------------------------------- - self.item_codes_preg_consumables['iv_antihypertensives'] = get_list_of_items( - self, ['Hydralazine, powder for injection, 20 mg ampoule']) + self.item_codes_preg_consumables['iv_antihypertensives'] = \ + {ic('Hydralazine, powder for injection, 20 mg ampoule'): 1} # ---------------------------------------- MAGNESIUM SULPHATE ------------------------------------------------ - self.item_codes_preg_consumables['magnesium_sulfate'] = get_list_of_items( - self, ['Magnesium sulfate, injection, 500 mg/ml in 10-ml ampoule']) + self.item_codes_preg_consumables['magnesium_sulfate'] = \ + {ic('Magnesium sulfate, injection, 500 mg/ml in 10-ml ampoule'): 2} # ---------------------------------------- MANAGEMENT OF ECLAMPSIA -------------------------------------------- - self.item_codes_preg_consumables['eclampsia_management_optional'] = get_list_of_items( - self, ['Misoprostol, tablet, 200 mcg', - 'Oxytocin, injection, 10 IU in 1 ml ampoule', - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Oxygen, 1000 liters, primarily with oxygen cylinders', - 'Complete blood count', - 'Blood collecting tube, 5 ml', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml']) + self.item_codes_preg_consumables['eclampsia_management_optional'] = \ + {ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 23_040, + ic('Complete blood count'): 1, + ic('Blood collecting tube, 5 ml'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + } # -------------------------------------- ANTIBIOTICS FOR PROM ------------------------------------------------ - self.item_codes_preg_consumables['abx_for_prom'] = get_list_of_items( - self, ['Benzathine benzylpenicillin, powder for injection, 2.4 million IU']) + self.item_codes_preg_consumables['abx_for_prom'] = \ + {ic('Benzathine benzylpenicillin, powder for injection, 2.4 million IU'): 8} # ----------------------------------- ORAL DIABETIC MANAGEMENT ----------------------------------------------- - self.item_codes_preg_consumables['oral_diabetic_treatment'] = get_list_of_items( - self, ['Glibenclamide 5mg_1000_CMST']) + # Dose changes at run time + self.item_codes_preg_consumables['oral_diabetic_treatment'] = \ + {ic('Glibenclamide 5mg_1000_CMST'): 1} # ---------------------------------------- INSULIN ---------------------------------------------------------- - self.item_codes_preg_consumables['insulin_treatment'] = get_list_of_items( - self, ['Insulin soluble 100 IU/ml, 10ml_each_CMST']) + # Dose changes at run time + self.item_codes_preg_consumables['insulin_treatment'] = \ + {ic('Insulin soluble 100 IU/ml, 10ml_each_CMST'): 1} def initialise_simulation(self, sim): @@ -730,7 +734,7 @@ def screening_interventions_delivered_at_every_contact(self, hsi_event): # check consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='urine_dipstick') + self, hsi_event, cons=self.item_codes_preg_consumables['urine_dipstick'], opt_cons=None) # If the intervention will be delivered the dx_manager runs, returning True if the consumables are # available and the test detects protein in the urine @@ -788,8 +792,10 @@ def iron_and_folic_acid_supplementation(self, hsi_event): # check consumable availability - dose is total days of pregnancy x 2 tablets days = self.get_approx_days_of_pregnancy(person_id) + updated_cons = {k: v*(days*2) for (k, v) in self.item_codes_preg_consumables['iron_folic_acid'].items()} + avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='iron_folic_acid', number=days*3) + self, hsi_event, cons=updated_cons, opt_cons=None) if avail: logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'iron_folic_acid'}) @@ -822,8 +828,11 @@ def balance_energy_and_protein_supplementation(self, hsi_event): # If the consumables are available... days = self.get_approx_days_of_pregnancy(person_id) + updated_cons = {k: v*days for (k, v) in + self.item_codes_preg_consumables['balanced_energy_protein'].items()} + avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='balanced_energy_protein', number=days) + self, hsi_event, cons=updated_cons, opt_cons=None) # And she is deemed to be at risk (i.e. BMI < 18) she is started on supplements if avail and (df.at[person_id, 'li_bmi'] == 1): @@ -885,8 +894,11 @@ def calcium_supplementation(self, hsi_event): or (df.at[person_id, 'la_parity'] > 4)): days = self.get_approx_days_of_pregnancy(person_id) * 3 + updated_cons = {k: v * days for (k, v) in + self.item_codes_preg_consumables['calcium'].items()} + avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='calcium', number=days) + self, hsi_event, cons=updated_cons, opt_cons=None) if avail: df.at[person_id, 'ac_receiving_calcium_supplements'] = True @@ -909,7 +921,9 @@ def point_of_care_hb_testing(self, hsi_event): # Run check against probability of testing being delivered avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='hb_test', optional='blood_test_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['hb_test'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) # We run the test through the dx_manager and if a woman has anaemia and its detected she will be admitted # for further care @@ -983,8 +997,9 @@ def syphilis_screening_and_treatment(self, hsi_event): logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'syphilis_test'}) avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='syphilis_test', - optional='blood_test_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['syphilis_test'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) test = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( dx_tests_to_run='blood_test_syphilis', hsi_event=hsi_event) @@ -993,8 +1008,9 @@ def syphilis_screening_and_treatment(self, hsi_event): if avail and test: avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='syphilis_treatment', - optional='blood_test_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['syphilis_treatment'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) if avail: # We assume that treatment is 100% effective at curing infection @@ -1059,7 +1075,9 @@ def gdm_screening(self, hsi_event): if self.rng.random_sample() < params['prob_intervention_delivered_gdm_test']: avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='gdm_test', optional='blood_test_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['gdm_test'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) # If the test accurately detects a woman has gestational diabetes the consumables are recorded and # she is referred for treatment @@ -1225,8 +1243,9 @@ def antenatal_blood_transfusion(self, individual_id, hsi_event): # Check for consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='blood_transfusion', number=2, - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['blood_transfusion'], + opt_cons=self.item_codes_preg_consumables['iv_drug_equipment']) sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.sim.modules['Labour'], sf='blood_tran', @@ -1253,9 +1272,12 @@ def initiate_maintenance_anti_hypertensive_treatment(self, individual_id, hsi_ev df = self.sim.population.props # Calculate the approximate dose for the remainder of pregnancy and check availability + days = self.get_approx_days_of_pregnancy(individual_id) * 4 + updated_cons = {k: v * days for (k, v) in + self.item_codes_preg_consumables['oral_antihypertensives'].items()} + avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='oral_antihypertensives', - number=(self.get_approx_days_of_pregnancy(individual_id) * 4)) + self, hsi_event, cons=updated_cons, opt_cons=None) # If the consumables are available then the woman is started on treatment if avail: @@ -1274,8 +1296,9 @@ def initiate_treatment_for_severe_hypertension(self, individual_id, hsi_event): # Define the consumables and check their availability avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='iv_antihypertensives', - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['iv_antihypertensives'], + opt_cons=self.item_codes_preg_consumables['iv_drug_equipment']) # If they are available then the woman is started on treatment if avail: @@ -1305,8 +1328,9 @@ def treatment_for_severe_pre_eclampsia_or_eclampsia(self, individual_id, hsi_eve df = self.sim.population.props avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='magnesium_sulfate', - optional='eclampsia_management_optional') + self, hsi_event, + cons=self.item_codes_preg_consumables['magnesium_sulfate'], + opt_cons=self.item_codes_preg_consumables['eclampsia_management_optional']) # check HCW will deliver intervention sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.sim.modules['Labour'], @@ -1329,8 +1353,9 @@ def antibiotics_for_prom(self, individual_id, hsi_event): # check consumables and whether HCW are available to deliver the intervention avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_preg_consumables, core='abx_for_prom', - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_preg_consumables['abx_for_prom'], + opt_cons=self.item_codes_preg_consumables['iv_drug_equipment']) sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.sim.modules['Labour'], sf='iv_abx', @@ -2426,6 +2451,8 @@ def apply(self, person_id, squeeze_factor): if not mother.la_currently_in_labour and not mother.hs_is_inpatient and mother.ps_gest_diab != 'none' \ and (mother.ac_gest_diab_on_treatment != 'none') and (mother.ps_gestational_age_in_weeks > 21): + est_length_preg = self.module.get_approx_days_of_pregnancy(person_id) + def schedule_gdm_event_and_checkup(): # Schedule GestationalDiabetesGlycaemicControlEvent which determines if this new treatment will # effectively control blood glucose prior to next follow up @@ -2450,9 +2477,12 @@ def schedule_gdm_event_and_checkup(): # meds if mother.ac_gest_diab_on_treatment == 'diet_exercise': + days = est_length_preg * 10 + updated_cons = {k: v * days for (k, v) in + self.module.item_codes_preg_consumables['oral_diabetic_treatment'].items()} + avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='oral_diabetic_treatment', - number=(self.module.get_approx_days_of_pregnancy(person_id) * 2)) + self.module, self, cons=updated_cons, opt_cons=None) # If the meds are available women are started on that treatment if avail: @@ -2468,9 +2498,15 @@ def schedule_gdm_event_and_checkup(): # blood sugar- they are started on insulin if mother.ac_gest_diab_on_treatment == 'orals': + # Dose is (avg.) 0.8 units per KG per day. Average weight is an appoximation + required_units_per_preg = 65 * (0.8 * est_length_preg) + required_vials = np.ceil(required_units_per_preg/1000) + + updated_cons = {k: v * required_vials for (k, v) in + self.module.item_codes_preg_consumables['insulin_treatment'].items()} + avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='insulin_treatment', - number=5) + self.module, self, cons=updated_cons, opt_cons=None) if avail: df.at[person_id, 'ac_gest_diab_on_treatment'] = 'insulin' @@ -2514,8 +2550,9 @@ def apply(self, person_id, squeeze_factor): # Request baseline PAC consumables baseline_cons = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='post_abortion_care_core', - optional='post_abortion_care_optional') + self.module, self, + cons=self.module.item_codes_preg_consumables['post_abortion_care_core'], + opt_cons=self.module.item_codes_preg_consumables['post_abortion_care_optional']) # Check HCW availability to deliver surgical removal of retained products sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.sim.modules['Labour'], @@ -2528,29 +2565,32 @@ def apply(self, person_id, squeeze_factor): if abortion_complications.has_any([person_id], 'sepsis', first=True): cons_for_sepsis_pac = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='post_abortion_care_sepsis_core', - optional='post_abortion_care_sepsis_optional') + self.module, self, + cons=self.module.item_codes_preg_consumables['post_abortion_care_sepsis_core'], + opt_cons=self.module.item_codes_preg_consumables['post_abortion_care_sepsis_optional']) if cons_for_sepsis_pac and (baseline_cons or sf_check): df.at[person_id, 'ac_received_post_abortion_care'] = True elif abortion_complications.has_any([person_id], 'haemorrhage', first=True): - cons_for_haemorrhage = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='blood_transfusion', number=2, - optional='iv_drug_equipment') + self.module, self, + cons=self.module.item_codes_preg_consumables['blood_transfusion'], + opt_cons=self.module.item_codes_preg_consumables['iv_drug_equipment']) cons_for_shock = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='post_abortion_care_shock', - optional='post_abortion_care_shock_optional') + self.module, self, + cons=self.module.item_codes_preg_consumables['post_abortion_care_shock'], + opt_cons=self.module.item_codes_preg_consumables['post_abortion_care_shock_optional']) if cons_for_haemorrhage and cons_for_shock and (baseline_cons or sf_check): df.at[person_id, 'ac_received_post_abortion_care'] = True elif abortion_complications.has_any([person_id], 'injury', first=True): cons_for_shock = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='post_abortion_care_shock', - optional='post_abortion_care_shock_optional') + self.module, self, + cons=self.module.item_codes_preg_consumables['post_abortion_care_shock'], + opt_cons=self.module.item_codes_preg_consumables['post_abortion_care_shock_optional']) if cons_for_shock and (baseline_cons or sf_check): df.at[person_id, 'ac_received_post_abortion_care'] = True @@ -2595,8 +2635,9 @@ def apply(self, person_id, squeeze_factor): # We define the required consumables and check their availability avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_preg_consumables, core='ectopic_pregnancy_core', - optional='ectopic_pregnancy_optional') + self.module, self, + cons=self.module.item_codes_preg_consumables['ectopic_pregnancy_core'], + opt_cons=self.module.item_codes_preg_consumables['ectopic_pregnancy_optional']) # If they are available then treatment can go ahead if avail: diff --git a/src/tlo/methods/contraception.py b/src/tlo/methods/contraception.py index 67d6684fce..580125f7ba 100644 --- a/src/tlo/methods/contraception.py +++ b/src/tlo/methods/contraception.py @@ -646,23 +646,60 @@ def select_contraceptive_following_birth(self, mother_id, mother_age): def get_item_code_for_each_contraceptive(self): """Get the item_code for each contraceptive and for contraceptive initiation.""" - # TODO: update with optional items (currently all considered essential) - get_items_from_pkg = self.sim.modules['HealthSystem'].get_item_codes_from_package_name + # ### Get item codes from item names and define number of units per case here + get_item_code = self.sim.modules['HealthSystem'].get_item_code_from_item_name _cons_codes = dict() - # items for each method that requires an HSI to switch to - _cons_codes['pill'] = get_items_from_pkg('Pill') - _cons_codes['male_condom'] = get_items_from_pkg('Male condom') - _cons_codes['other_modern'] = get_items_from_pkg('Female Condom') - # NB. The consumable female condom is used for the contraceptive state of "other_modern method" - _cons_codes['IUD'] = get_items_from_pkg('IUD') - _cons_codes['injections'] = get_items_from_pkg('Injectable') - _cons_codes['implant'] = get_items_from_pkg('Implant') - _cons_codes['female_sterilization'] = get_items_from_pkg('Female sterilization') + # # items for each method that requires an HSI to switch to + # in 80% cases combined pills administrated + # in other 20% cases same amount of progesterone-only pills ("Levonorgestrel 0.0375 mg, cycle") administrated + # (omitted in here) + _cons_codes['pill'] = \ + {get_item_code("Levonorgestrel 0.15 mg + Ethinyl estradiol 30 mcg (Microgynon), cycle"): 21 * 3.75} + _cons_codes['male_condom'] =\ + {get_item_code("Condom, male"): 30} + _cons_codes['other_modern'] =\ + {get_item_code("Female Condom_Each_CMST"): 30} + _cons_codes['IUD'] =\ + {get_item_code("Glove disposable powdered latex medium_100_CMST"): 2, + get_item_code("IUD, Copper T-380A"): 1} + _cons_codes['injections'] = \ + {get_item_code("Depot-Medroxyprogesterone Acetate 150 mg - 3 monthly"): 1, + get_item_code("Glove disposable powdered latex medium_100_CMST"): 1, + get_item_code("Water for injection, 10ml_Each_CMST"): 1, + get_item_code("Povidone iodine, solution, 10 %, 5 ml per injection"): 5, + get_item_code("Gauze, swabs 8-ply 10cm x 10cm_100_CMST"): 1} + _cons_codes['implant'] =\ + {get_item_code("Glove disposable powdered latex medium_100_CMST"): 3, + get_item_code("Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml"): 2, + get_item_code("Povidone iodine, solution, 10 %, 5 ml per injection"): 1*5, # unit: 1 ml + get_item_code("Syringe, needle + swab"): 2, + get_item_code("Trocar"): 1, + get_item_code("Needle suture intestinal round bodied ½ circle trocar_6_CMST"): 1, + # in 50% cases Jadelle administrated + # in other 50% cases other type of implant ("Implanon (Etonogestrel 68 mg)") administrated + # (omitted in here) + get_item_code("Jadelle (implant), box of 2_CMST"): 1, + get_item_code("Gauze, swabs 8-ply 10cm x 10cm_100_CMST"): 1} + _cons_codes['female_sterilization'] =\ + {get_item_code("Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml"): 1, + get_item_code("Atropine sulphate 600 micrograms/ml, 1ml_each_CMST"): 0.5, # 1 unit used only in 50% cases + # approximated by 0.5 unit each time + get_item_code("Diazepam, injection, 5 mg/ml, in 2 ml ampoule"): 1, + get_item_code("Syringe, autodestruct, 5ml, disposable, hypoluer with 21g needle_each_CMST"): 3, + get_item_code("Gauze, swabs 8-ply 10cm x 10cm_100_CMST"): 2, + get_item_code("Needle, suture, assorted sizes, round body"): 3, + get_item_code("Suture, catgut, chromic, 0, 150 cm"): 3, + get_item_code("Tape, adhesive, 2.5 cm wide, zinc oxide, 5 m roll"): 125, # unit: 1 cm long (2.5 cm wide) + get_item_code("Glove surgeon's size 7 sterile_2_CMST"): 2, + get_item_code("Paracetamol, tablet, 500 mg"): 8*500, # unit: 1 mg + get_item_code("Povidone iodine, solution, 10 %, 5 ml per injection"): 2*5, # unit: 1 ml + get_item_code("Cotton wool, 500g_1_CMST"): 100} # unit: 1 g + assert set(_cons_codes.keys()) == set(self.states_that_may_require_HSI_to_switch_to) # items used when initiating a modern reliable method after not using or switching from non-reliable method - _cons_codes['co_initiation'] = get_items_from_pkg('Contraception initiation') + _cons_codes['co_initiation'] = {get_item_code('Pregnancy slide test kit_100_CMST'): 1} return _cons_codes @@ -1145,7 +1182,6 @@ def apply(self, person_id, squeeze_factor): self.sim.population.props.at[person_id, "co_date_of_last_fp_appt"] = self.sim.date # Determine essential and optional items - # TODO: we don't distinguish essential X optional for contraception methods yet, will need to update once we do items_essential = self.module.cons_codes[self.new_contraceptive] items_optional = {} # Record use of consumables and default the person to "not_using" if the consumable is not available. diff --git a/src/tlo/methods/copd.py b/src/tlo/methods/copd.py index 7e85e57ee2..a39e263280 100644 --- a/src/tlo/methods/copd.py +++ b/src/tlo/methods/copd.py @@ -33,7 +33,7 @@ class Copd(Module): """The module responsible for determining Chronic Obstructive Pulmonary Diseases (COPD) status and outcomes. and initialises parameters and properties associated with COPD plus functions and events related to COPD.""" - INIT_DEPENDENCIES = {'SymptomManager', 'Lifestyle'} + INIT_DEPENDENCIES = {'SymptomManager', 'Lifestyle', 'HealthSystem'} ADDITIONAL_DEPENDENCIES = set() METADATA = { @@ -190,14 +190,14 @@ def define_symptoms(self): def lookup_item_codes(self): """Look-up the item-codes for the consumables needed in the HSI Events for this module.""" - # todo: Need to look-up these item-codes. + ic = self.sim.modules['HealthSystem'].get_item_code_from_item_name + self.item_codes = { - 'bronchodilater_inhaler': 293, - 'steroid_inhaler': 294, - 'oxygen': 127, - 'aminophylline': 292, - 'amoxycillin': 125, - 'prednisolone': 291 + 'bronchodilater_inhaler': ic('Salbutamol Inhaler 100mcg/dose - 200 doses '), + 'oxygen': ic('Oxygen, 1000 liters, primarily with oxygen cylinders'), + 'aminophylline': ic('Aminophylline 100mg, tablets'), + 'amoxycillin': ic('Amoxycillin 250mg_1000_CMST'), + 'prednisolone': ic('Prednisolone 5mg_100_CMST'), } def do_logging(self): @@ -225,10 +225,9 @@ def _common_first_appt( if ('breathless_moderate' in symptoms) or ('breathless_severe' in symptoms): patient_details_updates = {} # Give inhaler if patient does not already have one - if not patient_details.ch_has_inhaler and consumables_checker( - self.item_codes["bronchodilater_inhaler"] - ): - patient_details_updates["ch_has_inhaler"] = True + if not patient_details.ch_has_inhaler: + if consumables_checker({self.item_codes["bronchodilater_inhaler"]: 1}): + patient_details_updates["ch_has_inhaler"] = True if "breathless_severe" in symptoms: event = HSI_Copd_TreatmentOnSevereExacerbation( @@ -571,7 +570,8 @@ def apply(self, person_id, squeeze_factor): * Provide treatment: whatever is available at this facility at this time (no referral). """ df = self.sim.population.props - if not self.get_consumables(self.module.item_codes['oxygen']): + # Assume average 8L O2 for 2 days inpatient care + if not self.get_consumables({self.module.item_codes['oxygen']: 23_040}): # refer to the next higher facility if the current facility has no oxygen self.facility_levels_index += 1 if self.facility_levels_index >= len(self.all_facility_levels): @@ -581,10 +581,11 @@ def apply(self, person_id, squeeze_factor): else: # Give oxygen and AminoPhylline, if possible, ... and cancel death if the treatment is successful. + # Aminophylline dose = 100mg 8hrly, assuming 600mg in 48 hours prob_treatment_success = self.module.models.prob_livesaved_given_treatment( df=df.iloc[[person_id]], - oxygen=self.get_consumables(self.module.item_codes['oxygen']), - aminophylline=self.get_consumables(self.module.item_codes['aminophylline']) + oxygen=self.get_consumables({self.module.item_codes['oxygen']: 23_040}), + aminophylline=self.get_consumables({self.module.item_codes['aminophylline']: 600}) ) if prob_treatment_success: diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index c7f2577382..ef1e4f8cc7 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -953,9 +953,10 @@ def apply(self, person_id, squeeze_factor): "receiving an HSI. " # Check availability of antidepressant medication - item_code = self.module.parameters['anti_depressant_medication_item_code'] + # Dose is 25mg daily, patient provided with month supply - 25mg x 30.437 (days) = 761mg per month + item_code_with_dose = {self.module.parameters['anti_depressant_medication_item_code']: 761} - if self.get_consumables(item_codes=item_code): + if self.get_consumables(item_codes=item_code_with_dose): # If medication is available, flag as being on antidepressants df.at[person_id, 'de_on_antidepr'] = True @@ -996,7 +997,10 @@ def apply(self, person_id, squeeze_factor): return self.sim.modules['HealthSystem'].get_blank_appt_footprint() # Check availability of antidepressant medication - if self.get_consumables(self.module.parameters['anti_depressant_medication_item_code']): + # Dose is 25mg daily, patient provided with month supply - 25mg x 30.437 (days) = 761mg per month + item_code_with_dose = {self.module.parameters['anti_depressant_medication_item_code']: 761} + + if self.get_consumables(item_codes=item_code_with_dose): # Schedule their next HSI for a refill of medication, one month from now self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=HSI_Depression_Refill_Antidepressant(person_id=person_id, module=self.module), diff --git a/src/tlo/methods/diarrhoea.py b/src/tlo/methods/diarrhoea.py index fda4489fe1..cc589475d5 100644 --- a/src/tlo/methods/diarrhoea.py +++ b/src/tlo/methods/diarrhoea.py @@ -651,18 +651,23 @@ def report_daly_values(self): def look_up_consumables(self): """Look up and store the consumables item codes used in each of the HSI.""" - get_item_codes_from_package_name = self.sim.modules['HealthSystem'].get_item_codes_from_package_name - - self.consumables_used_in_hsi['ORS'] = get_item_codes_from_package_name( - package='ORS') - self.consumables_used_in_hsi['Treatment_Severe_Dehydration'] = get_item_codes_from_package_name( - package='Treatment of severe diarrhea') - self.consumables_used_in_hsi['Zinc_Under6mo'] = get_item_codes_from_package_name( - package='Zinc for Children 0-6 months') - self.consumables_used_in_hsi['Zinc_Over6mo'] = get_item_codes_from_package_name( - package='Zinc for Children 6-59 months') - self.consumables_used_in_hsi['Antibiotics_for_Dysentery'] = get_item_codes_from_package_name( - package='Antibiotics for treatment of dysentery') + ic = self.sim.modules['HealthSystem'].get_item_code_from_item_name + + self.consumables_used_in_hsi['ORS'] = {ic('ORS, sachet'): 2} + + self.consumables_used_in_hsi['Treatment_Severe_Dehydration'] = \ + {ic('ORS, sachet'): 2, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 1000} + + self.consumables_used_in_hsi['Zinc'] = ic('Zinc, tablet, 20 mg') + + # For weight based treatment for children under five, we've averaged the median weight for each for years + # 0-5 as 12kg. + # So for cipro/para - 10mg/kg 12 hrly for 7 days = ((10*12)*2) * 7 (same dose in mg reccomended) + self.consumables_used_in_hsi['Antibiotics_for_Dysentery'] = \ + {ic('Ciprofloxacin 250mg_100_CMST'): 1680, + ic("Paracetamol syrup 120mg/5ml_0.0119047619047619_CMST"): 70} # 24mg/ml so 1680/24 = 70ml per dose def do_treatment(self, person_id, hsi_event): """Method called by the HSI that enacts decisions about a treatment and its effect for diarrhoea caused by a @@ -706,9 +711,10 @@ def do_treatment(self, person_id, hsi_event): # ** Implement the procedure for treatment ** # STEP ZERO: Get the Zinc consumable (happens irrespective of whether child will die or not) + # Dose is 10mg 24hrly for 10 days <6months or 20m for >6mnths + dose = 100 if person.age_exact_years < 0.5 else 200 gets_zinc = hsi_event.get_consumables( - item_codes=self.consumables_used_in_hsi[ - 'Zinc_Under6mo' if person.age_exact_years < 0.5 else 'Zinc_Over6mo'] + item_codes={self.consumables_used_in_hsi['Zinc']: dose} ) # STEP ONE: Aim to alleviate dehydration: @@ -965,7 +971,7 @@ def do_at_generic_first_appt( self.rng.rand() < self.parameters["prob_hospitalization_on_danger_signs"] ) hsi_event_class = ( - HSI_Diarrhoea_Treatment_Inpatient if is_inpatient else + HSI_Diarrhoea_Treatment_Inpatient if is_inpatient else HSI_Diarrhoea_Treatment_Outpatient ) event = hsi_event_class(person_id=patient_id, module=self) diff --git a/src/tlo/methods/epilepsy.py b/src/tlo/methods/epilepsy.py index 6c4ff0e41d..8bc71fb69f 100644 --- a/src/tlo/methods/epilepsy.py +++ b/src/tlo/methods/epilepsy.py @@ -611,7 +611,14 @@ def apply(self, person_id, squeeze_factor): if best_available_medicine is not None: # Request the medicine from the health system - self.get_consumables(self.module.item_codes[best_available_medicine]) + + dose = {'phenobarbitone': 9131, # 100mg per day - 3 months + 'carbamazepine': 91_311, # 1000mg per day - 3 months + 'phenytoin': 27_393} # 300mg per day - 3 months + + self.get_consumables({self.module.item_codes[best_available_medicine]: + dose[best_available_medicine]}) + # Update this person's properties to show that they are currently on medication df.at[person_id, 'ep_antiep'] = True @@ -662,18 +669,30 @@ def apply(self, person_id, squeeze_factor): # Request the medicine best_available_medicine = self.module.get_best_available_medicine(self) if best_available_medicine is not None: + + # Schedule a reoccurrence of this follow-up in 3 months if ep_seiz_stat == '3', + # else, schedule this reoccurrence of it in 1 year (i.e., if ep_seiz_stat == '2' + if df.at[person_id, 'ep_seiz_stat'] == '3': + fu_mnths = 3 + else: + fu_mnths = 12 + # The medicine is available, so request it - self.get_consumables(self.module.item_codes[best_available_medicine]) + dose = {'phenobarbitone_3_mnths': 9131, 'phenobarbitone_12_mnths': 36_525, # 100mg per day - 3/12 months + 'carbamazepine_3_mnths': 91_311, 'carbamazepine_12_mnths': 365_250, # 1000mg per day - 3/12 months + 'phenytoin_3_mnths': 27_393, 'phenytoin_12_mnths': 109_575} # 300mg per day - 3/12 months + + self.get_consumables({self.module.item_codes[best_available_medicine]: + dose[f'{best_available_medicine}_{fu_mnths}_mnths']}) # Reset counter of "failed attempts" and put the appointment for the next occurrence to the usual self._counter_of_failed_attempts_due_to_unavailable_medicines = 0 self.EXPECTED_APPT_FOOTPRINT = self._DEFAULT_APPT_FOOTPRINT - # Schedule a reoccurrence of this follow-up in 3 months if ep_seiz_stat == '3', - # else, schedule this reoccurrence of it in 1 year (i.e., if ep_seiz_stat == '2') + # Schedule follow-up hs.schedule_hsi_event( hsi_event=self, - topen=self.sim.date + DateOffset(months=3 if df.at[person_id, 'ep_seiz_stat'] == '3' else 12), + topen=self.sim.date + DateOffset(months=fu_mnths), tclose=None, priority=0 ) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index a132c6e008..fde32ed915 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1013,31 +1013,37 @@ def initialise_simulation(self, sim): self.item_codes_for_consumables_required['circ'] = \ hs.get_item_codes_from_package_name("Male circumcision ") - self.item_codes_for_consumables_required['prep'] = { - hs.get_item_code_from_item_name("Tenofovir (TDF)/Emtricitabine (FTC), tablet, 300/200 mg"): 1} + # adult prep: 1 tablet daily + self.item_codes_for_consumables_required['prep'] = \ + hs.get_item_code_from_item_name("Tenofovir (TDF)/Emtricitabine (FTC), tablet, 300/200 mg") - # infant NVP given in 3-monthly dosages - self.item_codes_for_consumables_required['infant_prep'] = { - hs.get_item_code_from_item_name("Nevirapine, oral solution, 10 mg/ml"): 1} + # infant NVP 1.5mg daily for birth weight 2500g or above, for 6 weeks + self.item_codes_for_consumables_required['infant_prep'] = \ + hs.get_item_code_from_item_name("Nevirapine, oral solution, 10 mg/ml") # First - line ART for adults(age > "ART_age_cutoff_older_child") - self.item_codes_for_consumables_required['First-line ART regimen: adult'] = { - hs.get_item_code_from_item_name("First-line ART regimen: adult"): 1} - self.item_codes_for_consumables_required['First-line ART regimen: adult: cotrimoxazole'] = { - hs.get_item_code_from_item_name("Cotrimoxizole, 960mg pppy"): 1} + # TDF/3TC/DTG 120/60/50mg, 1 tablet per day + # cotrim adult tablet, 1 tablet per day, units specified in mg * dispensation days + self.item_codes_for_consumables_required['First-line ART regimen: adult'] = \ + hs.get_item_code_from_item_name("First-line ART regimen: adult") + self.item_codes_for_consumables_required['First-line ART regimen: adult: cotrimoxazole'] = \ + hs.get_item_code_from_item_name("Cotrimoxizole, 960mg pppy") # ART for older children aged ("ART_age_cutoff_younger_child" < age <= "ART_age_cutoff_older_child"): - # cotrim is separate item - optional in get_cons call - self.item_codes_for_consumables_required['First line ART regimen: older child'] = { - hs.get_item_code_from_item_name("First line ART regimen: older child"): 1} - self.item_codes_for_consumables_required['First line ART regimen: older child: cotrimoxazole'] = { - hs.get_item_code_from_item_name("Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg"): 1} + # ABC/3TC/DTG 120/60/50mg, 3 tablets per day + # cotrim paediatric tablet, 4 tablets per day, units specified in mg * dispensation days + self.item_codes_for_consumables_required['First line ART regimen: older child'] = \ + hs.get_item_code_from_item_name("First line ART regimen: older child") + self.item_codes_for_consumables_required['First line ART regimen: older child: cotrimoxazole'] = \ + hs.get_item_code_from_item_name("Cotrimoxazole 120mg_1000_CMST") # ART for younger children aged (age < "ART_age_cutoff_younger_child"): - self.item_codes_for_consumables_required['First line ART regimen: young child'] = { - hs.get_item_code_from_item_name("First line ART regimen: young child"): 1} - self.item_codes_for_consumables_required['First line ART regimen: young child: cotrimoxazole'] = { - hs.get_item_code_from_item_name("Sulfamethoxazole + trimethropin, oral suspension, 240 mg, 100 ml"): 1} + # ABC/3TC/DTG 120/60/10mg, 2 tablets per day + # cotrim paediatric tablet, 2 tablets per day, units specified in mg * dispensation days + self.item_codes_for_consumables_required['First line ART regimen: young child'] = \ + hs.get_item_code_from_item_name("First line ART regimen: young child") + self.item_codes_for_consumables_required['First line ART regimen: young child: cotrimoxazole'] = \ + hs.get_item_code_from_item_name("Cotrimoxazole 120mg_1000_CMST") # 7) Define the DxTests # HIV Rapid Diagnostic Test: @@ -2467,7 +2473,9 @@ def apply(self, person_id, squeeze_factor): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() # Check that infant prophylaxis is available and if it is, initiate: - if self.get_consumables(item_codes=self.module.item_codes_for_consumables_required['infant_prep']): + if self.get_consumables( + item_codes={self.module.item_codes_for_consumables_required['infant_prep']: 63} + ): df.at[person_id, "hv_is_on_prep"] = True # Schedule follow-up visit for 3 months time @@ -2551,7 +2559,10 @@ def apply(self, person_id, squeeze_factor): return self.make_appt_footprint({"Over5OPD": 1, "VCTPositive": 1}) # Check that PrEP is available and if it is, initiate or continue PrEP: - if self.get_consumables(item_codes=self.module.item_codes_for_consumables_required['prep']): + quantity_required = self.module.parameters['dispensation_period_months'] * 30 + if self.get_consumables( + item_codes={self.module.item_codes_for_consumables_required['prep']: quantity_required} + ): df.at[person_id, "hv_is_on_prep"] = True # Schedule 'decision about whether to continue on PrEP' for 3 months time @@ -2792,29 +2803,33 @@ def get_drugs(self, age_of_person): whether individual drugs were available""" p = self.module.parameters + dispensation_days = 30 * self.module.parameters['dispensation_period_months'] if age_of_person < p["ART_age_cutoff_young_child"]: # Formulation for young children drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['First line ART regimen: young child'], - optional_item_codes=self.module.item_codes_for_consumables_required[ - 'First line ART regimen: young child: cotrimoxazole'], + item_codes={self.module.item_codes_for_consumables_required[ + 'First line ART regimen: young child']: dispensation_days * 2}, + optional_item_codes={self.module.item_codes_for_consumables_required[ + 'First line ART regimen: young child: cotrimoxazole']: dispensation_days * 240}, return_individual_results=True) elif age_of_person <= p["ART_age_cutoff_older_child"]: # Formulation for older children drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['First line ART regimen: older child'], - optional_item_codes=self.module.item_codes_for_consumables_required[ - 'First line ART regimen: older child: cotrimoxazole'], + item_codes={self.module.item_codes_for_consumables_required[ + 'First line ART regimen: older child']: dispensation_days * 3}, + optional_item_codes={self.module.item_codes_for_consumables_required[ + 'First line ART regimen: older child: cotrimoxazole']: dispensation_days * 480}, return_individual_results=True) else: # Formulation for adults drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['First-line ART regimen: adult'], - optional_item_codes=self.module.item_codes_for_consumables_required[ - 'First-line ART regimen: adult: cotrimoxazole'], + item_codes={self.module.item_codes_for_consumables_required[ + 'First-line ART regimen: adult']: dispensation_days}, + optional_item_codes={self.module.item_codes_for_consumables_required[ + 'First-line ART regimen: adult: cotrimoxazole']: dispensation_days * 960}, return_individual_results=True) # add drug names to dict diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index dcd8527466..2280d41335 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -691,174 +691,180 @@ def get_and_store_labour_item_codes(self): This function defines the required consumables for each intervention delivered during this module and stores them in a module level dictionary called within HSIs """ - get_item_code_from_pkg = self.sim.modules['HealthSystem'].get_item_codes_from_package_name - - get_list_of_items = pregnancy_helper_functions.get_list_of_items - - # ---------------------------------- IV DRUG ADMIN EQUIPMENT ------------------------------------------------- - self.item_codes_lab_consumables['iv_drug_equipment'] = \ - get_list_of_items(self, ['Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) + ic = self.sim.modules['HealthSystem'].get_item_code_from_item_name # ---------------------------------- BLOOD TEST EQUIPMENT --------------------------------------------------- self.item_codes_lab_consumables['blood_test_equipment'] = \ - get_list_of_items(self, ['Blood collecting tube, 5 ml', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) + {ic('Blood collecting tube, 5 ml'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } + # ---------------------------------- IV DRUG ADMIN EQUIPMENT ------------------------------------------------- + self.item_codes_lab_consumables['iv_drug_equipment'] = \ + {ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } # ------------------------------------------ FULL BLOOD COUNT ------------------------------------------------- - self.item_codes_lab_consumables['full_blood_count'] = get_list_of_items(self, ['Complete blood count']) + self.item_codes_lab_consumables['full_blood_count'] = {ic('Complete blood count'): 1} # -------------------------------------------- DELIVERY ------------------------------------------------------ # assuming CDK has blade, soap, cord tie self.item_codes_lab_consumables['delivery_core'] = \ - get_list_of_items(self, ['Clean delivery kit', - 'Chlorhexidine 1.5% solution_5_CMST']) + {ic('Clean delivery kit'): 1, + ic('Chlorhexidine 1.5% solution_5_CMST'): 20, + } self.item_codes_lab_consumables['delivery_optional'] = \ - get_list_of_items(self, ['Cannula iv (winged with injection pot) 18_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Gauze, absorbent 90cm x 40m_each_CMST', - 'Paracetamol, tablet, 500 mg']) + {ic('Gauze, absorbent 90cm x 40m_each_CMST'): 30, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Paracetamol, tablet, 500 mg'): 8000 + } # -------------------------------------------- CAESAREAN DELIVERY ------------------------------------------ self.item_codes_lab_consumables['caesarean_delivery_core'] = \ - get_list_of_items(self, ['Halothane (fluothane)_250ml_CMST', - 'Ceftriaxone 1g, PFR_each_CMST', - 'Metronidazole 200mg_1000_CMST']) + {ic('Halothane (fluothane)_250ml_CMST'): 100, + ic('Ceftriaxone 1g, PFR_each_CMST'): 2, + ic('Metronidazole 200mg_1000_CMST'): 1, # todo: replace + } self.item_codes_lab_consumables['caesarean_delivery_optional'] = \ - get_list_of_items(self, ['Scalpel blade size 22 (individually wrapped)_100_CMST', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Paracetamol, tablet, 500 mg', - 'Declofenac injection_each_CMST', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml', - "ringer's lactate (Hartmann's solution), 1000 ml_12_IDA", - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - "Giving set iv administration + needle 15 drops/ml_each_CMST", - "Chlorhexidine 1.5% solution_5_CMST"]) + {ic('Scalpel blade size 22 (individually wrapped)_100_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Declofenac injection_each_CMST'): 2, + ic("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 2000, + } # -------------------------------------------- OBSTETRIC SURGERY ---------------------------------------------- self.item_codes_lab_consumables['obstetric_surgery_core'] = \ - get_list_of_items(self, ['Halothane (fluothane)_250ml_CMST', - 'Ceftriaxone 1g, PFR_each_CMST', - 'Metronidazole 200mg_1000_CMST']) + {ic('Halothane (fluothane)_250ml_CMST'): 100, + ic('Ceftriaxone 1g, PFR_each_CMST'): 2, + ic('Metronidazole 200mg_1000_CMST'): 1, # todo: replace + } self.item_codes_lab_consumables['obstetric_surgery_optional'] = \ - get_list_of_items(self, ['Scalpel blade size 22 (individually wrapped)_100_CMST', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Paracetamol, tablet, 500 mg', - 'Declofenac injection_each_CMST', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml', - "ringer's lactate (Hartmann's solution), 1000 ml_12_IDA", - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - "Giving set iv administration + needle 15 drops/ml_each_CMST"]) + {ic('Scalpel blade size 22 (individually wrapped)_100_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Declofenac injection_each_CMST'): 2, + ic("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 2000, + } # -------------------------------------------- ABX FOR PROM ------------------------------------------------- self.item_codes_lab_consumables['abx_for_prom'] = \ - get_list_of_items(self, ['Benzathine benzylpenicillin, powder for injection, 2.4 million IU']) + {ic('Benzathine benzylpenicillin, powder for injection, 2.4 million IU'): 8} # -------------------------------------------- ANTENATAL STEROIDS --------------------------------------------- + self.item_codes_lab_consumables['antenatal_steroids'] = \ - get_list_of_items(self, ['Dexamethasone 5mg/ml, 5ml_each_CMST']) + {ic('Dexamethasone 5mg/ml, 5ml_each_CMST'): 12} # ------------------------------------- INTRAVENOUS ANTIHYPERTENSIVES --------------------------------------- self.item_codes_lab_consumables['iv_antihypertensives'] = \ - get_list_of_items(self, ['Hydralazine, powder for injection, 20 mg ampoule']) + {ic('Hydralazine, powder for injection, 20 mg ampoule'): 1} # --------------------------------------- ORAL ANTIHYPERTENSIVES --------------------------------------------- self.item_codes_lab_consumables['oral_antihypertensives'] = \ - get_list_of_items(self, ['Methyldopa 250mg_1000_CMST']) + {ic('Methyldopa 250mg_1000_CMST'): 1} # ---------------------------------- SEVERE PRE-ECLAMPSIA/ECLAMPSIA ----------------------------------------- self.item_codes_lab_consumables['magnesium_sulfate'] = \ - get_list_of_items(self, ['Magnesium sulfate, injection, 500 mg/ml in 10-ml ampoule']) + {ic('Magnesium sulfate, injection, 500 mg/ml in 10-ml ampoule'): 2} self.item_codes_lab_consumables['eclampsia_management_optional'] = \ - get_list_of_items(self, ['Misoprostol, tablet, 200 mcg', - 'Oxytocin, injection, 10 IU in 1 ml ampoule', - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Oxygen, 1000 liters, primarily with oxygen cylinders', - 'Complete blood count', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml']) - + {ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 23_040, + ic('Complete blood count'): 1, + ic('Blood collecting tube, 5 ml'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + } # ------------------------------------- OBSTRUCTED LABOUR --------------------------------------------------- self.item_codes_lab_consumables['obstructed_labour'] = \ - get_list_of_items(self, ['Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml', - 'Benzylpenicillin 3g (5MU), PFR_each_CMST', - 'Gentamycin, injection, 40 mg/ml in 2 ml vial', - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Complete blood count', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml', - 'Paracetamol, tablet, 500 mg', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Gauze, absorbent 90cm x 40m_each_CMST', - 'Suture pack']) - + {ic('Lidocaine HCl (in dextrose 7.5%), ampoule 2 ml'): 1, + ic('Benzathine benzylpenicillin, powder for injection, 2.4 million IU'): 8, + ic('Gentamycin, injection, 40 mg/ml in 2 ml vial'): 6, + ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Complete blood count'): 1, + ic('Blood collecting tube, 5 ml'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Pethidine, 50 mg/ml, 2 ml ampoule'): 6, + ic('Gauze, absorbent 90cm x 40m_each_CMST'): 30, + ic('Suture pack'): 1, + } # ------------------------------------- OBSTETRIC VACUUM --------------------------------------------------- - self.item_codes_lab_consumables['vacuum'] = get_list_of_items(self, ['Vacuum, obstetric']) + self.item_codes_lab_consumables['vacuum'] = {ic('Vacuum, obstetric'): 1} # ------------------------------------- MATERNAL SEPSIS ----------------------------------------------------- self.item_codes_lab_consumables['maternal_sepsis_core'] = \ - get_list_of_items(self, ['Benzylpenicillin 3g (5MU), PFR_each_CMST', - 'Gentamycin, injection, 40 mg/ml in 2 ml vial']) - # 'Metronidazole, injection, 500 mg in 100 ml vial']) + {ic('Benzylpenicillin 3g (5MU), PFR_each_CMST'): 8, + ic('Gentamycin, injection, 40 mg/ml in 2 ml vial'): 6, + } self.item_codes_lab_consumables['maternal_sepsis_optional'] = \ - get_list_of_items(self, ['Cannula iv (winged with injection pot) 18_each_CMST', - 'Oxygen, 1000 liters, primarily with oxygen cylinders', - 'Paracetamol, tablet, 500 mg', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml', - 'Disposables gloves, powder free, 100 pieces per box', - 'Complete blood count']) - + {ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 23_040, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Complete blood count'): 1, + } # ------------------------------------- ACTIVE MANAGEMENT THIRD STAGE --------------------------------------- - self.item_codes_lab_consumables['amtsl'] = \ - get_list_of_items(self, ['Oxytocin, injection, 10 IU in 1 ml ampoule']) + self.item_codes_lab_consumables['amtsl'] = {ic('Oxytocin, injection, 10 IU in 1 ml ampoule'): 1} # ------------------------------------- POSTPARTUM HAEMORRHAGE --------------------------------------- self.item_codes_lab_consumables['pph_core'] = \ - get_list_of_items(self, ['Oxytocin, injection, 10 IU in 1 ml ampoule']) + {ic('Oxytocin, injection, 10 IU in 1 ml ampoule'): 5} self.item_codes_lab_consumables['pph_optional'] = \ - get_list_of_items(self, ['Misoprostol, tablet, 200 mcg', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Oxygen, 1000 liters, primarily with oxygen cylinders', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Bag, urine, collecting, 2000 ml', - 'Foley catheter', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box', - 'Complete blood count']) + {ic('Misoprostol, tablet, 200 mcg'): 600, + ic('Pethidine, 50 mg/ml, 2 ml ampoule'): 6, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 23_040, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1, + ic('Complete blood count'): 1, + } # ------------------------------------- BLOOD TRANSFUSION --------------------------------------- - self.item_codes_lab_consumables['blood_transfusion'] = get_list_of_items(self, ['Blood, one unit']) + self.item_codes_lab_consumables['blood_transfusion'] = {ic('Blood, one unit'): 2} # ------------------------------------------ FULL BLOOD COUNT ------------------------------------------------- - self.item_codes_lab_consumables['hb_test'] = get_list_of_items(self, ['Haemoglobin test (HB)']) + self.item_codes_lab_consumables['hb_test'] = {ic('Haemoglobin test (HB)'): 1} # ---------------------------------- IRON AND FOLIC ACID ------------------------------------------------------ + # Dose changes at run time self.item_codes_lab_consumables['iron_folic_acid'] = \ - get_item_code_from_pkg('Ferrous Salt + Folic Acid, tablet, 200 + 0.25 mg') + {ic('Ferrous Salt + Folic Acid, tablet, 200 + 0.25 mg'): 1} # -------------------------------------------- RESUSCITATION ------------------------------------------ - self.item_codes_lab_consumables['resuscitation'] = \ - get_list_of_items(self, ['Infant resuscitator, clear plastic + mask + bag_each_CMST']) + self.item_codes_lab_consumables['resuscitation'] =\ + {ic('Infant resuscitator, clear plastic + mask + bag_each_CMST'): 1} def initialise_simulation(self, sim): # Update self.current_parameters @@ -1668,7 +1674,9 @@ def prophylactic_labour_interventions(self, hsi_event): # If she has not already receive antibiotics, we check for consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='abx_for_prom', optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_lab_consumables['abx_for_prom'], + opt_cons=self.item_codes_lab_consumables['iv_drug_equipment']) # Then query if these consumables are available during this HSI And provide if available. # Antibiotics for from reduce risk of newborn sepsis within the first @@ -1682,8 +1690,9 @@ def prophylactic_labour_interventions(self, hsi_event): mni[person_id]['labour_state'] == 'late_preterm_labour': avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='antenatal_steroids', - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_lab_consumables['antenatal_steroids'], + opt_cons=self.item_codes_lab_consumables['iv_drug_equipment']) # If available they are given. Antenatal steroids reduce a preterm newborns chance of developing # respiratory distress syndrome and of death associated with prematurity @@ -1745,8 +1754,9 @@ def assessment_and_treatment_of_severe_pre_eclampsia_mgso4(self, hsi_event, labo # Define and check for the required consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='magnesium_sulfate', - optional='eclampsia_management_optional') + self, hsi_event, + cons=self.item_codes_lab_consumables['magnesium_sulfate'], + opt_cons=self.item_codes_lab_consumables['eclampsia_management_optional']) # If the consumables are available - the intervention is delivered. IV magnesium reduces the # probability that a woman with severe pre-eclampsia will experience eclampsia in labour @@ -1774,8 +1784,9 @@ def assessment_and_treatment_of_hypertension(self, hsi_event, labour_stage): # Then query if these consumables are available during this HSI avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='iv_antihypertensives', - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_lab_consumables['iv_antihypertensives'], + opt_cons=self.item_codes_lab_consumables['iv_drug_equipment']) # If they are available then the woman is started on treatment. Intravenous antihypertensive reduce a # womans risk of progression from mild to severe gestational hypertension ANd reduce risk of death for @@ -1789,8 +1800,9 @@ def assessment_and_treatment_of_hypertension(self, hsi_event, labour_stage): elif (labour_stage == 'pp') and (df.at[person_id, 'pn_htn_disorders'] == 'severe_gest_htn'): df.at[person_id, 'pn_htn_disorders'] = 'gest_htn' - avail = hsi_event.get_consumables( - item_codes=self.item_codes_lab_consumables['oral_antihypertensives']) + dose = (7 * 4) * 6 # approximating 4 tablets a day, for 6 weeks + cons = {_i: dose for _i in self.item_codes_lab_consumables['oral_antihypertensives']} + avail = hsi_event.get_consumables(item_codes=cons) if avail: df.at[person_id, 'la_gest_htn_on_treatment'] = True @@ -1821,8 +1833,9 @@ def assessment_and_treatment_of_eclampsia(self, hsi_event, labour_stage): # define and check required consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='magnesium_sulfate', - optional='eclampsia_management_optional') + self, hsi_event, + cons=self.item_codes_lab_consumables['magnesium_sulfate'], + opt_cons=self.item_codes_lab_consumables['eclampsia_management_optional']) if (labour_stage == 'ip') and (df.at[person_id, 'ac_admitted_for_immediate_delivery'] == 'none'): self.determine_delivery_mode_in_spe_or_ec(person_id, hsi_event, 'ec') @@ -1868,8 +1881,9 @@ def refer_for_cs(): # If the general package is available AND the facility has the correct tools to carry out the # delivery then it can occur avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='vacuum', - optional='obstructed_labour') + self, hsi_event, + cons=self.item_codes_lab_consumables['vacuum'], + opt_cons=self.item_codes_lab_consumables['obstructed_labour']) # run HCW check sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='avd', @@ -1921,8 +1935,9 @@ def assessment_and_treatment_of_maternal_sepsis(self, hsi_event, labour_stage): # Define and check available consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='maternal_sepsis_core', - optional='maternal_sepsis_optional') + self, hsi_event, + cons=self.item_codes_lab_consumables['maternal_sepsis_core'], + opt_cons=self.item_codes_lab_consumables['maternal_sepsis_optional']) # If delivered this intervention reduces a womans risk of dying from sepsis if avail and sf_check: @@ -2000,7 +2015,9 @@ def active_management_of_the_third_stage_of_labour(self, hsi_event): # Define and check available consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='amtsl', optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_lab_consumables['amtsl'], + opt_cons=self.item_codes_lab_consumables['iv_drug_equipment']) # run HCW check sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='uterotonic', @@ -2031,7 +2048,9 @@ def assessment_and_treatment_of_pph_uterine_atony(self, hsi_event): # Define and check available consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='pph_core', optional='pph_optional') + self, hsi_event, + cons=self.item_codes_lab_consumables['pph_core'], + opt_cons=self.item_codes_lab_consumables['pph_optional']) # run HCW check sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='uterotonic', @@ -2114,8 +2133,9 @@ def surgical_management_of_pph(self, hsi_event): # We log the required consumables and condition the surgery happening on the availability of the # first consumable in this package, the anaesthetic required for the surgery avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='obstetric_surgery_core', - optional='obstetric_surgery_optional') + self, hsi_event, + cons=self.item_codes_lab_consumables['obstetric_surgery_core'], + opt_cons=self.item_codes_lab_consumables['obstetric_surgery_optional']) # run HCW check sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='surg', @@ -2150,8 +2170,9 @@ def blood_transfusion(self, hsi_event): # Check consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_lab_consumables, core='blood_transfusion', number=2, - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_lab_consumables['blood_transfusion'], + opt_cons=self.item_codes_lab_consumables['iv_drug_equipment']) # check HCW sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='blood_tran', @@ -2904,8 +2925,9 @@ def apply(self, person_id, squeeze_factor): # LOG CONSUMABLES FOR DELIVERY... # We assume all deliveries require this basic package of consumables avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_lab_consumables, core='delivery_core', - optional='delivery_optional') + self.module, self, + cons=self.module.item_codes_lab_consumables['delivery_core'], + opt_cons=self.module.item_codes_lab_consumables['delivery_optional']) # If the clean delivery kit consumable is available, we assume women benefit from clean delivery if avail: @@ -2981,7 +3003,7 @@ def apply(self, person_id, squeeze_factor): # TODO: potential issue is that this consumable is being logged now for every birth as opposed to # for each birth where resuscitation of the newborn is required avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_lab_consumables, core='resuscitation') + self.module, self, cons=self.module.item_codes_lab_consumables['resuscitation'], opt_cons=None) # Run HCW check sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.module, @@ -3200,8 +3222,9 @@ def apply(self, person_id, squeeze_factor): # We log the required consumables and condition the caesarean happening on the availability of the # first consumable in this package, the anaesthetic required for the surgery avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, self.module.item_codes_lab_consumables, core='caesarean_delivery_core', - optional='caesarean_delivery_optional') + self.module, self, + cons=self.module.item_codes_lab_consumables['caesarean_delivery_core'], + opt_cons=self.module.item_codes_lab_consumables['caesarean_delivery_optional']) # We check that the HCW will deliver the intervention sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.module, sf='surg', diff --git a/src/tlo/methods/newborn_outcomes.py b/src/tlo/methods/newborn_outcomes.py index debfdb3530..492c37a1fe 100644 --- a/src/tlo/methods/newborn_outcomes.py +++ b/src/tlo/methods/newborn_outcomes.py @@ -377,43 +377,53 @@ def get_and_store_newborn_item_codes(self): This function defines the required consumables for each intervention delivered during this module and stores them in a module level dictionary called within HSIs """ - get_list_of_items = pregnancy_helper_functions.get_list_of_items - - # ---------------------------------- IV DRUG ADMIN EQUIPMENT ------------------------------------------------- - self.item_codes_nb_consumables['iv_drug_equipment'] = \ - get_list_of_items(self, ['Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) + ic = self.sim.modules['HealthSystem'].get_item_code_from_item_name + # First we store the item codes for the consumables for which their quantity varies for individuals based on + # length of pregnancy # ---------------------------------- BLOOD TEST EQUIPMENT --------------------------------------------------- self.item_codes_nb_consumables['blood_test_equipment'] = \ - get_list_of_items(self, ['Disposables gloves, powder free, 100 pieces per box']) + {ic('Blood collecting tube, 5 ml'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } + # ---------------------------------- IV DRUG ADMIN EQUIPMENT ------------------------------------------------- + self.item_codes_nb_consumables['iv_drug_equipment'] = \ + {ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } # -------------------------------------------- VITAMIN K ------------------------------------------ self.item_codes_nb_consumables['vitamin_k'] = \ - get_list_of_items(self, ['vitamin K1 (phytomenadione) 1 mg/ml, 1 ml, inj._100_IDA']) + {ic('vitamin K1 (phytomenadione) 1 mg/ml, 1 ml, inj._100_IDA'): 1} # -------------------------------------------- EYE CARE ------------------------------------------ - self.item_codes_nb_consumables['eye_care'] = get_list_of_items( - self, ['Tetracycline eye ointment, 1 %, tube 5 mg']) + self.item_codes_nb_consumables['eye_care'] = \ + {ic('Tetracycline eye ointment, 1 %, tube 5 mg'): 5} # ------------------------------------- SEPSIS - FULL SUPPORTIVE CARE --------------------------------------- + # Whilst abx for newborns are weight based the maximum dose does not exceed the minimum unit for the costing + # model self.item_codes_nb_consumables['sepsis_supportive_care_core'] = \ - get_list_of_items(self, ['Benzylpenicillin 1g (1MU), PFR_Each_CMST', - 'Gentamicin 40mg/ml, 2ml_each_CMST', - 'Oxygen, 1000 liters, primarily with oxygen cylinders']) + {ic('Benzylpenicillin 1g (1MU), PFR_Each_CMST'): 1, + ic('Gentamicin 40mg/ml, 2ml_each_CMST'): 1, + ic('Oxygen, 1000 liters, primarily with oxygen cylinders'): 5760 # + } self.item_codes_nb_consumables['sepsis_supportive_care_optional'] = \ - get_list_of_items(self, ['Dextrose (glucose) 5%, 1000ml_each_CMST', - 'Tube, feeding CH 8_each_CMST', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Giving set iv administration + needle 15 drops/ml_each_CMST', - 'Disposables gloves, powder free, 100 pieces per box']) + {ic('Dextrose (glucose) 5%, 1000ml_each_CMST'): 500, + ic('Tube, feeding CH 8_each_CMST'): 1, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Disposables gloves, powder free, 100 pieces per box'): 1 + } # ---------------------------------------- SEPSIS - ANTIBIOTICS --------------------------------------------- - self.item_codes_nb_consumables['sepsis_abx'] =\ - get_list_of_items(self, ['Benzylpenicillin 1g (1MU), PFR_Each_CMST', - 'Gentamicin 40mg/ml, 2ml_each_CMST']) + self.item_codes_nb_consumables['sepsis_abx'] = \ + {ic('Benzylpenicillin 1g (1MU), PFR_Each_CMST'): 1, + ic('Gentamicin 40mg/ml, 2ml_each_CMST'): 1, + } def initialise_simulation(self, sim): # For the first period (2010-2015) we use the first value in each list as a parameter @@ -969,8 +979,9 @@ def assessment_and_treatment_newborn_sepsis(self, hsi_event, facility_type): # check consumables avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_nb_consumables, core='sepsis_supportive_care_core', - optional='sepsis_supportive_care_optional') + self, hsi_event, + cons=self.item_codes_nb_consumables['sepsis_supportive_care_core'], + opt_cons=self.item_codes_nb_consumables['sepsis_supportive_care_optional']) # Then, if the consumables are available, treatment for sepsis is delivered if avail and sf_check: @@ -980,8 +991,9 @@ def assessment_and_treatment_newborn_sepsis(self, hsi_event, facility_type): # The same pattern is then followed for health centre care else: avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, self.item_codes_nb_consumables, core='sepsis_abx', - optional='iv_drug_equipment') + self, hsi_event, + cons=self.item_codes_nb_consumables['sepsis_abx'], + opt_cons=self.item_codes_nb_consumables['iv_drug_equipment']) if avail and sf_check: df.at[person_id, 'nb_inj_abx_neonatal_sepsis'] = True diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 25a2d54f6d..5d99968cef 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -1274,21 +1274,23 @@ def apply(self, person_id, squeeze_factor): return # Define the consumables - of_repair_cons = pregnancy_helper_functions.get_list_of_items( - self, ['Scalpel blade size 22 (individually wrapped)_100_CMST', - 'Halothane (fluothane)_250ml_CMST', - 'Ceftriaxone 1g, PFR_each_CMST', - 'Metronidazole 200mg_1000_CMST', - 'Cannula iv (winged with injection pot) 18_each_CMST', - 'Paracetamol, tablet, 500 mg', - 'Declofenac injection_each_CMST', - 'Pethidine, 50 mg/ml, 2 ml ampoule', - 'Foley catheter', - 'Bag, urine, collecting, 2000 ml', - "ringer's lactate (Hartmann's solution), 1000 ml_12_IDA", - 'Sodium chloride, injectable solution, 0,9 %, 500 ml', - "Giving set iv administration + needle 15 drops/ml_each_CMST", - "Chlorhexidine 1.5% solution_5_CMST"]) + ic = self.sim.modules['HealthSystem'].get_item_code_from_item_name + + of_repair_cons = \ + {ic('Scalpel blade size 22 (individually wrapped)_100_CMST'): 1, + ic('Halothane (fluothane)_250ml_CMST'): 100, + ic('Ceftriaxone 1g, PFR_each_CMST'): 2, + ic('Metronidazole 200mg_1000_CMST'): 6000, + ic('Cannula iv (winged with injection pot) 18_each_CMST'): 1, + ic('Paracetamol, tablet, 500 mg'): 8000, + ic('Declofenac injection_each_CMST'): 1, + ic('Foley catheter'): 1, + ic('Bag, urine, collecting, 2000 ml'): 1, + ic("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 2000, + ic('Sodium chloride, injectable solution, 0,9 %, 500 ml'): 2000, + ic('Giving set iv administration + needle 15 drops/ml_each_CMST'): 1, + ic('Chlorhexidine 1.5% solution_5_CMST'): 50, + } self.get_consumables(item_codes=of_repair_cons) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 20a712f134..8f7faa0503 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -21,7 +21,7 @@ def get_list_of_items(self, item_list): return codes -def return_cons_avail(self, hsi_event, cons_dict, **info): +def return_cons_avail(self, hsi_event, cons, opt_cons): """ This function is called by majority of interventions across maternal and neonatal modules to return whether a consumable or package of consumables are available. If analysis is not being conducted (as indicated by a series of @@ -38,21 +38,12 @@ def return_cons_avail(self, hsi_event, cons_dict, **info): ps_params = self.sim.modules['PregnancySupervisor'].current_parameters la_params = self.sim.modules['Labour'].current_parameters - # If 'number' is passed as an optional argument then a predetermined number of consumables will be requested - if 'number' in info.keys(): - core_cons = {cons_dict[info['core']][0]: info['number']} - else: - core_cons = cons_dict[info['core']] - - # If 'optional' is passed then the optional set of consumables is selected from the consumables dict - if 'optional' in info.keys(): - opt_cons = cons_dict[info['optional']] - else: + if opt_cons is None: opt_cons = [] # Check if analysis is currently running, if not then availability is determined normally if not ps_params['ps_analysis_in_progress'] and not la_params['la_analysis_in_progress']: - available = hsi_event.get_consumables(item_codes=core_cons, + available = hsi_event.get_consumables(item_codes=cons, optional_item_codes=opt_cons) if not available and (hsi_event.target in mni) and (hsi_event != 'AntenatalCare_Outpatient'): @@ -61,7 +52,7 @@ def return_cons_avail(self, hsi_event, cons_dict, **info): return available else: - available = hsi_event.get_consumables(item_codes=core_cons, optional_item_codes=opt_cons) + available = hsi_event.get_consumables(item_codes=cons, optional_item_codes=opt_cons) # Depending on HSI calling this function a different parameter set is used to determine if analysis is being # conducted diff --git a/src/tlo/methods/rti.py b/src/tlo/methods/rti.py index 654378c4bf..f591f5aa7b 100644 --- a/src/tlo/methods/rti.py +++ b/src/tlo/methods/rti.py @@ -3823,21 +3823,21 @@ def apply(self, person_id, squeeze_factor): # TODO: find a more complete list of required consumables for adults if is_child: self.module.item_codes_for_consumables_required['shock_treatment_child'] = { - get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 1, - get_item_code("Dextrose (glucose) 5%, 1000ml_each_CMST"): 1, + get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 500, + get_item_code("Dextrose (glucose) 5%, 1000ml_each_CMST"): 500, get_item_code('Cannula iv (winged with injection pot) 18_each_CMST'): 1, - get_item_code('Blood, one unit'): 1, - get_item_code("Oxygen, 1000 liters, primarily with oxygen cylinders"): 1 + get_item_code('Blood, one unit'): 2, + get_item_code("Oxygen, 1000 liters, primarily with oxygen cylinders"): 23_040 } is_cons_available = self.get_consumables( self.module.item_codes_for_consumables_required['shock_treatment_child'] ) else: self.module.item_codes_for_consumables_required['shock_treatment_adult'] = { - get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 1, + get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 2000, get_item_code('Cannula iv (winged with injection pot) 18_each_CMST'): 1, - get_item_code('Blood, one unit'): 1, - get_item_code("Oxygen, 1000 liters, primarily with oxygen cylinders"): 1 + get_item_code('Blood, one unit'): 2, + get_item_code("Oxygen, 1000 liters, primarily with oxygen cylinders"): 23_040 } is_cons_available = self.get_consumables( self.module.item_codes_for_consumables_required['shock_treatment_adult'] @@ -3935,7 +3935,7 @@ def apply(self, person_id, squeeze_factor): # If they have a fracture that needs a cast, ask for plaster of paris self.module.item_codes_for_consumables_required['fracture_treatment'] = { get_item_code('Plaster of Paris (POP) 10cm x 7.5cm slab_12_CMST'): fracturecastcounts, - get_item_code('Bandage, crepe 7.5cm x 1.4m long , when stretched'): slingcounts, + get_item_code('Bandage, crepe 7.5cm x 1.4m long , when stretched'): 200, } is_cons_available = self.get_consumables( self.module.item_codes_for_consumables_required['fracture_treatment'] @@ -4058,9 +4058,9 @@ def apply(self, person_id, squeeze_factor): # If they have an open fracture, ask for consumables to treat fracture if open_fracture_counts > 0: self.module.item_codes_for_consumables_required['open_fracture_treatment'] = { - get_item_code('Ceftriaxone 1g, PFR_each_CMST'): 1, - get_item_code('Cetrimide 15% + chlorhexidine 1.5% solution.for dilution _5_CMST'): 1, - get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 1, + get_item_code('Ceftriaxone 1g, PFR_each_CMST'): 2000, + get_item_code('Cetrimide 15% + chlorhexidine 1.5% solution.for dilution _5_CMST'): 500, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 100, get_item_code('Suture pack'): 1, } # If wound is "grossly contaminated" administer Metronidazole @@ -4068,9 +4068,10 @@ def apply(self, person_id, squeeze_factor): p = self.module.parameters prob_open_fracture_contaminated = p['prob_open_fracture_contaminated'] rand_for_contamination = self.module.rng.random_sample(size=1) + # NB: Dose used below from BNF is for surgical prophylaxsis if rand_for_contamination < prob_open_fracture_contaminated: self.module.item_codes_for_consumables_required['open_fracture_treatment'].update( - {get_item_code('Metronidazole, injection, 500 mg in 100 ml vial'): 1} + {get_item_code('Metronidazole, injection, 500 mg in 100 ml vial'): 1500} ) # Check that there are enough consumables to treat this person's fractures is_cons_available = self.get_consumables( @@ -4169,7 +4170,7 @@ def apply(self, person_id, squeeze_factor): if lacerationcounts > 0: self.module.item_codes_for_consumables_required['laceration_treatment'] = { get_item_code('Suture pack'): lacerationcounts, - get_item_code('Cetrimide 15% + chlorhexidine 1.5% solution.for dilution _5_CMST'): lacerationcounts, + get_item_code('Cetrimide 15% + chlorhexidine 1.5% solution.for dilution _5_CMST'): 500, } # check the number of suture kits required and request them @@ -4277,7 +4278,7 @@ def apply(self, person_id, squeeze_factor): # check if they have multiple burns, which implies a higher burned total body surface area (TBSA) which # will alter the treatment plan self.module.item_codes_for_consumables_required['burn_treatment'].update( - {get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 1} + {get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 4000} ) is_cons_available = self.get_consumables( @@ -4463,20 +4464,20 @@ def apply(self, person_id, squeeze_factor): description='Summary of the pain medicine requested by each person') if df.loc[person_id, 'age_years'] < 16: self.module.item_codes_for_consumables_required['pain_management'] = { - get_item_code("Paracetamol 500mg_1000_CMST"): 1 + get_item_code("Paracetamol 500mg_1000_CMST"): 8000 } cond = self.get_consumables( self.module.item_codes_for_consumables_required['pain_management'] ) else: self.module.item_codes_for_consumables_required['pain_management'] = { - get_item_code("diclofenac sodium 25 mg, enteric coated_1000_IDA"): 1 + get_item_code("diclofenac sodium 25 mg, enteric coated_1000_IDA"): 300 } cond1 = self.get_consumables( self.module.item_codes_for_consumables_required['pain_management'] ) self.module.item_codes_for_consumables_required['pain_management'] = { - get_item_code("Paracetamol 500mg_1000_CMST"): 1 + get_item_code("Paracetamol 500mg_1000_CMST"): 8000 } cond2 = self.get_consumables( self.module.item_codes_for_consumables_required['pain_management'] @@ -4533,7 +4534,7 @@ def apply(self, person_id, squeeze_factor): data=dict_to_output, description='Summary of the pain medicine requested by each person') self.module.item_codes_for_consumables_required['pain_management'] = { - get_item_code("tramadol HCl 100 mg/2 ml, for injection_100_IDA"): 1 + get_item_code("tramadol HCl 100 mg/2 ml, for injection_100_IDA"): 300 } is_cons_available = self.get_consumables( self.module.item_codes_for_consumables_required['pain_management'] @@ -4565,7 +4566,7 @@ def apply(self, person_id, squeeze_factor): description='Summary of the pain medicine requested by each person') # give morphine self.module.item_codes_for_consumables_required['pain_management'] = { - get_item_code("morphine sulphate 10 mg/ml, 1 ml, injection (nt)_10_IDA"): 1 + get_item_code("morphine sulphate 10 mg/ml, 1 ml, injection (nt)_10_IDA"): 120 } is_cons_available = self.get_consumables( self.module.item_codes_for_consumables_required['pain_management'] @@ -4725,22 +4726,22 @@ def apply(self, person_id, squeeze_factor): # Request first draft of consumables used in major surgery self.module.item_codes_for_consumables_required['major_surgery'] = { # request a general anaesthetic - get_item_code("Halothane (fluothane)_250ml_CMST"): 1, + get_item_code("Halothane (fluothane)_250ml_CMST"): 100, # clean the site of the surgery - get_item_code("Chlorhexidine 1.5% solution_5_CMST"): 1, + get_item_code("Chlorhexidine 1.5% solution_5_CMST"): 500, # tools to begin surgery get_item_code("Scalpel blade size 22 (individually wrapped)_100_CMST"): 1, # administer an IV get_item_code('Cannula iv (winged with injection pot) 18_each_CMST'): 1, get_item_code("Giving set iv administration + needle 15 drops/ml_each_CMST"): 1, - get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 1, + get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 2000, # repair incision made get_item_code("Suture pack"): 1, - get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 1, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 100, # administer pain killer - get_item_code('Pethidine, 50 mg/ml, 2 ml ampoule'): 1, + get_item_code('Pethidine, 50 mg/ml, 2 ml ampoule'): 6, # administer antibiotic - get_item_code("Ampicillin injection 500mg, PFR_each_CMST"): 1, + get_item_code("Ampicillin injection 500mg, PFR_each_CMST"): 1000, # equipment used by surgeon, gloves and facemask get_item_code('Disposables gloves, powder free, 100 pieces per box'): 1, get_item_code('surgical face mask, disp., with metal nose piece_50_IDA'): 1, @@ -5061,22 +5062,22 @@ def apply(self, person_id, squeeze_factor): # Request first draft of consumables used in major surgery self.module.item_codes_for_consumables_required['minor_surgery'] = { # request a local anaesthetic - get_item_code("Halothane (fluothane)_250ml_CMST"): 1, + get_item_code("Halothane (fluothane)_250ml_CMST"): 100, # clean the site of the surgery - get_item_code("Chlorhexidine 1.5% solution_5_CMST"): 1, + get_item_code("Chlorhexidine 1.5% solution_5_CMST"): 500, # tools to begin surgery get_item_code("Scalpel blade size 22 (individually wrapped)_100_CMST"): 1, # administer an IV get_item_code('Cannula iv (winged with injection pot) 18_each_CMST'): 1, get_item_code("Giving set iv administration + needle 15 drops/ml_each_CMST"): 1, - get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 1, + get_item_code("ringer's lactate (Hartmann's solution), 1000 ml_12_IDA"): 2000, # repair incision made get_item_code("Suture pack"): 1, - get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 1, + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"): 100, # administer pain killer - get_item_code('Pethidine, 50 mg/ml, 2 ml ampoule'): 1, + get_item_code('Pethidine, 50 mg/ml, 2 ml ampoule'): 6, # administer antibiotic - get_item_code("Ampicillin injection 500mg, PFR_each_CMST"): 1, + get_item_code("Ampicillin injection 500mg, PFR_each_CMST"): 1000, # equipment used by surgeon, gloves and facemask get_item_code('Disposables gloves, powder free, 100 pieces per box'): 1, get_item_code('surgical face mask, disp., with metal nose piece_50_IDA'): 1, diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index b769e60fb9..053469a253 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -743,6 +743,7 @@ def get_consumables_for_dx_and_tx(self): ) # 4) -------- Define the treatment options -------- + # treatment supplied as full kits for duration of treatment # adult treatment - primary self.item_codes_for_consumables_required['tb_tx_adult'] = \ hs.get_item_code_from_item_name("Cat. I & III Patient Kit A") @@ -760,12 +761,16 @@ def get_consumables_for_dx_and_tx(self): hs.get_item_code_from_item_name("Cat. II Patient Kit A2") # mdr treatment - self.item_codes_for_consumables_required['tb_mdrtx'] = { - hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1} + self.item_codes_for_consumables_required['tb_mdrtx'] = \ + hs.get_item_code_from_item_name("Treatment: second-line drugs") # ipt - self.item_codes_for_consumables_required['tb_ipt'] = { - hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} + self.item_codes_for_consumables_required['tb_ipt'] = \ + hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg") + + # 3hp + self.item_codes_for_consumables_required['tb_3HP'] = \ + hs.get_item_code_from_item_name("Isoniazid/Rifapentine") def initialise_population(self, population): @@ -2102,8 +2107,9 @@ def apply(self, person_id, squeeze_factor): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() treatment_regimen = self.select_treatment(person_id) + # treatment supplied in kits, one kit per treatment course treatment_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required[treatment_regimen] + item_codes={self.module.item_codes_for_consumables_required[treatment_regimen]: 1} ) # if require MDR treatment, and not currently at level 2, refer to level 2 @@ -2340,8 +2346,9 @@ class HSI_Tb_Start_or_Continue_Ipt(HSI_Event, IndividualScopeEventMixin): * HIV.HSI_Hiv_StartOrContinueTreatment for PLHIV, diagnosed and on ART * Tb.HSI_Tb_StartTreatment for up to 5 contacts of diagnosed active TB case - if person referred by ART initiation (HIV+), IPT given for 36 months - paediatric IPT is 6-9 months + Isoniazid preventive therapy for HIV-infected children : 6 months, 180 doses + 3HP (Isoniazid/Rifapentine) for adults: 12 weeks, 12 doses + 3HP for children ages >2 yrs hiv- """ def __init__(self, module, person_id): @@ -2381,10 +2388,23 @@ def apply(self, person_id, squeeze_factor): else: # Check/log use of consumables, and give IPT if available - # if not available, reschedule IPT start - if self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required["tb_ipt"] - ): + + # if child and HIV+ or child under 2 yrs + if ((person["age_years"] <= 15) and person["hv_inf"]) or (person["age_years"] <= 2): + + # 6 months dispensation, once daily + drugs_available = self.get_consumables( + item_codes={self.module.item_codes_for_consumables_required["tb_ipt"]: 180}) + + # for all others + else: + # 12 weeks dispensation, once weekly + drugs_available = self.get_consumables( + item_codes={self.module.item_codes_for_consumables_required["tb_3HP"]: 12} + ) + + # if available, schedule IPT decision + if drugs_available: # Update properties df.at[person_id, "tb_on_ipt"] = True df.at[person_id, "tb_date_ipt"] = self.sim.date diff --git a/tests/test_maternal_health_helper_and_analysis_functions.py b/tests/test_maternal_health_helper_and_analysis_functions.py index 83a93c224d..daea95a5e6 100644 --- a/tests/test_maternal_health_helper_and_analysis_functions.py +++ b/tests/test_maternal_health_helper_and_analysis_functions.py @@ -258,9 +258,9 @@ def test_analysis_events_force_availability_of_consumables_when_scheduled_in_anc syph_test = module.item_codes_preg_consumables['syphilis_test'] syph_treat = module.item_codes_preg_consumables['syphilis_treatment'] - for cons in iron, protein, calcium, syph_test, syph_treat: - sim.modules['HealthSystem'].override_availability_of_consumables( - {cons[0]: 0.0}) + for cons in 'iron_folic_acid', 'balanced_energy_protein', 'calcium', 'syphilis_test', 'syphilis_treatment': + updated_cons = {k: v * 0 for (k, v) in module.item_codes_preg_consumables[cons].items()} + sim.modules['HealthSystem'].override_availability_of_consumables(updated_cons) # refresh the consumables sim.modules['HealthSystem'].consumables._refresh_availability_of_consumables(date=sim.date)