From 0c7aa4169b8def292aa93f8f95d3a94e80110419 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 7 Oct 2024 13:38:49 +0100 Subject: [PATCH 01/27] created mnh_outcome_logger to eventually replace logging for complication incidence --- src/tlo/methods/labour.py | 19 +++ src/tlo/methods/newborn_outcomes.py | 12 ++ src/tlo/methods/postnatal_supervisor.py | 14 ++ src/tlo/methods/pregnancy_helper_functions.py | 78 +++++++++++ src/tlo/methods/pregnancy_supervisor.py | 126 +++++++++++++----- 5 files changed, 215 insertions(+), 34 deletions(-) diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index 35081b7d27..02fcd65630 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -1286,6 +1286,8 @@ def set_intrapartum_complications(self, individual_id, complication): 'type': f'{complication}', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[complication] += 1 + if complication == 'obstruction_cpd': mni[individual_id]['cpd'] = True @@ -1296,6 +1298,8 @@ def set_intrapartum_complications(self, individual_id, complication): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'placental_abruption', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['placental_abruption'] += 1 + elif complication == 'antepartum_haem': random_choice = self.rng.choice(['mild_moderate', 'severe'], @@ -1308,6 +1312,8 @@ def set_intrapartum_complications(self, individual_id, complication): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'mild_mod_antepartum_haemorrhage', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_mod_antepartum_haemorrhage'] += 1 + else: pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'severe_aph_onset', @@ -1315,6 +1321,7 @@ def set_intrapartum_complications(self, individual_id, complication): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'severe_antepartum_haemorrhage', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['severe_antepartum_haemorrhage'] += 1 elif complication == 'sepsis_chorioamnionitis': df.at[individual_id, 'la_sepsis'] = True @@ -1324,6 +1331,7 @@ def set_intrapartum_complications(self, individual_id, complication): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'sepsis', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_intrapartum'] += 1 elif complication == 'uterine_rupture': df.at[individual_id, 'la_uterine_rupture'] = True @@ -1332,6 +1340,7 @@ def set_intrapartum_complications(self, individual_id, complication): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'uterine_rupture', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['uterine_rupture'] += 1 def set_postpartum_complications(self, individual_id, complication): """ @@ -1387,6 +1396,7 @@ def set_postpartum_complications(self, individual_id, complication): logger_pn.info(key='maternal_complication', data={'person': individual_id, 'type': f'{complication}', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[complication] += 1 # Store mni variables used during treatment if complication == 'pph_uterine_atony': @@ -2427,6 +2437,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'early_preterm_labour', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['early_preterm_labour'] += 1 elif params['list_limits_for_defining_term_status'][4] <= gestational_age_in_days <= params['list_limits' '_for_defining' @@ -2438,6 +2449,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'late_preterm_labour', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['late_preterm_labour'] += 1 elif gestational_age_in_days >= params['list_limits_for_defining_term_status'][6]: @@ -2446,6 +2458,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'post_term_labour', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['post_term_labour'] += 1 labour_state = mni[individual_id]['labour_state'] @@ -2615,6 +2628,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'obstructed_labour', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['obstructed_labour'] += 1 # And we determine if any existing hypertensive disorders would worsen self.module.progression_of_hypertensive_disorders(individual_id, property_prefix='ps') @@ -2731,6 +2745,7 @@ def apply(self, individual_id): if df.at[individual_id, 'la_intrapartum_still_birth'] or mni[individual_id]['single_twin_still_birth']: logger.info(key='intrapartum_stillbirth', data={'mother_id': individual_id, 'date_of_ip_stillbirth': self.sim.date}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['intrapartum_stillbirth'] += 1 # Reset property if individual_id in mni: @@ -2821,10 +2836,13 @@ def apply(self, mother_id): logger_pn.info(key='maternal_complication', data={'person': mother_id, 'type': 'sepsis_postnatal', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_postnatal'] += 1 + if df.at[mother_id, 'la_postpartum_haem']: logger_pn.info(key='maternal_complication', data={'person': mother_id, 'type': 'primary_postpartum_haemorrhage', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['primary_postpartum_haemorrhage'] += 1 self.module.progression_of_hypertensive_disorders(mother_id, property_prefix='pn') @@ -2980,6 +2998,7 @@ def apply(self, person_id, squeeze_factor): logger.info(key='maternal_complication', data={'person': person_id, 'type': 'obstructed_labour', 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['obstructed_labour'] += 1 # ======================================= COMPLICATION MANAGEMENT ========================== # Next, women in labour are assessed for complications and treatment delivered if a need is identified and diff --git a/src/tlo/methods/newborn_outcomes.py b/src/tlo/methods/newborn_outcomes.py index 3691bc6003..b0e8d461bd 100644 --- a/src/tlo/methods/newborn_outcomes.py +++ b/src/tlo/methods/newborn_outcomes.py @@ -525,26 +525,31 @@ def apply_risk_of_congenital_anomaly(self, child_id): self.congeintal_anomalies.set(child_id, 'heart') logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'congenital_heart_anomaly'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['congenital_heart_anomaly'] += 1 if self.rng.random_sample() < params['prob_limb_musc_skeletal_anomaly']: self.congeintal_anomalies.set(child_id, 'limb_musc_skeletal') logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'limb_or_musculoskeletal_anomaly'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['limb_or_musculoskeletal_anomaly'] += 1 if self.rng.random_sample() < params['prob_urogenital_anomaly']: self.congeintal_anomalies.set(child_id, 'urogenital') logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'urogenital_anomaly'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['urogenital_anomaly'] += 1 if self.rng.random_sample() < params['prob_digestive_anomaly']: self.congeintal_anomalies.set(child_id, 'digestive') logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'digestive_anomaly'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['digestive_anomaly'] += 1 if self.rng.random_sample() < params['prob_other_anomaly']: self.congeintal_anomalies.set(child_id, 'other') logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'other_anomaly'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['other_anomaly'] += 1 def apply_risk_of_neonatal_infection_and_sepsis(self, child_id): """ @@ -561,6 +566,7 @@ def apply_risk_of_neonatal_infection_and_sepsis(self, child_id): logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'early_onset_sepsis'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['early_onset_sepsis'] += 1 def apply_risk_of_encephalopathy(self, child_id, timing): """ @@ -594,6 +600,7 @@ def apply_risk_of_encephalopathy(self, child_id, timing): logger.info(key='newborn_complication', data={'newborn': child_id, 'type': f'{df.at[child_id, "nb_encephalopathy"]}'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{df.at[child_id, "nb_encephalopathy"]}'] += 1 # Check all encephalopathy cases receive a grade if df.at[child_id, 'nb_encephalopathy'] == 'none': @@ -619,6 +626,7 @@ def apply_risk_of_preterm_respiratory_distress_syndrome(self, child_id): logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'respiratory_distress_syndrome'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['respiratory_distress_syndrome'] += 1 def apply_risk_of_not_breathing_at_birth(self, child_id): """ @@ -641,6 +649,7 @@ def apply_risk_of_not_breathing_at_birth(self, child_id): logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'not_breathing_at_birth'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['not_breathing_at_birth'] += 1 def scheduled_week_one_postnatal_event(self, individual_id): """ @@ -1158,13 +1167,16 @@ def on_birth(self, mother_id, child_id): (df.at[child_id, 'nb_low_birth_weight_status'] == 'very_low_birth_weight') or\ (df.at[child_id, 'nb_low_birth_weight_status'] == 'extremely_low_birth_weight'): logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'low_birth_weight'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['low_birth_weight'] += 1 elif df.at[child_id, 'nb_low_birth_weight_status'] == 'macrosomia': logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'macrosomia'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['macrosomia'] += 1 df.at[child_id, 'nb_size_for_gestational_age'] = mni[mother_id]['birth_size'] if df.at[child_id, 'nb_size_for_gestational_age'] == 'small_for_gestational_age': logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'small_for_gestational_age'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['small_for_gestational_age'] += 1 df.at[child_id, 'nb_early_init_breastfeeding'] = False df.at[child_id, 'nb_breastfeeding_status'] = 'none' diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 25bce6013f..428be94aaa 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -358,6 +358,7 @@ def further_on_birth_postnatal_supervisor(self, mother_id): logger.info(key='maternal_complication', data={'person': mother_id, 'type': f'{fistula_type}_fistula', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{fistula_type}_fistula'] += 1 # Determine if she will seek care for repair care_seeking_for_repair = self.pn_linear_models[ @@ -503,6 +504,7 @@ def onset(eq): logger.info(key='maternal_complication', data={'person': person, 'type': 'sepsis', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_postnatal'] += 1 # ------------------------------------ SECONDARY PPH ---------------------------------------------------------- # Next we determine if any women will experience postnatal bleeding @@ -518,6 +520,7 @@ def onset(eq): logger.info(key='maternal_complication', data={'person': person, 'type': 'secondary_postpartum_haemorrhage', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['secondary_postpartum_haemorrhage'] += 1 # --------------------------------------------- ANAEMIA -------------------------------------------------- # We apply a risk of anaemia developing in this week, and determine its severity @@ -597,6 +600,7 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': person, 'type': disease, 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[disease] += 1 if disease == 'severe_pre_eclamp': mni[person]['new_onset_spe'] = True @@ -642,6 +646,7 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': person, 'type': 'mild_pre_eclamp', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_pre_eclamp'] += 1 # -------------------------------- RISK OF GESTATIONAL HYPERTENSION -------------------------------------- gest_hypertension = self.apply_linear_model( @@ -655,6 +660,7 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': person, 'type': 'mild_gest_htn', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_gest_htn'] += 1 # -------------------------------- RISK OF DEATH SEVERE HYPERTENSION ------------------------------------------ # Risk of death is applied to women with severe hypertensive disease @@ -747,6 +753,7 @@ def apply_risk_of_neonatal_complications_in_week_one(self, child_id, mother_id): logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'early_onset_sepsis'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['early_onset_sepsis'] += 1 def set_postnatal_complications_neonates(self, upper_and_lower_day_limits): """ @@ -774,6 +781,7 @@ def set_postnatal_complications_neonates(self, upper_and_lower_day_limits): logger.info(key='newborn_complication', data={'newborn': person, 'type': 'late_onset_sepsis'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['late_onset_sepsis'] += 1 # Then we determine if care will be sought for newly septic newborns care_seeking = pd.Series( @@ -1030,6 +1038,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'sepsis', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_postnatal'] += 1 # Sepsis secondary to endometritis is stored within the mni as it is used as a predictor in a linear model if endo_result: @@ -1049,6 +1058,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'secondary_postpartum_haemorrhage', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['secondary_postpartum_haemorrhage'] += 1 # ------------------------------------------------ NEW ONSET ANAEMIA ------------------------------------------ # And then risk of developing anaemia... @@ -1117,6 +1127,8 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': person, 'type': disease, 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[disease] += 1 + if disease == 'severe_pre_eclamp': mni[person]['new_onset_spe'] = True @@ -1139,6 +1151,7 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'mild_pre_eclamp', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_pre_eclamp'] += 1 else: risk_gh_after_pregnancy = self.module.pn_linear_models['gest_htn_pn'].predict(df.loc[[ @@ -1149,6 +1162,7 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'mild_gest_htn', 'timing': 'postnatal'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_gest_htn'] += 1 # ====================================== POSTNATAL CHECK ================================================== # Import the HSI which represents postnatal care diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 8f7faa0503..4866d78ce7 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -8,6 +8,84 @@ from tlo import logging +def generate_mnh_outcome_counter(): + outcome_list = ['spontaneous_abortion', + 'induced_abortion', + 'complicated_spontaneous_abortion', + 'complicated_induced_abortion', + 'induced_abortion_injury', + 'induced_abortion_sepsis', + 'induced_abortion_haemorrhage', + 'induced_abortion_other_comp', + 'spontaneous_abortion_sepsis', + 'spontaneous_abortion_haemorrhage', + 'spontaneous_abortion_other_comp', + 'gest_diab', + 'clinical_chorioamnionitis', + 'antenatal_stillbirth', + 'ectopic_unruptured', + 'multiple_pregnancy', + 'placenta_praevia', + 'ectopic_ruptured', + 'syphilis', + 'placental_abruption', + 'severe_antepartum_haemorrhage', + 'mild_mod_antepartum_haemorrhage', + 'PROM' + 'obstruction_cpd', + 'obstruction_malpos_malpres', + 'obstruction_other', + 'obstructed_labour', + 'sepsis_intrapartum', + 'uterine_rupture', 'early_preterm_labour', 'late_preterm_labour', 'post_term_labour', + 'intrapartum_stillbirth', + 'spontaneous_abortion', + 'induced_abortion', + 'complicated_spontaneous_abortion', + 'complicated_induced_abortion', + 'induced_abortion_injury', + 'induced_abortion_sepsis', + 'induced_abortion_haemorrhage', + 'induced_abortion_other_comp', + 'spontaneous_abortion_sepsis', + 'spontaneous_abortion_haemorrhage', + 'spontaneous_abortion_other_comp', + 'gest_diab', + 'mild_pre_eclamp', + 'mild_gest_htn' + 'severe_pre_eclamp', + 'eclampsia', + 'severe_gest_htn', + 'placental_abruption', + 'severe_antepartum_haemorrhage', + 'mild_mod_antepartum_haemorrhage', + 'PROM' + 'clinical_chorioamnionitis', + 'antenatal_stillbirth', + 'ectopic_unruptured', + 'multiple_pregnancy', + 'placenta_praevia', + 'ectopic_ruptured', + 'syphilis', + 'obstruction_cpd', + 'obstruction_malpos_malpres', + 'obstruction_other', + 'obstructed_labour', + 'sepsis_intrapartum', + 'uterine_rupture', 'early_preterm_labour', 'late_preterm_labour', 'post_term_labour', + 'intrapartum_stillbirth', 'vesicovaginal_fistula', 'rectovaginal_fistula', + 'secondary_postpartum_haemorrhage', 'pph_uterine_atony', 'pph_retained_placenta', 'pph_other', + 'primary_postpartum_haemorrhage', 'sepsis_endometritis', 'sepsis_urinary_tract', + 'sepsis_skin_soft_tissue', 'sepsis_postnatal', 'congenital_heart_anomaly', 'limb_or_musculoskeletal_anomaly', + 'urogenital_anomaly', + 'digestive_anomaly', 'other_anomaly', 'mild_enceph', 'moderate_enceph', 'severe_enceph', + 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', 'macrosomia', + 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis', + ] + + mnh_outcome_counter = {k: 0 for k in outcome_list} + + return mnh_outcome_counter def get_list_of_items(self, item_list): """ diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 7dd8819ab6..e82eb71fad 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -62,6 +62,9 @@ def __init__(self, name=None, resourcefilepath=None): # This variable will store a Bitset handler for the property ps_abortion_complications self.abortion_complications = None + # Finally we create a dictionary to capture the frequency of key outcomes for logging + self.mnh_outcome_counter = pregnancy_helper_functions.generate_mnh_outcome_counter() + INIT_DEPENDENCIES = {'Demography'} OPTIONAL_INIT_DEPENDENCIES = {'HealthBurden', 'Malaria', 'CardioMetabolicDisorders', 'Hiv'} @@ -970,6 +973,7 @@ def do_after_abortion(self, individual_id, type_abortion): logger.info(key='maternal_complication', data={'person': individual_id, 'type': f'{type_abortion}', 'timing': 'antenatal'}) + self.mnh_outcome_counter[type_abortion] += 1 # This function officially ends a pregnancy through the contraception module (updates 'is_pregnant' and # determines post pregnancy contraception) @@ -1000,6 +1004,8 @@ def do_after_abortion(self, individual_id, type_abortion): 'type': f'complicated_{type_abortion}', 'timing': 'antenatal'}) + self.mnh_outcome_counter[f'complicated_{type_abortion}'] += 1 + self.apply_risk_of_abortion_complications(individual_id, f'{type_abortion}') def apply_risk_of_abortion_complications(self, individual_id, cause): @@ -1019,6 +1025,7 @@ def apply_risk_of_abortion_complications(self, individual_id, cause): logger.info(key='maternal_complication', data={'person': individual_id, 'type': f'{cause}_injury', 'timing': 'antenatal'}) + self.mnh_outcome_counter[f'{cause}_injury'] += 1 if self.rng.random_sample() < params['prob_haemorrhage_post_abortion']: self.abortion_complications.set([individual_id], 'haemorrhage') @@ -1027,6 +1034,7 @@ def apply_risk_of_abortion_complications(self, individual_id, cause): logger.info(key='maternal_complication', data={'person': individual_id, 'type': f'{cause}_haemorrhage', 'timing': 'antenatal'}) + self.mnh_outcome_counter[f'{cause}_haemorrhage'] += 1 if self.rng.random_sample() < params['prob_sepsis_post_abortion']: self.abortion_complications.set([individual_id], 'sepsis') @@ -1035,12 +1043,14 @@ def apply_risk_of_abortion_complications(self, individual_id, cause): logger.info(key='maternal_complication', data={'person': individual_id, 'type': f'{cause}_sepsis', 'timing': 'antenatal'}) + self.mnh_outcome_counter[f'{cause}_sepsis'] += 1 if not self.abortion_complications.has_any([individual_id], 'sepsis', 'haemorrhage', 'injury', first=True): self.abortion_complications.set([individual_id], 'other') logger.info(key='maternal_complication', data={'person': individual_id, 'type': f'{cause}_other_comp', 'timing': 'antenatal'}) + self.mnh_outcome_counter[f'{cause}_other_comp'] += 1 # We assume only women with complicated abortions will experience disability pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'abortion_onset', self.sim.date) @@ -1103,6 +1113,8 @@ def apply_risk_of_gestational_diabetes(self, gestation_of_interest): logger.info(key='maternal_complication', data={'person': person, 'type': 'gest_diab', 'timing': 'antenatal'}) + self.mnh_outcome_counter['gest_diab'] += 1 + def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): """ @@ -1127,6 +1139,7 @@ def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): logger.info(key='maternal_complication', data={'person': person, 'type': 'mild_pre_eclamp', 'timing': 'antenatal'}) + self.mnh_outcome_counter['mild_pre_eclamp'] += 1 # -------------------------------- RISK OF GESTATIONAL HYPERTENSION -------------------------------------- # For women who dont develop pre-eclampsia during this month, we apply a risk of gestational hypertension @@ -1141,6 +1154,7 @@ def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): logger.info(key='maternal_complication', data={'person': person, 'type': 'mild_gest_htn', 'timing': 'antenatal'}) + self.mnh_outcome_counter['mild_gest_htn'] += 1 def apply_risk_of_progression_of_hypertension(self, gestation_of_interest): """ @@ -1198,6 +1212,8 @@ def log_new_progressed_cases(disease): logger.info(key='maternal_complication', data={'person': person, 'type': disease, 'timing': 'antenatal'}) + self.mnh_outcome_counter[disease] +=1 + if disease == 'severe_pre_eclamp': self.mother_and_newborn_info[person]['new_onset_spe'] = True @@ -1273,6 +1289,7 @@ def apply_risk_of_placental_abruption(self, gestation_of_interest): logger.info(key='maternal_complication', data={'person': person, 'type': 'placental_abruption', 'timing': 'antenatal'}) + self.mnh_outcome_counter['placental_abruption'] += 1 def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): """ @@ -1314,6 +1331,7 @@ def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): logger.info(key='maternal_complication', data={'person': person, 'type': 'severe_antepartum_haemorrhage', 'timing': 'antenatal'}) + self.mnh_outcome_counter['severe_antepartum_haemorrhage'] += 1 non_severe_women = (df.loc[antepartum_haemorrhage.loc[antepartum_haemorrhage].index, 'ps_antepartum_haemorrhage'] != 'severe') @@ -1326,6 +1344,8 @@ def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): logger.info(key='maternal_complication', data={'person': person, 'type': 'mild_mod_antepartum_haemorrhage', 'timing': 'antenatal'}) + self.mnh_outcome_counter['mild_mod_antepartum_haemorrhage'] += 1 + def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gestation_of_interest): """ @@ -1354,6 +1374,7 @@ def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gest logger.info(key='maternal_complication', data={'person': person, 'type': 'PROM', 'timing': 'antenatal'}) + self.mnh_outcome_counter['PROM'] += 1 # Determine if those with PROM will develop infection prior to care seeking infection = pd.Series(self.rng.random_sample(len(prom.loc[prom])) < params['prob_chorioamnionitis'], @@ -1369,6 +1390,7 @@ def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gest logger.info(key='maternal_complication', data={'person': person, 'type': 'clinical_chorioamnionitis', 'timing': 'antenatal'}) + self.mnh_outcome_counter['clinical_chorioamnionitis'] += 1 def apply_risk_of_preterm_labour(self, gestation_of_interest): """ @@ -1433,6 +1455,7 @@ def update_variables_post_still_birth_for_data_frame(self, women): self.sim.modules['Contraception'].end_pregnancy(person) mni[person]['delete_mni'] = True logger.info(key='antenatal_stillbirth', data={'mother': person}) + self.mnh_outcome_counter['antenatal_stillbirth'] += 1 # Call functions across the modules to ensure properties are rest self.sim.modules['Labour'].reset_due_date(id_or_index=women.index, new_due_date=pd.NaT) @@ -1455,6 +1478,7 @@ def update_variables_post_still_birth_for_individual(self, individual_id): mni[individual_id]['delete_mni'] = True logger.info(key='antenatal_stillbirth', data={'mother': individual_id}) + self.mnh_outcome_counter['antenatal_stillbirth'] += 1 # Reset pregnancy and schedule possible update of contraception self.sim.modules['Contraception'].end_pregnancy(individual_id) @@ -1768,6 +1792,7 @@ def apply(self, population): logger.info(key='maternal_complication', data={'person': person, 'type': 'ectopic_unruptured', 'timing': 'antenatal'}) + self.module.mnh_outcome_counter['ectopic_unruptured'] += 1 self.sim.schedule_event(EctopicPregnancyEvent(self.module, person), (self.sim.date + pd.Timedelta(days=7 * 3 + self.module.rng.randint(0, 7 * 2)))) @@ -1787,6 +1812,7 @@ def apply(self, population): logger.info(key='maternal_complication', data={'person': person, 'type': 'multiple_pregnancy', 'timing': 'antenatal'}) + self.module.mnh_outcome_counter['multiple_pregnancy'] += 1 # -----------------------------APPLYING RISK OF PLACENTA PRAEVIA ------------------------------------------- # Next,we apply a one off risk of placenta praevia (placenta will grow to cover the cervix either partially or @@ -1801,6 +1827,7 @@ def apply(self, population): logger.info(key='maternal_complication', data={'person': person, 'type': 'placenta_praevia', 'timing': 'antenatal'}) + self.module.mnh_outcome_counter['placenta_praevia'] += 1 # ------------------------- APPLYING RISK OF SYPHILIS INFECTION DURING PREGNANCY --------------------------- # Finally apply risk that syphilis will develop during pregnancy @@ -1987,6 +2014,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'ectopic_ruptured', 'timing': 'antenatal'}) + self.module.mnh_outcome_counter['ectopic_ruptured'] += 1 # Set the variable df.at[individual_id, 'ps_ectopic_pregnancy'] = 'ruptured' @@ -2097,7 +2125,7 @@ def apply(self, individual_id): logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'syphilis', 'timing': 'antenatal'}) - + self.module.mnh_outcome_counter['syphilis'] += 1 class ParameterUpdateEvent(Event, PopulationScopeEventMixin): """This is ParameterUpdateEvent. It is scheduled to occur once on 2015 to update parameters being used by the @@ -2236,36 +2264,66 @@ def __init__(self, module): def apply(self, population): df = self.sim.population.props - women_reproductive_age = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - (df.age_years < 50))]) - pregnant_at_year_end = len(df.index[df.is_alive & df.is_pregnant]) - women_with_previous_sa = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - (df.age_years < 50) & df.ps_prev_spont_abortion)]) - women_with_previous_pe = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - (df.age_years < 50) & df.ps_prev_pre_eclamp)]) - women_with_hysterectomy = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - (df.age_years < 50) & df.la_has_had_hysterectomy)]) - - yearly_prev_sa = (women_with_previous_sa / women_reproductive_age) * 100 - yearly_prev_pe = (women_with_previous_pe / women_reproductive_age) * 100 - yearly_prev_hysterectomy = (women_with_hysterectomy / women_reproductive_age) * 100 - - parity_list = list() - for parity in [0, 1, 2, 3, 4, 5]: - if parity < 5: - par = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50) & - (df.la_parity == parity))]) - else: - par = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50) & - (df.la_parity >= parity))]) - - yearly_prev = (par / women_reproductive_age) * 100 - parity_list.append(yearly_prev) - - logger.info(key='preg_info', - data={'women_repro_age': women_reproductive_age, - 'women_pregnant': pregnant_at_year_end, - 'prev_sa': yearly_prev_sa, - 'prev_pe': yearly_prev_pe, - 'hysterectomy': yearly_prev_hysterectomy, - 'parity': parity_list}) + # Complication incidence + # Denominators + yrly_live_births = len(df.index[(df.date_of_birth.year == self.sim.date.year)]) + yrly_pregnancies = len(df.index[(df.date_of_last_pregnancy.year == self.sim.date.year)]) + yrly_comp_pregnancies = [] + + # # Lets only do purely antenatal stuff here, purely labour stuff in labour and everything else in postnatal/newborn + # + # ectopic_incidence = (self.module.mnh_outcome_counter[''] / yrly_pregnancies) * 1000 + # abortion_incidence = + # miscarriage_incidence = + + + + + # logger.info(key='an_comp_incidence', + # data={'women_repro_age': women_reproductive_age, + # 'women_pregnant': pregnant_at_year_end, + # 'prev_sa': yearly_prev_sa, + # 'prev_pe': yearly_prev_pe, + # 'hysterectomy': yearly_prev_hysterectomy, + # 'parity': parity_list} + # + # + # + # + # + # + # women_reproductive_age = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & + # (df.age_years < 50))]) + # pregnant_at_year_end = len(df.index[df.is_alive & df.is_pregnant]) + # women_with_previous_sa = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & + # (df.age_years < 50) & df.ps_prev_spont_abortion)]) + # women_with_previous_pe = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & + # (df.age_years < 50) & df.ps_prev_pre_eclamp)]) + # women_with_hysterectomy = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & + # (df.age_years < 50) & df.la_has_had_hysterectomy)]) + # + # yearly_prev_sa = (women_with_previous_sa / women_reproductive_age) * 100 + # yearly_prev_pe = (women_with_previous_pe / women_reproductive_age) * 100 + # yearly_prev_hysterectomy = (women_with_hysterectomy / women_reproductive_age) * 100 + # + # parity_list = list() + # for parity in [0, 1, 2, 3, 4, 5]: + # if parity < 5: + # par = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50) & + # (df.la_parity == parity))]) + # else: + # par = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50) & + # (df.la_parity >= parity))]) + # + # yearly_prev = (par / women_reproductive_age) * 100 + # parity_list.append(yearly_prev) + # + # logger.info(key='preg_info', + # data={'women_repro_age': women_reproductive_age, + # 'women_pregnant': pregnant_at_year_end, + # 'prev_sa': yearly_prev_sa, + # 'prev_pe': yearly_prev_pe, + # 'hysterectomy': yearly_prev_hysterectomy, + # 'parity': parity_list}) + + self.module.mnh_outcome_counter = {k:0 for k in self.module.outcome_list} From 87d6f1f98e91ed1309564c33f51b906599d7ec3c Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 7 Oct 2024 14:02:04 +0100 Subject: [PATCH 02/27] created mnh_outcome_logger to eventually replace logging for complication incidence --- src/tlo/methods/pregnancy_helper_functions.py | 98 +++++-------------- src/tlo/methods/pregnancy_supervisor.py | 8 +- 2 files changed, 29 insertions(+), 77 deletions(-) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 4866d78ce7..0eb75480ab 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -9,79 +9,31 @@ from tlo import logging def generate_mnh_outcome_counter(): - outcome_list = ['spontaneous_abortion', - 'induced_abortion', - 'complicated_spontaneous_abortion', - 'complicated_induced_abortion', - 'induced_abortion_injury', - 'induced_abortion_sepsis', - 'induced_abortion_haemorrhage', - 'induced_abortion_other_comp', - 'spontaneous_abortion_sepsis', - 'spontaneous_abortion_haemorrhage', - 'spontaneous_abortion_other_comp', - 'gest_diab', - 'clinical_chorioamnionitis', - 'antenatal_stillbirth', - 'ectopic_unruptured', - 'multiple_pregnancy', - 'placenta_praevia', - 'ectopic_ruptured', - 'syphilis', - 'placental_abruption', - 'severe_antepartum_haemorrhage', - 'mild_mod_antepartum_haemorrhage', - 'PROM' - 'obstruction_cpd', - 'obstruction_malpos_malpres', - 'obstruction_other', - 'obstructed_labour', - 'sepsis_intrapartum', - 'uterine_rupture', 'early_preterm_labour', 'late_preterm_labour', 'post_term_labour', - 'intrapartum_stillbirth', - 'spontaneous_abortion', - 'induced_abortion', - 'complicated_spontaneous_abortion', - 'complicated_induced_abortion', - 'induced_abortion_injury', - 'induced_abortion_sepsis', - 'induced_abortion_haemorrhage', - 'induced_abortion_other_comp', - 'spontaneous_abortion_sepsis', - 'spontaneous_abortion_haemorrhage', - 'spontaneous_abortion_other_comp', - 'gest_diab', - 'mild_pre_eclamp', - 'mild_gest_htn' - 'severe_pre_eclamp', - 'eclampsia', - 'severe_gest_htn', - 'placental_abruption', - 'severe_antepartum_haemorrhage', - 'mild_mod_antepartum_haemorrhage', - 'PROM' - 'clinical_chorioamnionitis', - 'antenatal_stillbirth', - 'ectopic_unruptured', - 'multiple_pregnancy', - 'placenta_praevia', - 'ectopic_ruptured', - 'syphilis', - 'obstruction_cpd', - 'obstruction_malpos_malpres', - 'obstruction_other', - 'obstructed_labour', - 'sepsis_intrapartum', - 'uterine_rupture', 'early_preterm_labour', 'late_preterm_labour', 'post_term_labour', - 'intrapartum_stillbirth', 'vesicovaginal_fistula', 'rectovaginal_fistula', - 'secondary_postpartum_haemorrhage', 'pph_uterine_atony', 'pph_retained_placenta', 'pph_other', - 'primary_postpartum_haemorrhage', 'sepsis_endometritis', 'sepsis_urinary_tract', - 'sepsis_skin_soft_tissue', 'sepsis_postnatal', 'congenital_heart_anomaly', 'limb_or_musculoskeletal_anomaly', - 'urogenital_anomaly', - 'digestive_anomaly', 'other_anomaly', 'mild_enceph', 'moderate_enceph', 'severe_enceph', - 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', 'macrosomia', - 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis', - ] + outcome_list = [ # early/abortive outcomes + 'ectopic_unruptured', 'ectopic_ruptured','multiple_pregnancy', 'placenta_praevia', + 'spontaneous_abortion', 'induced_abortion', 'complicated_spontaneous_abortion', + 'complicated_induced_abortion', 'induced_abortion_injury', 'induced_abortion_sepsis', + 'induced_abortion_haemorrhage','induced_abortion_other_comp','spontaneous_abortion_sepsis', + 'spontaneous_abortion_haemorrhage', 'spontaneous_abortion_other_comp', + + # antenatal onset outcomes + 'gest_diab', 'mild_pre_eclamp', 'mild_gest_htn','severe_pre_eclamp', 'eclampsia','severe_gest_htn', + 'syphilis', 'PROM', 'clinical_chorioamnionitis', 'placental_abruption', + 'mild_mod_antepartum_haemorrhage','severe_antepartum_haemorrhage', 'antenatal_stillbirth', + + # intrapartum/postpartum onset outcomes + 'obstruction_cpd', 'obstruction_malpos_malpres', 'obstruction_other','obstructed_labour', + 'uterine_rupture','sepsis_intrapartum','sepsis_endometritis', 'sepsis_urinary_tract', + 'sepsis_skin_soft_tissue', 'sepsis_postnatal', 'intrapartum_stillbirth', 'early_preterm_labour', + 'late_preterm_labour', 'post_term_labour', 'pph_uterine_atony', 'pph_retained_placenta', + 'pph_other', 'primary_postpartum_haemorrhage', 'secondary_postpartum_haemorrhage', + 'vesicovaginal_fistula', 'rectovaginal_fistula', + + # newborn outcomes + 'congenital_heart_anomaly', 'limb_or_musculoskeletal_anomaly', 'urogenital_anomaly', + 'digestive_anomaly', 'other_anomaly', 'mild_enceph', 'moderate_enceph', + 'severe_enceph', 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', + 'macrosomia', 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis'] mnh_outcome_counter = {k: 0 for k in outcome_list} diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index e82eb71fad..8a13d62164 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -2266,9 +2266,9 @@ def apply(self, population): # Complication incidence # Denominators - yrly_live_births = len(df.index[(df.date_of_birth.year == self.sim.date.year)]) - yrly_pregnancies = len(df.index[(df.date_of_last_pregnancy.year == self.sim.date.year)]) - yrly_comp_pregnancies = [] + # yrly_live_births = len(df.index[(df.date_of_birth.year == self.sim.date.year)]) + # yrly_pregnancies = len(df.index[(df.date_of_last_pregnancy.year == self.sim.date.year)]) + # yrly_comp_pregnancies = [] # # Lets only do purely antenatal stuff here, purely labour stuff in labour and everything else in postnatal/newborn # @@ -2326,4 +2326,4 @@ def apply(self, population): # 'hysterectomy': yearly_prev_hysterectomy, # 'parity': parity_list}) - self.module.mnh_outcome_counter = {k:0 for k in self.module.outcome_list} + # self.module.mnh_outcome_counter = {k:0 for k in self.module.outcome_list} From cdd98420615dba156a37e36ef484058821008963 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 7 Oct 2024 16:16:05 +0100 Subject: [PATCH 03/27] added first logging to pregnancy supervisor --- src/tlo/methods/pregnancy_helper_functions.py | 3 +- src/tlo/methods/pregnancy_supervisor.py | 71 ++++++++++++++++--- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 0eb75480ab..93607873df 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -37,7 +37,8 @@ def generate_mnh_outcome_counter(): mnh_outcome_counter = {k: 0 for k in outcome_list} - return mnh_outcome_counter + return {'counter': mnh_outcome_counter, + 'outcomes': outcome_list} def get_list_of_items(self, item_list): """ diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 8a13d62164..34f52e6271 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -63,7 +63,8 @@ def __init__(self, name=None, resourcefilepath=None): self.abortion_complications = None # Finally we create a dictionary to capture the frequency of key outcomes for logging - self.mnh_outcome_counter = pregnancy_helper_functions.generate_mnh_outcome_counter() + mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() + self.mnh_outcome_counter = mnh_oc['counter'] INIT_DEPENDENCIES = {'Demography'} @@ -2263,18 +2264,66 @@ def __init__(self, module): def apply(self, population): df = self.sim.population.props + counter = self.module.mnh_outcome_counter # Complication incidence # Denominators - # yrly_live_births = len(df.index[(df.date_of_birth.year == self.sim.date.year)]) - # yrly_pregnancies = len(df.index[(df.date_of_last_pregnancy.year == self.sim.date.year)]) - # yrly_comp_pregnancies = [] + yrly_live_births = len(df[(df['date_of_birth'].dt.year == self.sim.date.year - 1) & (df['mother_id'] >= 0)]) + yrly_pregnancies =len(df[df['date_of_last_pregnancy'].dt.year == self.sim.date.year - 1]) - # # Lets only do purely antenatal stuff here, purely labour stuff in labour and everything else in postnatal/newborn - # - # ectopic_incidence = (self.module.mnh_outcome_counter[''] / yrly_pregnancies) * 1000 - # abortion_incidence = - # miscarriage_incidence = + yrly_comp_pregnancies = (counter['ectopic_unruptured'] + counter['spontaneous_abortion'] + + counter['induced_abortion'] + counter['antenatal_stillbirth'] + + counter['intrapartum_stillbirth'] + yrly_live_births) + + yrly_total_births = yrly_live_births + counter['antenatal_stillbirth'] + counter['intrapartum_stillbirth'] + + logger.info(key='yrl_counter_dict', + data=counter) + + # MATERNAL COMPLICATIONS + logger.info(key='mat_comp_incidence', + data= {k:(counter[k]/denom) * 1000 for k, denom in + zip(['ectopic_unruptured', 'induced_abortion', 'spontaneous_abortion', + 'placenta_praevia', 'gest_diab', 'syphilis'], + [yrly_pregnancies, yrly_comp_pregnancies, yrly_comp_pregnancies, yrly_pregnancies, + yrly_comp_pregnancies, yrly_comp_pregnancies,])}) + + logger.info(key='mat_comp_incidence', + data={k: (counter[k] / yrly_live_births) * 1000 for k in + ['obstructed_labour', 'uterine_rupture', 'sepsis_intrapartum', + 'mild_mod_antepartum_haemorrhage', 'severe_antepartum_haemorrhage', + 'mild_pre_eclamp', 'mild_gest_htn', 'mild_gest_htn', 'severe_pre_eclamp', + 'severe_gest_htn', 'eclampsia', 'sepsis_postnatal', 'primary_postpartum_haemorrhage', + 'secondary_postpartum_haemorrhage', 'vesicovaginal_fistula', 'rectovaginal_fistula']}) + + logger.info(key='mat_comp_incidence', + data={'antepartum_haemorrhage': ((counter['mild_mod_antepartum_haemorrhage'] + + counter['severe_antepartum_haemorrhage']) / + yrly_live_births) * 1000}) + + logger.info(key='mat_comp_incidence', + data={k: (counter[k] / yrly_live_births) * 100 for k in + ['early_preterm_labour', 'late_preterm_labour', 'post_term_labour']}) + + # NEWBORN COMPLICATIONS + logger.info(key='nb_incidence', + data={k: (counter[k] / yrly_live_births) * 1000 for k in + ['congenital_heart_anomaly', 'limb_or_musculoskeletal_anomaly', 'urogenital_anomaly', + 'digestive_anomaly', 'other_anomaly', 'mild_enceph', 'moderate_enceph', 'severe_enceph', + 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', 'macrosomia', + 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis']}) + + + neonatal_deaths = len(df[(df['date_of_death'].dt.year == self.sim.date.year - 1) & (df['age_days'] <= 28)]) + direct_maternal_deaths = [] + + # DIRECT MATERNAL DEATHS, NEWBORN DEATHS AND STILLBIRTH + logger.info(key='deaths_and_stillbirths', + data={'antenatal_stillbirth': (counter['antenatal_stillbirth'] / yrly_total_births) * 1000, + 'intrapartum_stillbirth': (counter['intrapartum_stillbirth'] / yrly_total_births) * 1000, + 'neonatal_deaths': neonatal_deaths, + 'nmr' : (neonatal_deaths/yrly_live_births) * 1000, + 'direct_maternal_deaths': []}) @@ -2326,4 +2375,6 @@ def apply(self, population): # 'hysterectomy': yearly_prev_hysterectomy, # 'parity': parity_list}) - # self.module.mnh_outcome_counter = {k:0 for k in self.module.outcome_list} + mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() + outcome_list = mnh_oc['outcomes'] + self.module.mnh_outcome_counter = {k:0 for k in outcome_list} From b9a193dafd32a8544eb8cc463175433cbccdab86 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 10:23:36 +0100 Subject: [PATCH 04/27] finalising logging event --- src/tlo/methods/labour.py | 4 + src/tlo/methods/newborn_outcomes.py | 1 + src/tlo/methods/postnatal_supervisor.py | 8 + src/tlo/methods/pregnancy_helper_functions.py | 11 +- src/tlo/methods/pregnancy_supervisor.py | 164 ++++++++---------- 5 files changed, 91 insertions(+), 97 deletions(-) diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index 02fcd65630..21bc602d01 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -1529,9 +1529,12 @@ def apply_risk_of_early_postpartum_death(self, individual_id): # If a cause is returned death is scheduled if potential_cause_of_death: pregnancy_helper_functions.log_mni_for_maternal_death(self, individual_id) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['direct_mat_death'] += 1 + self.sim.modules['Demography'].do_death(individual_id=individual_id, cause=potential_cause_of_death, originating_module=self.sim.modules['Labour']) + # If she hasn't died from any complications, we reset some key properties that resolve after risk of death # has been applied else: @@ -2700,6 +2703,7 @@ def apply(self, individual_id): # If a cause is returned death is scheduled if potential_cause_of_death: pregnancy_helper_functions.log_mni_for_maternal_death(self.module, individual_id) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['direct_mat_death'] += 1 self.sim.modules['Demography'].do_death(individual_id=individual_id, cause=potential_cause_of_death, originating_module=self.sim.modules['Labour']) diff --git a/src/tlo/methods/newborn_outcomes.py b/src/tlo/methods/newborn_outcomes.py index b0e8d461bd..4019544bd4 100644 --- a/src/tlo/methods/newborn_outcomes.py +++ b/src/tlo/methods/newborn_outcomes.py @@ -1030,6 +1030,7 @@ def link_twins(self, child_one, child_two, mother_id): 'date_of_delivery': self.sim.date} logger.info(key='twin_birth', data=twin_birth, description='A record of each birth of twin pairs') + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['twin_birth'] += 1 # Finally we log the second live birth and add another to the womans parity df.at[mother_id, 'la_parity'] += 1 diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 428be94aaa..ef81b3b8a5 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -838,6 +838,7 @@ def apply_risk_of_maternal_or_neonatal_death_postnatal(self, mother_or_child, in if potential_cause_of_death: mni[individual_id]['didnt_seek_care'] = True pregnancy_helper_functions.log_mni_for_maternal_death(self, individual_id) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['direct_mat_death'] += 1 self.sim.modules['Demography'].do_death(individual_id=individual_id, cause=potential_cause_of_death, originating_module=self.sim.modules['PostnatalSupervisor']) del mni[individual_id] @@ -950,6 +951,13 @@ def apply(self, population): # Set mni[person]['delete_mni'] to True meaning after the next DALY event each womans MNI dict is deleted for person in week_8_postnatal_women.loc[week_8_postnatal_women].index: mni[person]['delete_mni'] = True + + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['six_week_survivors'] += 1 + + if df.at[person, 'pn_anaemia_following_pregnancy'] != 'none': + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[ + f'pn_anaemia_{df.at[person, "pn_anaemia_following_pregnancy"]}'] += 1 + logger.info(key='total_mat_pnc_visits', data={'mother': person, 'visits': df.at[person, 'la_pn_checks_maternal'], 'anaemia': df.at[person, 'pn_anaemia_following_pregnancy']}) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 93607873df..ad3237328f 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -10,13 +10,14 @@ def generate_mnh_outcome_counter(): outcome_list = [ # early/abortive outcomes - 'ectopic_unruptured', 'ectopic_ruptured','multiple_pregnancy', 'placenta_praevia', + 'ectopic_unruptured', 'ectopic_ruptured','multiple_pregnancy', 'twin_birth', 'placenta_praevia', 'spontaneous_abortion', 'induced_abortion', 'complicated_spontaneous_abortion', 'complicated_induced_abortion', 'induced_abortion_injury', 'induced_abortion_sepsis', 'induced_abortion_haemorrhage','induced_abortion_other_comp','spontaneous_abortion_sepsis', 'spontaneous_abortion_haemorrhage', 'spontaneous_abortion_other_comp', # antenatal onset outcomes + 'an_anaemia_mild', 'an_anaemia_moderate', 'an_anaemia_severe', 'gest_diab', 'mild_pre_eclamp', 'mild_gest_htn','severe_pre_eclamp', 'eclampsia','severe_gest_htn', 'syphilis', 'PROM', 'clinical_chorioamnionitis', 'placental_abruption', 'mild_mod_antepartum_haemorrhage','severe_antepartum_haemorrhage', 'antenatal_stillbirth', @@ -27,13 +28,17 @@ def generate_mnh_outcome_counter(): 'sepsis_skin_soft_tissue', 'sepsis_postnatal', 'intrapartum_stillbirth', 'early_preterm_labour', 'late_preterm_labour', 'post_term_labour', 'pph_uterine_atony', 'pph_retained_placenta', 'pph_other', 'primary_postpartum_haemorrhage', 'secondary_postpartum_haemorrhage', - 'vesicovaginal_fistula', 'rectovaginal_fistula', + 'vesicovaginal_fistula', 'rectovaginal_fistula', 'pn_anaemia_mild', 'pn_anaemia_moderate', + 'pn_anaemia_severe', # newborn outcomes 'congenital_heart_anomaly', 'limb_or_musculoskeletal_anomaly', 'urogenital_anomaly', 'digestive_anomaly', 'other_anomaly', 'mild_enceph', 'moderate_enceph', 'severe_enceph', 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', - 'macrosomia', 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis'] + 'macrosomia', 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis', + + 'direct_mat_death', 'six_week_survivors' + ] mnh_outcome_counter = {k: 0 for k in outcome_list} diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 34f52e6271..671a6af07a 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -658,6 +658,9 @@ def further_on_birth_pregnancy_supervisor(self, mother_id): df.at[mother_id, 'ps_date_of_anc1'] = pd.NaT # And store her anaemia status to calculate the prevalence of anaemia on birth + if df.at[mother_id, 'ps_anaemia_in_pregnancy'] != 'none': + self.mnh_outcome_counter[f'an_anaemia_{df.at[mother_id, "ps_anaemia_in_pregnancy"]}'] += 1 + logger.info(key='conditions_on_birth', data={'mother': mother_id, 'anaemia_status': df.at[mother_id, 'ps_anaemia_in_pregnancy'], 'gdm_status': df.at[mother_id, 'ps_gest_diab'], @@ -1609,6 +1612,7 @@ def apply_risk_of_death_from_monthly_complications(self, individual_id): pregnancy_helper_functions.log_mni_for_maternal_death(self, individual_id) self.sim.modules['Demography'].do_death(individual_id=individual_id, cause=potential_cause_of_death, originating_module=self.sim.modules['PregnancySupervisor']) + self.mnh_outcome_counter['direct_mat_death'] += 1 del mni[individual_id] # If not we reset variables and the woman survives @@ -2264,116 +2268,88 @@ def __init__(self, module): def apply(self, population): df = self.sim.population.props - counter = self.module.mnh_outcome_counter + c = self.module.mnh_outcome_counter - # Complication incidence - # Denominators - yrly_live_births = len(df[(df['date_of_birth'].dt.year == self.sim.date.year - 1) & (df['mother_id'] >= 0)]) - yrly_pregnancies =len(df[df['date_of_last_pregnancy'].dt.year == self.sim.date.year - 1]) + logger.info(key='yrly_counter_dict', data=c) - yrly_comp_pregnancies = (counter['ectopic_unruptured'] + counter['spontaneous_abortion'] + - counter['induced_abortion'] + counter['antenatal_stillbirth'] + - counter['intrapartum_stillbirth'] + yrly_live_births) + def rate (count, denom, multiplier): + return (count/denom) * multiplier - yrly_total_births = yrly_live_births + counter['antenatal_stillbirth'] + counter['intrapartum_stillbirth'] + # DENOMINATORS + live_births = len(df[(df['date_of_birth'].dt.year == self.sim.date.year - 1) & (df['mother_id'] >= 0)]) - logger.info(key='yrl_counter_dict', - data=counter) + pregnancies =len(df[df['date_of_last_pregnancy'].dt.year == self.sim.date.year - 1]) - # MATERNAL COMPLICATIONS - logger.info(key='mat_comp_incidence', - data= {k:(counter[k]/denom) * 1000 for k, denom in - zip(['ectopic_unruptured', 'induced_abortion', 'spontaneous_abortion', - 'placenta_praevia', 'gest_diab', 'syphilis'], - [yrly_pregnancies, yrly_comp_pregnancies, yrly_comp_pregnancies, yrly_pregnancies, - yrly_comp_pregnancies, yrly_comp_pregnancies,])}) + comp_pregnancies = (c['ectopic_unruptured'] + c['spontaneous_abortion'] + + c['induced_abortion'] + c['antenatal_stillbirth'] + + c['intrapartum_stillbirth'] + live_births) - logger.info(key='mat_comp_incidence', - data={k: (counter[k] / yrly_live_births) * 1000 for k in - ['obstructed_labour', 'uterine_rupture', 'sepsis_intrapartum', - 'mild_mod_antepartum_haemorrhage', 'severe_antepartum_haemorrhage', - 'mild_pre_eclamp', 'mild_gest_htn', 'mild_gest_htn', 'severe_pre_eclamp', - 'severe_gest_htn', 'eclampsia', 'sepsis_postnatal', 'primary_postpartum_haemorrhage', - 'secondary_postpartum_haemorrhage', 'vesicovaginal_fistula', 'rectovaginal_fistula']}) + deliveries = live_births - c['twin_birth'] - logger.info(key='mat_comp_incidence', - data={'antepartum_haemorrhage': ((counter['mild_mod_antepartum_haemorrhage'] + - counter['severe_antepartum_haemorrhage']) / - yrly_live_births) * 1000}) + total_births = live_births + c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] + + # MATERNAL COMPLICATION INCIDENCE + total_an_anaemia_cases = c['an_anaemia_mild'] + c['an_anaemia_moderate'] + c['an_anaemia_severe'] + total_pn_anaemia_cases = c['pn_anaemia_mild'] + c['pn_anaemia_moderate'] + c['pn_anaemia_severe'] + total_preterm_birth = c['early_preterm_labour'] + c['late_preterm_labour'] + total_aph = c['mild_mod_antepartum_haemorrhage'] + c['severe_antepartum_haemorrhage'] + total_sepsis = c['clinical_chorioamnionitis'] + c['sepsis_intrapartum'] + c['sepsis_postnatal'] + total_pph = c['primary_postpartum_haemorrhage'] + c['secondary_postpartum_haemorrhage'] + total_fistula = c['vesicovaginal_fistula'] + c['rectovaginal_fistula'] + total_neo_sepsis = c['early_onset_sepsis'] + c['late_onset_sepsis'] + total_neo_enceph = c['mild_enceph'] + c['moderate_enceph'] + c['severe_enceph'] + total_neo_resp_conds = c['respiratory_distress_syndrome'] + c['not_breathing_at_birth'] + total_neo_enceph + total_cba = (c['congenital_heart_anomaly'] + c['limb_or_musculoskeletal_anomaly'] + + c['urogenital_anomaly'] + c['digestive_anomaly'] + c['other_anomaly']) logger.info(key='mat_comp_incidence', - data={k: (counter[k] / yrly_live_births) * 100 for k in - ['early_preterm_labour', 'late_preterm_labour', 'post_term_labour']}) + data={'an_anaemia': rate(total_an_anaemia_cases, live_births, 100), + 'ectopic_unruptured' : rate(c['ectopic_unruptured'], pregnancies, 1000), + 'induced_abortion' : rate(c['induced_abortion'], comp_pregnancies, 1000), + 'spontaneous_abortion': rate(c['spontaneous_abortion'], comp_pregnancies, 1000), + 'placenta_praevia': rate(c['placenta_praevia'], pregnancies, 1000), + 'gest_diab': rate(c['gest_diab'], comp_pregnancies, 1000), + 'PROM': rate(c['PROM'], comp_pregnancies, 1000), + 'preterm_birth': rate(total_preterm_birth, live_births, 100), + 'antepartum_haem': rate(total_aph, live_births, 1000), + 'obstructed_labour': rate(c['obstructed_labour'], live_births, 1000), + 'uterine_rupture': rate(c['uterine_rupture'], live_births, 1000), + 'sepsis': rate(total_sepsis, live_births, 1000), + 'mild_pre_eclamp': rate(c['mild_pre_eclamp'], live_births, 1000), + 'mild_gest_htn': rate(c['mild_gest_htn'], live_births, 1000), + 'severe_pre_eclamp': rate(c['severe_pre_eclamp'], live_births, 1000), + 'severe_gest_htn': rate(c['severe_gest_htn'], live_births, 1000), + 'eclampsia': rate(c['eclampsia'], live_births, 1000), + 'postpartum_haem': rate(total_pph, live_births, 1000), + 'fistula': rate(total_fistula, live_births, 1000), + 'pn_anaemia': rate(total_pn_anaemia_cases, c['six_week_survivors'], 100)}) # NEWBORN COMPLICATIONS - logger.info(key='nb_incidence', - data={k: (counter[k] / yrly_live_births) * 1000 for k in - ['congenital_heart_anomaly', 'limb_or_musculoskeletal_anomaly', 'urogenital_anomaly', - 'digestive_anomaly', 'other_anomaly', 'mild_enceph', 'moderate_enceph', 'severe_enceph', - 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', 'macrosomia', - 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis']}) - + logger.info(key='nb_comp_incidence', + data={'twin_birth': rate(c['twin_birth'], deliveries, 100), + 'nb_sepsis': rate(total_neo_sepsis, live_births, 1000), + 'nb_enceph': rate(total_neo_enceph, live_births, 1000), + 'nb_resp_diff': rate(total_neo_resp_conds, live_births, 100), + 'nb_cba': rate(total_cba, live_births, 1000), + 'nb_rds': rate(c['respiratory_distress_syndrome'], total_preterm_birth, 1000), + 'nb_lbw': rate(c['low_birth_weight'], live_births, 100), + 'nb_macrosomia': rate(c['macrosomia'], live_births, 100), + 'nb_sga': rate(c['small_for_gestational_age'], live_births, 100)}) + # DIRECT MATERNAL DEATHS, NEWBORN DEATHS AND STILLBIRTH neonatal_deaths = len(df[(df['date_of_death'].dt.year == self.sim.date.year - 1) & (df['age_days'] <= 28)]) - direct_maternal_deaths = [] + stillbirths = c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] - # DIRECT MATERNAL DEATHS, NEWBORN DEATHS AND STILLBIRTH logger.info(key='deaths_and_stillbirths', - data={'antenatal_stillbirth': (counter['antenatal_stillbirth'] / yrly_total_births) * 1000, - 'intrapartum_stillbirth': (counter['intrapartum_stillbirth'] / yrly_total_births) * 1000, + data={'antenatal_sbr': rate(c['antenatal_stillbirth'], total_births, 1000), + 'intrapartum_sbr': rate(c['intrapartum_stillbirth'], total_births, 1000), + 'total_stillbirths': stillbirths, + 'sbr': rate(stillbirths, total_births, 1000), 'neonatal_deaths': neonatal_deaths, - 'nmr' : (neonatal_deaths/yrly_live_births) * 1000, - 'direct_maternal_deaths': []}) - - - - - # logger.info(key='an_comp_incidence', - # data={'women_repro_age': women_reproductive_age, - # 'women_pregnant': pregnant_at_year_end, - # 'prev_sa': yearly_prev_sa, - # 'prev_pe': yearly_prev_pe, - # 'hysterectomy': yearly_prev_hysterectomy, - # 'parity': parity_list} - # - # - # - # - # - # - # women_reproductive_age = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - # (df.age_years < 50))]) - # pregnant_at_year_end = len(df.index[df.is_alive & df.is_pregnant]) - # women_with_previous_sa = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - # (df.age_years < 50) & df.ps_prev_spont_abortion)]) - # women_with_previous_pe = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - # (df.age_years < 50) & df.ps_prev_pre_eclamp)]) - # women_with_hysterectomy = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & - # (df.age_years < 50) & df.la_has_had_hysterectomy)]) - # - # yearly_prev_sa = (women_with_previous_sa / women_reproductive_age) * 100 - # yearly_prev_pe = (women_with_previous_pe / women_reproductive_age) * 100 - # yearly_prev_hysterectomy = (women_with_hysterectomy / women_reproductive_age) * 100 - # - # parity_list = list() - # for parity in [0, 1, 2, 3, 4, 5]: - # if parity < 5: - # par = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50) & - # (df.la_parity == parity))]) - # else: - # par = len(df.index[(df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50) & - # (df.la_parity >= parity))]) - # - # yearly_prev = (par / women_reproductive_age) * 100 - # parity_list.append(yearly_prev) - # - # logger.info(key='preg_info', - # data={'women_repro_age': women_reproductive_age, - # 'women_pregnant': pregnant_at_year_end, - # 'prev_sa': yearly_prev_sa, - # 'prev_pe': yearly_prev_pe, - # 'hysterectomy': yearly_prev_hysterectomy, - # 'parity': parity_list}) + 'nmr' : rate(neonatal_deaths, live_births, 1000), + 'direct_maternal_deaths': c['direct_mat_death'], + 'direct_mmr': rate(c['direct_mat_death'], live_births, 100_000), + }) mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() outcome_list = mnh_oc['outcomes'] From ada05e089db947e31b5e85531bde429e839deb07 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 12:58:21 +0100 Subject: [PATCH 05/27] add health system logging --- .../methods/care_of_women_during_pregnancy.py | 9 ++++++- src/tlo/methods/labour.py | 3 +++ src/tlo/methods/postnatal_supervisor.py | 18 +++++++++++-- src/tlo/methods/pregnancy_helper_functions.py | 9 ++++++- src/tlo/methods/pregnancy_supervisor.py | 25 +++++++++++++++++-- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/tlo/methods/care_of_women_during_pregnancy.py b/src/tlo/methods/care_of_women_during_pregnancy.py index 69ce038299..b2df6c777b 100644 --- a/src/tlo/methods/care_of_women_during_pregnancy.py +++ b/src/tlo/methods/care_of_women_during_pregnancy.py @@ -484,8 +484,10 @@ def further_on_birth_care_of_women_in_pregnancy(self, mother_id): if df.at[mother_id, 'is_alive']: + anc_count = df.at[mother_id, "ac_total_anc_visits_current_pregnancy"] + # run a check at birth to make sure no women exceed 8 visits - if df.at[mother_id, 'ac_total_anc_visits_current_pregnancy'] > 9: + if anc_count > 9: logger.info(key='error', data=f'Mother {mother_id} attended >8 ANC visits during her pregnancy') # We log the total number of ANC contacts a woman has undergone at the time of birth via this dictionary @@ -494,6 +496,11 @@ def further_on_birth_care_of_women_in_pregnancy(self, mother_id): else: ga_anc_one = 0.0 + if anc_count > 8: + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['anc8+'] += 1 + else: + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'anc{anc_count}'] += 1 + total_anc_visit_count = {'person_id': mother_id, 'total_anc': df.at[mother_id, 'ac_total_anc_visits_current_pregnancy'], 'ga_anc_one': ga_anc_one} diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index 21bc602d01..d84c41c1e0 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -1060,6 +1060,9 @@ def further_on_birth_labour(self, mother_id): 'facility_type': str(mni[mother_id]['delivery_setting']), 'mode': mni[mother_id]['mode_of_delivery']}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[ + f'{str(mni[mother_id]["delivery_setting"])}_delivery'] += 1 + # Store only live births to a mother parity if not df.at[mother_id, 'la_intrapartum_still_birth']: df.at[mother_id, 'la_parity'] += 1 # Only live births contribute to parity diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index ef81b3b8a5..5ba48a0e24 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -895,6 +895,7 @@ def apply(self, population): df = population.props store_dalys_in_mni = pregnancy_helper_functions.store_dalys_in_mni mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info + mnh_counter = self.sim.modules['PregnancySupervisor'].mnh_outcome_counter # ================================ UPDATING LENGTH OF POSTPARTUM PERIOD IN WEEKS ============================ # Here we update how far into the postpartum period each woman who has recently delivered is @@ -950,13 +951,19 @@ def apply(self, population): # Set mni[person]['delete_mni'] to True meaning after the next DALY event each womans MNI dict is deleted for person in week_8_postnatal_women.loc[week_8_postnatal_women].index: + pn_checks = df.at[person, 'la_pn_checks_maternal'] + mni[person]['delete_mni'] = True self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['six_week_survivors'] += 1 if df.at[person, 'pn_anaemia_following_pregnancy'] != 'none': - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[ - f'pn_anaemia_{df.at[person, "pn_anaemia_following_pregnancy"]}'] += 1 + mnh_counter[f'pn_anaemia_{df.at[person, "pn_anaemia_following_pregnancy"]}'] += 1 + + if pn_checks >= 3: + mnh_counter['m_pnc3+'] += 1 + else: + mnh_counter[f'm_pnc{pn_checks}'] += 1 logger.info(key='total_mat_pnc_visits', data={'mother': person, 'visits': df.at[person, 'la_pn_checks_maternal'], @@ -977,6 +984,13 @@ def apply(self, population): self.sim.start_date) for person in week_5_postnatal_neonates.loc[week_5_postnatal_neonates].index: + pn_checks = df.at[person, 'nb_pnc_check'] + + if pn_checks >= 3: + mnh_counter['n_pnc3+'] += 1 + else: + mnh_counter[f'n_pnc{pn_checks}'] += 1 + self.sim.modules['NewbornOutcomes'].set_disability_status(person) logger.info(key='total_neo_pnc_visits', data={'child': person, 'visits': df.at[person, 'nb_pnc_check']}) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index ad3237328f..3353f86fc8 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -37,7 +37,13 @@ def generate_mnh_outcome_counter(): 'severe_enceph', 'respiratory_distress_syndrome', 'not_breathing_at_birth', 'low_birth_weight', 'macrosomia', 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis', - 'direct_mat_death', 'six_week_survivors' + # death outcomes + 'direct_mat_death', 'six_week_survivors', + + # service coverage outcomes + 'anc0', 'anc1', 'anc2', 'anc3', 'anc4', 'anc5', 'anc6', 'anc7', 'anc8', 'anc8+', + 'home_birth_delivery', 'hospital_delivery', 'health_centre_delivery', + 'm_pnc0', 'm_pnc1', 'm_pnc2', 'm_pnc3+', 'n_pnc0', 'n_pnc1', 'n_pnc2', 'n_pnc3+', ] mnh_outcome_counter = {k: 0 for k in outcome_list} @@ -386,6 +392,7 @@ def calculate_risk_of_death_from_causes(self, risks): cause_of_death = self.rng.choice(list(risks.keys()), p=probs) # Return the primary cause of death so that it can be passed to the demography function + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{cause_of_death}_death'] += 1 return cause_of_death else: # Return false if death will not occur diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 671a6af07a..520474222d 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -2348,8 +2348,29 @@ def rate (count, denom, multiplier): 'neonatal_deaths': neonatal_deaths, 'nmr' : rate(neonatal_deaths, live_births, 1000), 'direct_maternal_deaths': c['direct_mat_death'], - 'direct_mmr': rate(c['direct_mat_death'], live_births, 100_000), - }) + 'direct_mmr': rate(c['direct_mat_death'], live_births, 100_000)}) + + anc1 = sum(c[f'anc{i}'] for i in range(1, 9)) + c['anc8+'] + anc4 = sum(c[f'anc{i}'] for i in range(4, 9))+ c['anc8+'] + anc8 = c['anc8'] + c['anc8+'] + + m_pnc1 = sum(c[f'm_pnc{i}'] for i in range(1, 3)) + c['m_pnc3+'] + n_pnc1 = sum(c[f'm_pnc{i}'] for i in range(1, 3)) + c['m_pnc3+'] + + # HEALTH SERVICE COVERAGE + logger.info(key='service_coverage', + data={'anc1+': rate(anc1 , total_births, 100), + 'anc4+': rate(anc4, total_births, 100), + 'anc8+': rate(anc8, total_births, 100), + + 'fd_rate': rate(anc1 , total_births, 100), + 'hb_rate': rate(c['home_birth_delivery'] , total_births, 100), + 'hc_rate': rate(c['health_centre_delivery'] , total_births, 100), + 'hp_rate': rate(c['hospital_delivery'] , total_births, 100), + + 'm_pnc1+': rate(m_pnc1, total_births, 1000), + 'n_pnc1+': rate(n_pnc1, total_births, 1000)}) + mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() outcome_list = mnh_oc['outcomes'] From bfac2451d4822ce885f8d526ace23182374196a2 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 13:27:39 +0100 Subject: [PATCH 06/27] old logging removed --- .../methods/care_of_women_during_pregnancy.py | 16 ++- src/tlo/methods/labour.py | 59 ++-------- src/tlo/methods/newborn_outcomes.py | 24 ----- src/tlo/methods/postnatal_supervisor.py | 53 +-------- src/tlo/methods/pregnancy_helper_functions.py | 1 + src/tlo/methods/pregnancy_supervisor.py | 101 ++---------------- 6 files changed, 27 insertions(+), 227 deletions(-) diff --git a/src/tlo/methods/care_of_women_during_pregnancy.py b/src/tlo/methods/care_of_women_during_pregnancy.py index b2df6c777b..d1d52f076a 100644 --- a/src/tlo/methods/care_of_women_during_pregnancy.py +++ b/src/tlo/methods/care_of_women_during_pregnancy.py @@ -490,22 +490,18 @@ def further_on_birth_care_of_women_in_pregnancy(self, mother_id): if anc_count > 9: logger.info(key='error', data=f'Mother {mother_id} attended >8 ANC visits during her pregnancy') - # We log the total number of ANC contacts a woman has undergone at the time of birth via this dictionary - if 'ga_anc_one' in mni[mother_id]: - ga_anc_one = float(mni[mother_id]['ga_anc_one']) - else: - ga_anc_one = 0.0 - if anc_count > 8: self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['anc8+'] += 1 else: self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'anc{anc_count}'] += 1 - total_anc_visit_count = {'person_id': mother_id, - 'total_anc': df.at[mother_id, 'ac_total_anc_visits_current_pregnancy'], - 'ga_anc_one': ga_anc_one} + # We log the gestational age at first ANC + if 'ga_anc_one' in mni[mother_id]: + ga_anc_one = float(mni[mother_id]['ga_anc_one']) + else: + ga_anc_one = 0.0 - logger.info(key='anc_count_on_birth', data=total_anc_visit_count, + logger.info(key='ga_at_anc1', data={'person_id': mother_id, 'ga_anc_one': ga_anc_one}, description='A dictionary containing the number of ANC visits each woman has on birth') def on_hsi_alert(self, person_id, treatment_id): diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index d84c41c1e0..3d97aa23dd 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -1056,9 +1056,7 @@ def further_on_birth_labour(self, mother_id): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info # log delivery setting - logger.info(key='delivery_setting_and_mode', data={'mother': mother_id, - 'facility_type': str(mni[mother_id]['delivery_setting']), - 'mode': mni[mother_id]['mode_of_delivery']}) + logger.info(key='delivery_mode', data={'mother': mother_id, 'mode': mni[mother_id]['mode_of_delivery']}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[ f'{str(mni[mother_id]["delivery_setting"])}_delivery'] += 1 @@ -1285,10 +1283,6 @@ def set_intrapartum_complications(self, individual_id, complication): pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'obstructed_labour_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{complication}', - 'timing': 'intrapartum'}) - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[complication] += 1 if complication == 'obstruction_cpd': @@ -1298,11 +1292,8 @@ def set_intrapartum_complications(self, individual_id, complication): # to labour) elif complication == 'placental_abruption': df.at[individual_id, 'la_placental_abruption'] = True - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'placental_abruption', - 'timing': 'intrapartum'}) - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['placental_abruption'] += 1 + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['placental_abruption'] += 1 elif complication == 'antepartum_haem': random_choice = self.rng.choice(['mild_moderate', 'severe'], @@ -1312,18 +1303,13 @@ def set_intrapartum_complications(self, individual_id, complication): if random_choice != 'severe': pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'mild_mod_aph_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'mild_mod_antepartum_haemorrhage', - 'timing': 'intrapartum'}) - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_mod_antepartum_haemorrhage'] += 1 + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_mod_antepartum_haemorrhage'] += 1 else: pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'severe_aph_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'severe_antepartum_haemorrhage', - 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['severe_antepartum_haemorrhage'] += 1 elif complication == 'sepsis_chorioamnionitis': @@ -1331,18 +1317,14 @@ def set_intrapartum_complications(self, individual_id, complication): mni[individual_id]['chorio_in_preg'] = True pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'sepsis_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'sepsis', - 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_intrapartum'] += 1 elif complication == 'uterine_rupture': df.at[individual_id, 'la_uterine_rupture'] = True pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, f'{complication}_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'uterine_rupture', - 'timing': 'intrapartum'}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['uterine_rupture'] += 1 def set_postpartum_complications(self, individual_id, complication): @@ -1396,9 +1378,6 @@ def set_postpartum_complications(self, individual_id, complication): # Set primary complication to true df.at[individual_id, 'la_postpartum_haem'] = True - logger_pn.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{complication}', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[complication] += 1 # Store mni variables used during treatment @@ -2439,10 +2418,6 @@ def apply(self, individual_id): 'defining_term_status'][3]: mni[individual_id]['labour_state'] = 'early_preterm_labour' - - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'early_preterm_labour', - 'timing': 'intrapartum'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['early_preterm_labour'] += 1 elif params['list_limits_for_defining_term_status'][4] <= gestational_age_in_days <= params['list_limits' @@ -2451,19 +2426,11 @@ def apply(self, individual_id): 'status'][5]: mni[individual_id]['labour_state'] = 'late_preterm_labour' - - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'late_preterm_labour', - 'timing': 'intrapartum'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['late_preterm_labour'] += 1 elif gestational_age_in_days >= params['list_limits_for_defining_term_status'][6]: mni[individual_id]['labour_state'] = 'postterm_labour' - - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'post_term_labour', - 'timing': 'intrapartum'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['post_term_labour'] += 1 labour_state = mni[individual_id]['labour_state'] @@ -2631,9 +2598,6 @@ def apply(self, individual_id): self.module.set_intrapartum_complications(individual_id, complication=complication) if df.at[individual_id, 'la_obstructed_labour']: - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'obstructed_labour', - 'timing': 'intrapartum'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['obstructed_labour'] += 1 # And we determine if any existing hypertensive disorders would worsen @@ -2750,8 +2714,6 @@ def apply(self, individual_id): del mni[individual_id] if df.at[individual_id, 'la_intrapartum_still_birth'] or mni[individual_id]['single_twin_still_birth']: - logger.info(key='intrapartum_stillbirth', data={'mother_id': individual_id, - 'date_of_ip_stillbirth': self.sim.date}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['intrapartum_stillbirth'] += 1 # Reset property @@ -2840,15 +2802,9 @@ def apply(self, mother_id): self.module.set_postpartum_complications(mother_id, complication=complication) if df.at[mother_id, 'la_sepsis_pp']: - logger_pn.info(key='maternal_complication', data={'person': mother_id, - 'type': 'sepsis_postnatal', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_postnatal'] += 1 if df.at[mother_id, 'la_postpartum_haem']: - logger_pn.info(key='maternal_complication', data={'person': mother_id, - 'type': 'primary_postpartum_haemorrhage', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['primary_postpartum_haemorrhage'] += 1 self.module.progression_of_hypertensive_disorders(mother_id, property_prefix='pn') @@ -3002,9 +2958,6 @@ def apply(self, person_id, squeeze_factor): self.module.progression_of_hypertensive_disorders(person_id, property_prefix='ps') if df.at[person_id, 'la_obstructed_labour']: - logger.info(key='maternal_complication', data={'person': person_id, - 'type': 'obstructed_labour', - 'timing': 'intrapartum'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['obstructed_labour'] += 1 # ======================================= COMPLICATION MANAGEMENT ========================== diff --git a/src/tlo/methods/newborn_outcomes.py b/src/tlo/methods/newborn_outcomes.py index 4019544bd4..e8f32ed7af 100644 --- a/src/tlo/methods/newborn_outcomes.py +++ b/src/tlo/methods/newborn_outcomes.py @@ -523,32 +523,22 @@ def apply_risk_of_congenital_anomaly(self, child_id): if self.rng.random_sample() < params['prob_congenital_heart_anomaly']: self.congeintal_anomalies.set(child_id, 'heart') - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'congenital_heart_anomaly'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['congenital_heart_anomaly'] += 1 if self.rng.random_sample() < params['prob_limb_musc_skeletal_anomaly']: self.congeintal_anomalies.set(child_id, 'limb_musc_skeletal') - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'limb_or_musculoskeletal_anomaly'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['limb_or_musculoskeletal_anomaly'] += 1 if self.rng.random_sample() < params['prob_urogenital_anomaly']: self.congeintal_anomalies.set(child_id, 'urogenital') - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'urogenital_anomaly'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['urogenital_anomaly'] += 1 if self.rng.random_sample() < params['prob_digestive_anomaly']: self.congeintal_anomalies.set(child_id, 'digestive') - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'digestive_anomaly'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['digestive_anomaly'] += 1 if self.rng.random_sample() < params['prob_other_anomaly']: self.congeintal_anomalies.set(child_id, 'other') - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'other_anomaly'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['other_anomaly'] += 1 def apply_risk_of_neonatal_infection_and_sepsis(self, child_id): @@ -563,9 +553,6 @@ def apply_risk_of_neonatal_infection_and_sepsis(self, child_id): # The linear model calculates the individuals probability of early_onset_neonatal_sepsis if self.eval(self.nb_linear_models['early_onset_neonatal_sepsis'], child_id): df.at[child_id, 'nb_early_onset_neonatal_sepsis'] = True - - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'early_onset_sepsis'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['early_onset_sepsis'] += 1 def apply_risk_of_encephalopathy(self, child_id, timing): @@ -598,8 +585,6 @@ def apply_risk_of_encephalopathy(self, child_id, timing): else: df.at[child_id, 'nb_encephalopathy'] = 'severe_enceph' - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': f'{df.at[child_id, "nb_encephalopathy"]}'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{df.at[child_id, "nb_encephalopathy"]}'] += 1 # Check all encephalopathy cases receive a grade @@ -623,9 +608,6 @@ def apply_risk_of_preterm_respiratory_distress_syndrome(self, child_id): # Use the linear model to calculate individual risk and make changes if self.eval(self.nb_linear_models['rds_preterm'], child_id): df.at[child_id, 'nb_preterm_respiratory_distress'] = True - - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'respiratory_distress_syndrome'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['respiratory_distress_syndrome'] += 1 def apply_risk_of_not_breathing_at_birth(self, child_id): @@ -646,9 +628,6 @@ def apply_risk_of_not_breathing_at_birth(self, child_id): # explicitly modelled elif self.rng.random_sample() < params['prob_failure_to_transition']: df.at[child_id, 'nb_not_breathing_at_birth'] = True - - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'not_breathing_at_birth'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['not_breathing_at_birth'] += 1 def scheduled_week_one_postnatal_event(self, individual_id): @@ -1167,16 +1146,13 @@ def on_birth(self, mother_id, child_id): if (df.at[child_id, 'nb_low_birth_weight_status'] == 'low_birth_weight') or\ (df.at[child_id, 'nb_low_birth_weight_status'] == 'very_low_birth_weight') or\ (df.at[child_id, 'nb_low_birth_weight_status'] == 'extremely_low_birth_weight'): - logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'low_birth_weight'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['low_birth_weight'] += 1 elif df.at[child_id, 'nb_low_birth_weight_status'] == 'macrosomia': - logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'macrosomia'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['macrosomia'] += 1 df.at[child_id, 'nb_size_for_gestational_age'] = mni[mother_id]['birth_size'] if df.at[child_id, 'nb_size_for_gestational_age'] == 'small_for_gestational_age': - logger.info(key='newborn_complication', data={'newborn': child_id, 'type': 'small_for_gestational_age'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['small_for_gestational_age'] += 1 df.at[child_id, 'nb_early_init_breastfeeding'] = False diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 5ba48a0e24..34c31fca9e 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -354,10 +354,6 @@ def further_on_birth_postnatal_supervisor(self, mother_id): # Store the onset weight for daly calculations store_dalys_in_mni(mother_id, mni, f'{fistula_type}_fistula_onset', self.sim.date) - - logger.info(key='maternal_complication', data={'person': mother_id, - 'type': f'{fistula_type}_fistula', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{fistula_type}_fistula'] += 1 # Determine if she will seek care for repair @@ -501,9 +497,6 @@ def onset(eq): for person in new_sepsis.loc[new_sepsis].index: store_dalys_in_mni(person, mni, 'sepsis_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': person, - 'type': 'sepsis', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_postnatal'] += 1 # ------------------------------------ SECONDARY PPH ---------------------------------------------------------- @@ -516,10 +509,6 @@ def onset(eq): for person in onset_pph.loc[onset_pph].index: store_dalys_in_mni(person, mni, 'secondary_pph_onset', self.sim.date) - - logger.info(key='maternal_complication', data={'person': person, - 'type': 'secondary_postpartum_haemorrhage', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['secondary_postpartum_haemorrhage'] += 1 # --------------------------------------------- ANAEMIA -------------------------------------------------- @@ -597,9 +586,6 @@ def log_new_progressed_cases(disease): mni=mni, mni_variable='eclampsia_onset', date=self.sim.date) for person in new_onset_disease.index: - logger.info(key='maternal_complication', data={'person': person, - 'type': disease, - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[disease] += 1 if disease == 'severe_pre_eclamp': @@ -641,12 +627,8 @@ def log_new_progressed_cases(disease): df.loc[pre_eclampsia.loc[pre_eclampsia].index, 'ps_prev_pre_eclamp'] = True df.loc[pre_eclampsia.loc[pre_eclampsia].index, 'pn_htn_disorders'] = 'mild_pre_eclamp' - - for person in pre_eclampsia.loc[pre_eclampsia].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'mild_pre_eclamp', - 'timing': 'postnatal'}) - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_pre_eclamp'] += 1 + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[ + 'mild_pre_eclamp'] += (len(pre_eclampsia.loc[pre_eclampsia].index)) # -------------------------------- RISK OF GESTATIONAL HYPERTENSION -------------------------------------- gest_hypertension = self.apply_linear_model( @@ -655,12 +637,8 @@ def log_new_progressed_cases(disease): (df['pn_htn_disorders'] == 'none')]) df.loc[gest_hypertension.loc[gest_hypertension].index, 'pn_htn_disorders'] = 'gest_htn' - - for person in gest_hypertension.loc[gest_hypertension].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'mild_gest_htn', - 'timing': 'postnatal'}) - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_gest_htn'] += 1 + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[ + 'mild_gest_htn'] += len(gest_hypertension.loc[gest_hypertension].index) # -------------------------------- RISK OF DEATH SEVERE HYPERTENSION ------------------------------------------ # Risk of death is applied to women with severe hypertensive disease @@ -750,9 +728,6 @@ def apply_risk_of_neonatal_complications_in_week_one(self, child_id, mother_id): if self.rng.random_sample() < risk_eons: df.at[child_id, 'pn_sepsis_early_neonatal'] = True self.sim.modules['NewbornOutcomes'].newborn_care_info[child_id]['sepsis_postnatal'] = True - - logger.info(key='newborn_complication', data={'newborn': child_id, - 'type': 'early_onset_sepsis'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['early_onset_sepsis'] += 1 def set_postnatal_complications_neonates(self, upper_and_lower_day_limits): @@ -778,9 +753,6 @@ def set_postnatal_complications_neonates(self, upper_and_lower_day_limits): for person in onset_sepsis.loc[onset_sepsis].index: if person in nci: nci[person]['sepsis_postnatal'] = True - - logger.info(key='newborn_complication', data={'newborn': person, - 'type': 'late_onset_sepsis'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['late_onset_sepsis'] += 1 # Then we determine if care will be sought for newly septic newborns @@ -1057,9 +1029,6 @@ def apply(self, individual_id): if endo_result or ut_result or ssti_result: df.at[individual_id, 'pn_sepsis_late_postpartum'] = True store_dalys_in_mni(individual_id, mni, 'sepsis_onset', self.sim.date) - - logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'sepsis', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['sepsis_postnatal'] += 1 # Sepsis secondary to endometritis is stored within the mni as it is used as a predictor in a linear model @@ -1076,10 +1045,6 @@ def apply(self, individual_id): if risk_secondary_pph > self.module.rng.random_sample(): df.at[individual_id, 'pn_postpartum_haem_secondary'] = True store_dalys_in_mni(individual_id, mni, 'secondary_pph_onset', self.sim.date) - - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'secondary_postpartum_haemorrhage', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['secondary_postpartum_haemorrhage'] += 1 # ------------------------------------------------ NEW ONSET ANAEMIA ------------------------------------------ @@ -1146,9 +1111,6 @@ def log_new_progressed_cases(disease): if not new_onset_disease.empty: for person in new_onset_disease.index: - logger.info(key='maternal_complication', data={'person': person, - 'type': disease, - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[disease] += 1 if disease == 'severe_pre_eclamp': @@ -1170,9 +1132,6 @@ def log_new_progressed_cases(disease): if risk_pe_after_pregnancy > self.module.rng.random_sample(): df.at[individual_id, 'pn_htn_disorders'] = 'mild_pre_eclamp' df.at[individual_id, 'ps_prev_pre_eclamp'] = True - - logger.info(key='maternal_complication', data={'person': individual_id, 'type': 'mild_pre_eclamp', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_pre_eclamp'] += 1 else: @@ -1180,10 +1139,6 @@ def log_new_progressed_cases(disease): individual_id]])[individual_id] if risk_gh_after_pregnancy > self.module.rng.random_sample(): df.at[individual_id, 'pn_htn_disorders'] = 'gest_htn' - - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'mild_gest_htn', - 'timing': 'postnatal'}) self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_gest_htn'] += 1 # ====================================== POSTNATAL CHECK ================================================== diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 3353f86fc8..446ad0a433 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -8,6 +8,7 @@ from tlo import logging + def generate_mnh_outcome_counter(): outcome_list = [ # early/abortive outcomes 'ectopic_unruptured', 'ectopic_ruptured','multiple_pregnancy', 'twin_birth', 'placenta_praevia', diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 520474222d..ff3295a5c2 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -661,11 +661,6 @@ def further_on_birth_pregnancy_supervisor(self, mother_id): if df.at[mother_id, 'ps_anaemia_in_pregnancy'] != 'none': self.mnh_outcome_counter[f'an_anaemia_{df.at[mother_id, "ps_anaemia_in_pregnancy"]}'] += 1 - logger.info(key='conditions_on_birth', data={'mother': mother_id, - 'anaemia_status': df.at[mother_id, 'ps_anaemia_in_pregnancy'], - 'gdm_status': df.at[mother_id, 'ps_gest_diab'], - 'htn_status': df.at[mother_id, 'ps_htn_disorders']}) - # We currently assume that hyperglycemia due to gestational diabetes resolves following birth if df.at[mother_id, 'ps_gest_diab'] != 'none': df.at[mother_id, 'ps_gest_diab'] = 'none' @@ -974,9 +969,6 @@ def do_after_abortion(self, individual_id, type_abortion): params = self.current_parameters # Log the pregnancy loss - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{type_abortion}', - 'timing': 'antenatal'}) self.mnh_outcome_counter[type_abortion] += 1 # This function officially ends a pregnancy through the contraception module (updates 'is_pregnant' and @@ -1004,10 +996,6 @@ def do_after_abortion(self, individual_id, type_abortion): risk_of_complications = params['prob_complicated_ia'] if self.rng.random_sample() < risk_of_complications: - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'complicated_{type_abortion}', - 'timing': 'antenatal'}) - self.mnh_outcome_counter[f'complicated_{type_abortion}'] += 1 self.apply_risk_of_abortion_complications(individual_id, f'{type_abortion}') @@ -1026,34 +1014,22 @@ def apply_risk_of_abortion_complications(self, individual_id, cause): if cause == 'induced_abortion': if self.rng.random_sample() < params['prob_injury_post_abortion']: self.abortion_complications.set([individual_id], 'injury') - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{cause}_injury', - 'timing': 'antenatal'}) self.mnh_outcome_counter[f'{cause}_injury'] += 1 if self.rng.random_sample() < params['prob_haemorrhage_post_abortion']: self.abortion_complications.set([individual_id], 'haemorrhage') pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'abortion_haem_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{cause}_haemorrhage', - 'timing': 'antenatal'}) self.mnh_outcome_counter[f'{cause}_haemorrhage'] += 1 if self.rng.random_sample() < params['prob_sepsis_post_abortion']: self.abortion_complications.set([individual_id], 'sepsis') pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'abortion_sep_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{cause}_sepsis', - 'timing': 'antenatal'}) self.mnh_outcome_counter[f'{cause}_sepsis'] += 1 if not self.abortion_complications.has_any([individual_id], 'sepsis', 'haemorrhage', 'injury', first=True): self.abortion_complications.set([individual_id], 'other') - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': f'{cause}_other_comp', - 'timing': 'antenatal'}) self.mnh_outcome_counter[f'{cause}_other_comp'] += 1 # We assume only women with complicated abortions will experience disability @@ -1113,11 +1089,7 @@ def apply_risk_of_gestational_diabetes(self, gestation_of_interest): df.loc[gest_diab.loc[gest_diab].index, 'ps_gest_diab'] = 'uncontrolled' df.loc[gest_diab.loc[gest_diab].index, 'ps_prev_gest_diab'] = True - for person in gest_diab.loc[gest_diab].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'gest_diab', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['gest_diab'] += 1 + self.mnh_outcome_counter['gest_diab'] += len(gest_diab.loc[gest_diab].index) def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): @@ -1139,11 +1111,7 @@ def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): df.loc[pre_eclampsia.loc[pre_eclampsia].index, 'ps_prev_pre_eclamp'] = True df.loc[pre_eclampsia.loc[pre_eclampsia].index, 'ps_htn_disorders'] = 'mild_pre_eclamp' - for person in pre_eclampsia.loc[pre_eclampsia].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'mild_pre_eclamp', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['mild_pre_eclamp'] += 1 + self.mnh_outcome_counter['mild_pre_eclamp'] += len(pre_eclampsia.loc[pre_eclampsia].index) # -------------------------------- RISK OF GESTATIONAL HYPERTENSION -------------------------------------- # For women who dont develop pre-eclampsia during this month, we apply a risk of gestational hypertension @@ -1154,11 +1122,7 @@ def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): df.loc[gest_hypertension.loc[gest_hypertension].index, 'ps_htn_disorders'] = 'gest_htn' - for person in gest_hypertension.loc[gest_hypertension].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'mild_gest_htn', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['mild_gest_htn'] += 1 + self.mnh_outcome_counter['mild_gest_htn'] += len(gest_hypertension.loc[gest_hypertension].inde) def apply_risk_of_progression_of_hypertension(self, gestation_of_interest): """ @@ -1213,9 +1177,6 @@ def log_new_progressed_cases(disease): # And log all of the new onset cases of any hypertensive disease for person in new_onset_disease.index: - logger.info(key='maternal_complication', data={'person': person, - 'type': disease, - 'timing': 'antenatal'}) self.mnh_outcome_counter[disease] +=1 if disease == 'severe_pre_eclamp': @@ -1289,11 +1250,7 @@ def apply_risk_of_placental_abruption(self, gestation_of_interest): ~df['la_currently_in_labour']]) df.loc[placenta_abruption.loc[placenta_abruption].index, 'ps_placental_abruption'] = True - for person in placenta_abruption.loc[placenta_abruption].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'placental_abruption', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['placental_abruption'] += 1 + self.mnh_outcome_counter['placental_abruption'] += len(placenta_abruption.loc[placenta_abruption].index) def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): """ @@ -1331,11 +1288,7 @@ def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): severe_women.loc[severe_women].index.to_series().apply( pregnancy_helper_functions.store_dalys_in_mni, mni=mni, mni_variable='severe_aph_onset', date=self.sim.date) - for person in severe_women.loc[severe_women].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'severe_antepartum_haemorrhage', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['severe_antepartum_haemorrhage'] += 1 + self.mnh_outcome_counter['severe_antepartum_haemorrhage'] += len(severe_women.loc[severe_women].index) non_severe_women = (df.loc[antepartum_haemorrhage.loc[antepartum_haemorrhage].index, 'ps_antepartum_haemorrhage'] != 'severe') @@ -1344,12 +1297,7 @@ def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): pregnancy_helper_functions.store_dalys_in_mni, mni=mni, mni_variable='mild_mod_aph_onset', date=self.sim.date) - for person in non_severe_women.loc[non_severe_women].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'mild_mod_antepartum_haemorrhage', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['mild_mod_antepartum_haemorrhage'] += 1 - + self.mnh_outcome_counter['mild_mod_antepartum_haemorrhage'] += len(non_severe_women.loc[non_severe_women].index) def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gestation_of_interest): """ @@ -1373,12 +1321,7 @@ def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gest # We allow women to seek care for PROM df.loc[prom.loc[prom].index, 'ps_emergency_event'] = True - - for person in prom.loc[prom].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'PROM', - 'timing': 'antenatal'}) - self.mnh_outcome_counter['PROM'] += 1 + self.mnh_outcome_counter['PROM'] += len(prom.loc[prom].index) # Determine if those with PROM will develop infection prior to care seeking infection = pd.Series(self.rng.random_sample(len(prom.loc[prom])) < params['prob_chorioamnionitis'], @@ -1391,9 +1334,6 @@ def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gest for person in infection.loc[infection].index: self.mother_and_newborn_info[person]['chorio_in_preg'] = True - logger.info(key='maternal_complication', data={'person': person, - 'type': 'clinical_chorioamnionitis', - 'timing': 'antenatal'}) self.mnh_outcome_counter['clinical_chorioamnionitis'] += 1 def apply_risk_of_preterm_labour(self, gestation_of_interest): @@ -1458,7 +1398,6 @@ def update_variables_post_still_birth_for_data_frame(self, women): for person in women.index: self.sim.modules['Contraception'].end_pregnancy(person) mni[person]['delete_mni'] = True - logger.info(key='antenatal_stillbirth', data={'mother': person}) self.mnh_outcome_counter['antenatal_stillbirth'] += 1 # Call functions across the modules to ensure properties are rest @@ -1481,7 +1420,6 @@ def update_variables_post_still_birth_for_individual(self, individual_id): df.at[individual_id, 'ps_prev_stillbirth'] = True mni[individual_id]['delete_mni'] = True - logger.info(key='antenatal_stillbirth', data={'mother': individual_id}) self.mnh_outcome_counter['antenatal_stillbirth'] += 1 # Reset pregnancy and schedule possible update of contraception @@ -1794,9 +1732,6 @@ def apply(self, population): # For women whose pregnancy is ectopic we scheduled them to the EctopicPregnancyEvent in between 3-5 weeks # (this simulates time period prior to which symptoms onset- and may trigger care seeking) for person in ectopic_risk.loc[ectopic_risk].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'ectopic_unruptured', - 'timing': 'antenatal'}) self.module.mnh_outcome_counter['ectopic_unruptured'] += 1 self.sim.schedule_event(EctopicPregnancyEvent(self.module, person), @@ -1812,12 +1747,7 @@ def apply(self, population): < params['prob_multiples'], index=multiple_risk.loc[multiple_risk].index) df.loc[multiples.loc[multiples].index, 'ps_multiple_pregnancy'] = True - - for person in multiples.loc[multiples].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'multiple_pregnancy', - 'timing': 'antenatal'}) - self.module.mnh_outcome_counter['multiple_pregnancy'] += 1 + self.module.mnh_outcome_counter['multiple_pregnancy'] += len(multiples.loc[multiples].index) # -----------------------------APPLYING RISK OF PLACENTA PRAEVIA ------------------------------------------- # Next,we apply a one off risk of placenta praevia (placenta will grow to cover the cervix either partially or @@ -1827,12 +1757,7 @@ def apply(self, population): df.loc[new_pregnancy & (df['ps_ectopic_pregnancy'] == 'none')]) df.loc[placenta_praevia.loc[placenta_praevia].index, 'ps_placenta_praevia'] = True - - for person in placenta_praevia.loc[placenta_praevia].index: - logger.info(key='maternal_complication', data={'person': person, - 'type': 'placenta_praevia', - 'timing': 'antenatal'}) - self.module.mnh_outcome_counter['placenta_praevia'] += 1 + self.module.mnh_outcome_counter['placenta_praevia'] += len(placenta_praevia.loc[placenta_praevia].index) # ------------------------- APPLYING RISK OF SYPHILIS INFECTION DURING PREGNANCY --------------------------- # Finally apply risk that syphilis will develop during pregnancy @@ -2016,9 +1941,6 @@ def apply(self, individual_id): if not df.at[individual_id, 'is_alive'] or (df.at[individual_id, 'ps_ectopic_pregnancy'] != 'not_ruptured'): return - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'ectopic_ruptured', - 'timing': 'antenatal'}) self.module.mnh_outcome_counter['ectopic_ruptured'] += 1 # Set the variable @@ -2127,9 +2049,7 @@ def apply(self, individual_id): return df.at[individual_id, 'ps_syphilis'] = True - logger.info(key='maternal_complication', data={'person': individual_id, - 'type': 'syphilis', - 'timing': 'antenatal'}) + self.module.mnh_outcome_counter['syphilis'] += 1 class ParameterUpdateEvent(Event, PopulationScopeEventMixin): @@ -2371,7 +2291,6 @@ def rate (count, denom, multiplier): 'm_pnc1+': rate(m_pnc1, total_births, 1000), 'n_pnc1+': rate(n_pnc1, total_births, 1000)}) - mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() outcome_list = mnh_oc['outcomes'] self.module.mnh_outcome_counter = {k:0 for k in outcome_list} From 905091b6ed1ad32f25bf9e454b3af116821cb6c2 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 13:35:41 +0100 Subject: [PATCH 07/27] fix --- src/tlo/methods/pregnancy_supervisor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index ff3295a5c2..20c91fe4ef 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -1122,7 +1122,7 @@ def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): df.loc[gest_hypertension.loc[gest_hypertension].index, 'ps_htn_disorders'] = 'gest_htn' - self.mnh_outcome_counter['mild_gest_htn'] += len(gest_hypertension.loc[gest_hypertension].inde) + self.mnh_outcome_counter['mild_gest_htn'] += len(gest_hypertension.loc[gest_hypertension].index) def apply_risk_of_progression_of_hypertension(self, gestation_of_interest): """ From 5a04b444bb38c1c39b46ca462c6a9b6333e7d195 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 14:06:34 +0100 Subject: [PATCH 08/27] fix --- src/tlo/methods/pregnancy_supervisor.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 20c91fe4ef..4185bd935a 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -2190,11 +2190,6 @@ def apply(self, population): df = self.sim.population.props c = self.module.mnh_outcome_counter - logger.info(key='yrly_counter_dict', data=c) - - def rate (count, denom, multiplier): - return (count/denom) * multiplier - # DENOMINATORS live_births = len(df[(df['date_of_birth'].dt.year == self.sim.date.year - 1) & (df['mother_id'] >= 0)]) @@ -2208,7 +2203,15 @@ def rate (count, denom, multiplier): total_births = live_births + c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] + if (live_births == 0) or (pregnancies == 0): + return + # MATERNAL COMPLICATION INCIDENCE + logger.info(key='yearly_counter_dict', data=c) + + def rate (count, denom, multiplier): + return (count/denom) * multiplier + total_an_anaemia_cases = c['an_anaemia_mild'] + c['an_anaemia_moderate'] + c['an_anaemia_severe'] total_pn_anaemia_cases = c['pn_anaemia_mild'] + c['pn_anaemia_moderate'] + c['pn_anaemia_severe'] total_preterm_birth = c['early_preterm_labour'] + c['late_preterm_labour'] From c62d5df215023617e8acc4adbf2951f61313c196 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 14:26:45 +0100 Subject: [PATCH 09/27] fix --- src/tlo/methods/pregnancy_supervisor.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 4185bd935a..408b5c7847 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -2192,19 +2192,17 @@ def apply(self, population): # DENOMINATORS live_births = len(df[(df['date_of_birth'].dt.year == self.sim.date.year - 1) & (df['mother_id'] >= 0)]) - pregnancies =len(df[df['date_of_last_pregnancy'].dt.year == self.sim.date.year - 1]) - comp_pregnancies = (c['ectopic_unruptured'] + c['spontaneous_abortion'] + c['induced_abortion'] + c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] + live_births) - deliveries = live_births - c['twin_birth'] - total_births = live_births + c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] + total_preterm_birth = c['early_preterm_labour'] + c['late_preterm_labour'] - if (live_births == 0) or (pregnancies == 0): - return + for denom in [live_births, pregnancies, comp_pregnancies, deliveries, total_births, total_preterm_birth]: + if denom == 0: + return # MATERNAL COMPLICATION INCIDENCE logger.info(key='yearly_counter_dict', data=c) @@ -2214,7 +2212,6 @@ def rate (count, denom, multiplier): total_an_anaemia_cases = c['an_anaemia_mild'] + c['an_anaemia_moderate'] + c['an_anaemia_severe'] total_pn_anaemia_cases = c['pn_anaemia_mild'] + c['pn_anaemia_moderate'] + c['pn_anaemia_severe'] - total_preterm_birth = c['early_preterm_labour'] + c['late_preterm_labour'] total_aph = c['mild_mod_antepartum_haemorrhage'] + c['severe_antepartum_haemorrhage'] total_sepsis = c['clinical_chorioamnionitis'] + c['sepsis_intrapartum'] + c['sepsis_postnatal'] total_pph = c['primary_postpartum_haemorrhage'] + c['secondary_postpartum_haemorrhage'] From d8423e1b93130e6af2935306e11f09ab5e6cdf56 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 14:32:11 +0100 Subject: [PATCH 10/27] comments for clarity --- src/tlo/methods/pregnancy_supervisor.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 408b5c7847..4f2e05dfcb 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -2191,6 +2191,7 @@ def apply(self, population): c = self.module.mnh_outcome_counter # DENOMINATORS + # Define denominators used to calculate rates, cancel the event if any are 0 to prevent division by 0 errors live_births = len(df[(df['date_of_birth'].dt.year == self.sim.date.year - 1) & (df['mother_id'] >= 0)]) pregnancies =len(df[df['date_of_last_pregnancy'].dt.year == self.sim.date.year - 1]) comp_pregnancies = (c['ectopic_unruptured'] + c['spontaneous_abortion'] + @@ -2205,8 +2206,10 @@ def apply(self, population): return # MATERNAL COMPLICATION INCIDENCE - logger.info(key='yearly_counter_dict', data=c) + # Log the yearly dictionary (allows for analyses with outcomes not used in this event) + logger.info(key='yearly_mnh_counter_dict', data=c) + # Calculate and store rates of key maternal and neonatal complications def rate (count, denom, multiplier): return (count/denom) * multiplier @@ -2257,6 +2260,7 @@ def rate (count, denom, multiplier): 'nb_sga': rate(c['small_for_gestational_age'], live_births, 100)}) # DIRECT MATERNAL DEATHS, NEWBORN DEATHS AND STILLBIRTH + # Calculate and store rates of maternal and newborn death and stillbirth neonatal_deaths = len(df[(df['date_of_death'].dt.year == self.sim.date.year - 1) & (df['age_days'] <= 28)]) stillbirths = c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] @@ -2270,6 +2274,7 @@ def rate (count, denom, multiplier): 'direct_maternal_deaths': c['direct_mat_death'], 'direct_mmr': rate(c['direct_mat_death'], live_births, 100_000)}) + # Finally log coverage of key health services anc1 = sum(c[f'anc{i}'] for i in range(1, 9)) + c['anc8+'] anc4 = sum(c[f'anc{i}'] for i in range(4, 9))+ c['anc8+'] anc8 = c['anc8'] + c['anc8+'] @@ -2291,6 +2296,7 @@ def rate (count, denom, multiplier): 'm_pnc1+': rate(m_pnc1, total_births, 1000), 'n_pnc1+': rate(n_pnc1, total_births, 1000)}) + # Reset the dictionary so all values = 0 mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() outcome_list = mnh_oc['outcomes'] self.module.mnh_outcome_counter = {k:0 for k in outcome_list} From a6b12fe5d200c736043150d81a3097835cc18763 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Tue, 8 Oct 2024 15:33:09 +0100 Subject: [PATCH 11/27] removed counting which wasnt needed --- src/tlo/methods/pregnancy_helper_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 446ad0a433..3ae5b5b264 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -393,7 +393,6 @@ def calculate_risk_of_death_from_causes(self, risks): cause_of_death = self.rng.choice(list(risks.keys()), p=probs) # Return the primary cause of death so that it can be passed to the demography function - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{cause_of_death}_death'] += 1 return cause_of_death else: # Return false if death will not occur From 5ca2314b4eb04cb14909fe835ab4e98f87688668 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Wed, 9 Oct 2024 10:30:13 +0100 Subject: [PATCH 12/27] fixes to pregnancy_supervisor test --- tests/test_pregnancy_supervisor.py | 33 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/test_pregnancy_supervisor.py b/tests/test_pregnancy_supervisor.py index 31adcf03b5..4a1aafe0b7 100644 --- a/tests/test_pregnancy_supervisor.py +++ b/tests/test_pregnancy_supervisor.py @@ -123,8 +123,11 @@ def test_run_core_modules_normal_allocation_of_pregnancy(seed, tmpdir): # check that no errors have been logged during the simulation run output = parse_log_file(sim.log_filepath) - assert 'error' not in output['tlo.methods.pregnancy_supervisor'] - assert 'error' not in output['tlo.methods.care_of_women_during_pregnancy'] + if 'tlo.methods.pregnancy_supervisor' in output: + assert 'error' not in output['tlo.methods.pregnancy_supervisor'] + + if 'tlo.methods.care_of_women_during_pregnancy' in output: + assert 'error' not in output['tlo.methods.care_of_women_during_pregnancy'] @pytest.mark.slow @@ -136,13 +139,16 @@ def test_run_core_modules_high_volumes_of_pregnancy(seed, tmpdir): register_modules(sim) sim.make_initial_population(n=5000) set_all_women_as_pregnant_and_reset_baseline_parity(sim) - sim.simulate(end_date=Date(2011, 1, 1)) + sim.simulate(end_date=Date(2011, 1, 2)) check_dtypes(sim) # check that no errors have been logged during the simulation run output = parse_log_file(sim.log_filepath) - assert 'error' not in output['tlo.methods.pregnancy_supervisor'] - assert 'error' not in output['tlo.methods.care_of_women_during_pregnancy'] + if 'tlo.methods.pregnancy_supervisor' in output: + assert 'error' not in output['tlo.methods.pregnancy_supervisor'] + + if 'tlo.methods.care_of_women_during_pregnancy' in output: + assert 'error' not in output['tlo.methods.care_of_women_during_pregnancy'] @pytest.mark.slow @@ -172,14 +178,15 @@ def test_run_core_modules_high_volumes_of_pregnancy_hsis_cant_run(seed, tmpdir): sim.make_initial_population(n=5000) set_all_women_as_pregnant_and_reset_baseline_parity(sim) - sim.simulate(end_date=Date(2011, 1, 1)) + sim.simulate(end_date=Date(2011, 1, 2)) check_dtypes(sim) # check that no errors have been logged during the simulation run output = parse_log_file(sim.log_filepath) for module in ['pregnancy_supervisor', 'care_of_women_during_pregnancy', 'labour', 'postnatal_supervisor', 'newborn_outcomes']: - assert 'error' not in output[f'tlo.methods.{module}'] + if module in output: + assert 'error' not in output[f'tlo.methods.{module}'] @pytest.mark.slow @@ -220,13 +227,16 @@ def test_run_with_all_referenced_modules_registered(seed, tmpdir): sim.make_initial_population(n=5000) set_all_women_as_pregnant_and_reset_baseline_parity(sim) # keep high volume of pregnancy to increase risk of error - sim.simulate(end_date=Date(2011, 1, 1)) + sim.simulate(end_date=Date(2011, 1, 2)) check_dtypes(sim) # check that no errors have been logged during the simulation run output = parse_log_file(sim.log_filepath) - assert 'error' not in output['tlo.methods.pregnancy_supervisor'] - assert 'error' not in output['tlo.methods.care_of_women_during_pregnancy'] + if 'tlo.methods.pregnancy_supervisor' in output: + assert 'error' not in output['tlo.methods.pregnancy_supervisor'] + + if 'tlo.methods.care_of_women_during_pregnancy' in output: + assert 'error' not in output['tlo.methods.care_of_women_during_pregnancy'] def test_store_dalys_in_mni_function_and_daly_calculations(seed): @@ -1082,6 +1092,9 @@ def test_pregnancy_supervisor_gdm(seed): pregnancy_helper_functions.update_mni_dictionary(sim.modules['PregnancySupervisor'], woman) pregnancy_helper_functions.update_mni_dictionary(sim.modules['Labour'], woman) + sim.modules['PregnancySupervisor'].mother_and_newborn_info[woman]['delivery_setting'] = 'home_birth' + + # Run pregnancy supervisor event pregnancy_sup = pregnancy_supervisor.PregnancySupervisorEvent(module=sim.modules['PregnancySupervisor']) sim.date = sim.date + pd.DateOffset(weeks=20) From 09a216f6b058b23d51cc20ebd3b5aa8618e891a3 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Thu, 10 Oct 2024 09:31:32 +0100 Subject: [PATCH 13/27] fix denom error --- src/tlo/methods/pregnancy_supervisor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 4f2e05dfcb..648cde5a59 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -2293,8 +2293,8 @@ def rate (count, denom, multiplier): 'hc_rate': rate(c['health_centre_delivery'] , total_births, 100), 'hp_rate': rate(c['hospital_delivery'] , total_births, 100), - 'm_pnc1+': rate(m_pnc1, total_births, 1000), - 'n_pnc1+': rate(n_pnc1, total_births, 1000)}) + 'm_pnc1+': rate(m_pnc1, total_births, 100), + 'n_pnc1+': rate(n_pnc1, total_births, 100)}) # Reset the dictionary so all values = 0 mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() From 03161e0b15306d4302db589b36cd49bd77376495 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Thu, 10 Oct 2024 09:40:50 +0100 Subject: [PATCH 14/27] fix indentation error --- src/tlo/methods/postnatal_supervisor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 34c31fca9e..c8cbbcc748 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -1139,7 +1139,7 @@ def log_new_progressed_cases(disease): individual_id]])[individual_id] if risk_gh_after_pregnancy > self.module.rng.random_sample(): df.at[individual_id, 'pn_htn_disorders'] = 'gest_htn' - self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_gest_htn'] += 1 + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['mild_gest_htn'] += 1 # ====================================== POSTNATAL CHECK ================================================== # Import the HSI which represents postnatal care From de3ee93e0065c29c570da7b625e436332eaef1d4 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Thu, 10 Oct 2024 14:24:21 +0100 Subject: [PATCH 15/27] added missing logging --- src/tlo/methods/labour.py | 16 ++++------------ src/tlo/methods/postnatal_supervisor.py | 10 ---------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index 3d97aa23dd..3692c691a8 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -1446,9 +1446,7 @@ def progression_of_hypertensive_disorders(self, individual_id, property_prefix): pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'eclampsia_onset', self.sim.date) - current_log.info(key='maternal_complication', data={'person': individual_id, - 'type': 'eclampsia', - 'timing': timing}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['eclampsia'] +=1 # Or from mild to severe gestational hypertension, risk reduced by treatment if df.at[individual_id, f'{property_prefix}_htn_disorders'] == 'gest_htn': @@ -1461,9 +1459,7 @@ def progression_of_hypertensive_disorders(self, individual_id, property_prefix): if risk_prog_gh_sgh > self.rng.random_sample(): df.at[individual_id, f'{property_prefix}_htn_disorders'] = 'severe_gest_htn' - current_log.info(key='maternal_complication', data={'person': individual_id, - 'type': 'severe_gest_htn', - 'timing': timing}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['severe_gest_htn'] +=1 # Or from severe gestational hypertension to severe pre-eclampsia... if df.at[individual_id, f'{property_prefix}_htn_disorders'] == 'severe_gest_htn': @@ -1471,9 +1467,7 @@ def progression_of_hypertensive_disorders(self, individual_id, property_prefix): df.at[individual_id, f'{property_prefix}_htn_disorders'] = 'severe_pre_eclamp' mni[individual_id]['new_onset_spe'] = True - current_log.info(key='maternal_complication', data={'person': individual_id, - 'type': 'severe_pre_eclamp', - 'timing': timing}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['severe_pre_eclamp'] +=1 # Or from mild pre-eclampsia to severe pre-eclampsia... if df.at[individual_id, f'{property_prefix}_htn_disorders'] == 'mild_pre_eclamp': @@ -1481,9 +1475,7 @@ def progression_of_hypertensive_disorders(self, individual_id, property_prefix): df.at[individual_id, f'{property_prefix}_htn_disorders'] = 'severe_pre_eclamp' mni[individual_id]['new_onset_spe'] = True - current_log.info(key='maternal_complication', data={'person': individual_id, - 'type': 'severe_pre_eclamp', - 'timing': timing}) + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['severe_pre_eclamp'] +=1 def apply_risk_of_early_postpartum_death(self, individual_id): """ diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index c8cbbcc748..183877f4ed 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -525,11 +525,6 @@ def onset(eq): store_dalys_in_mni(person, mni, f'{df.at[person, "pn_anaemia_following_pregnancy"]}_anaemia_pp_onset', self.sim.date) - logger.info(key='maternal_complication', data={'person': person, - 'type': f'{df.at[person, "pn_anaemia_following_pregnancy"]}' - f'_anaemia', - 'timing': 'postnatal'}) - # --------------------------------------- HYPERTENSION ------------------------------------------ # For women who are still experiencing a hypertensive disorder of pregnancy we determine if that will now # resolve @@ -1062,11 +1057,6 @@ def apply(self, individual_id): store_dalys_in_mni(individual_id, mni, f'{df.at[individual_id, "pn_anaemia_following_pregnancy"]}_' f'anaemia_pp_onset', self.sim.date) - logger.info(key='maternal_complication', - data={'person': individual_id, - 'type': f'{df.at[individual_id, "pn_anaemia_following_pregnancy"]}_anaemia', - 'timing': 'postnatal'}) - # -------------------------------------------- HYPERTENSION ----------------------------------------------- # For women who remain hypertensive after delivery we apply a probability that this will resolve in the # first week after birth From 0acf362f4b9fa0b094615f5455459c6c60bee99a Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 11 Nov 2024 15:33:11 +0000 Subject: [PATCH 16/27] removed unused variable --- src/tlo/methods/labour.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index 3692c691a8..e48b48ecba 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -1414,13 +1414,6 @@ def progression_of_hypertensive_disorders(self, individual_id, property_prefix): params = self.current_parameters mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info - if property_prefix == 'ps': - timing = 'intrapartum' - current_log = logger - else: - timing = 'postnatal' - current_log = logger_pn - # n.b. on birth women whose hypertension will continue into the postnatal period have their disease state stored # in a new property therefore antenatal/intrapartum hypertension is 'ps_htn_disorders' and postnatal is # 'pn_htn_disorders' hence the use of property prefix variable (as this function is called before and after From 8cc45abeb348d76df8ef323769ba275fff3b1770 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:12:18 +0000 Subject: [PATCH 17/27] additional counters and updates to mnh_ --- src/tlo/methods/postnatal_supervisor.py | 5 +++- src/tlo/methods/pregnancy_helper_functions.py | 23 ++++++++++++--- src/tlo/methods/pregnancy_supervisor.py | 28 +++++++++++++------ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 183877f4ed..63cd9b2a60 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -646,6 +646,9 @@ def log_new_progressed_cases(disease): # Those women who die the on_death function in demography is applied for person in die_from_htn.loc[die_from_htn].index: + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['severe_gestational_hypertension_m_death'] += 1 + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter['direct_mat_death'] += 1 + self.sim.modules['Demography'].do_death(individual_id=person, cause='severe_gestational_hypertension', originating_module=self.sim.modules['PostnatalSupervisor']) @@ -838,7 +841,7 @@ def apply_risk_of_maternal_or_neonatal_death_postnatal(self, mother_or_child, in # If this neonate will die then we make the appropriate changes if self.rng.random_sample() < risk_of_death: - + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{cause}_n_death'] += 1 self.sim.modules['Demography'].do_death(individual_id=individual_id, cause=cause, originating_module=self.sim.modules['PostnatalSupervisor']) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 3ae5b5b264..7e2bd7bed7 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -10,6 +10,11 @@ def generate_mnh_outcome_counter(): + """ + Returns a dictionary with relevant maternal and newborn health outcomes to be used by modules as a counter for + each outcome as the simulation moves forward in time. + """ + outcome_list = [ # early/abortive outcomes 'ectopic_unruptured', 'ectopic_ruptured','multiple_pregnancy', 'twin_birth', 'placenta_praevia', 'spontaneous_abortion', 'induced_abortion', 'complicated_spontaneous_abortion', @@ -39,13 +44,22 @@ def generate_mnh_outcome_counter(): 'macrosomia', 'small_for_gestational_age', 'early_onset_sepsis', 'late_onset_sepsis', # death outcomes - 'direct_mat_death', 'six_week_survivors', + 'direct_mat_death', 'six_week_survivors','induced_abortion_m_death', 'spontaneous_abortion_m_death', + 'ectopic_pregnancy_m_death', 'severe_gestational_hypertension_m_death', + 'severe_pre_eclampsia_m_death', 'eclampsia_m_death', 'antepartum_haemorrhage_m_death', + 'antenatal_sepsis_m_death', + 'intrapartum_sepsis_m_death', 'postpartum_sepsis_m_death', 'uterine_rupture_m_death', + 'postpartum_haemorrhage_m_death','secondary_postpartum_haemorrhage_m_death', + 'early_onset_sepsis_n_death', 'late_onset_sepsis_n_death', 'encephalopathy_n_death', + 'neonatal_respiratory_depression_n_death', 'preterm_other_n_death', + 'respiratory_distress_syndrome_n_death', 'congenital_heart_anomaly_n_death', + 'limb_or_musculoskeletal_anomaly_n_death', 'urogenital_anomaly_n_death', 'digestive_anomaly_n_death', + 'other_anomaly_n_death', # service coverage outcomes 'anc0', 'anc1', 'anc2', 'anc3', 'anc4', 'anc5', 'anc6', 'anc7', 'anc8', 'anc8+', 'home_birth_delivery', 'hospital_delivery', 'health_centre_delivery', - 'm_pnc0', 'm_pnc1', 'm_pnc2', 'm_pnc3+', 'n_pnc0', 'n_pnc1', 'n_pnc2', 'n_pnc3+', - ] + 'm_pnc0', 'm_pnc1', 'm_pnc2', 'm_pnc3+', 'n_pnc0', 'n_pnc1', 'n_pnc2', 'n_pnc3+'] mnh_outcome_counter = {k: 0 for k in outcome_list} @@ -392,7 +406,8 @@ def calculate_risk_of_death_from_causes(self, risks): # Now use the list of probabilities to conduct a weighted random draw to determine primary cause of death cause_of_death = self.rng.choice(list(risks.keys()), p=probs) - # Return the primary cause of death so that it can be passed to the demography function + # Return and log the primary cause of death so that it can be passed to the demography function + self.sim.modules['PregnancySupervisor'].mnh_outcome_counter[f'{cause_of_death}_{target}_death'] += 1 return cause_of_death else: # Return false if death will not occur diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 648cde5a59..af27caaada 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -1230,6 +1230,9 @@ def apply_risk_of_death_from_hypertension(self, gestation_of_interest): if not at_risk_of_death_htn.loc[at_risk_of_death_htn].empty: # Those women who die have InstantaneousDeath scheduled for person in at_risk_of_death_htn.loc[at_risk_of_death_htn].index: + self.mnh_outcome_counter['severe_gestational_hypertension_m_death'] += 1 + self.mnh_outcome_counter['direct_mat_death'] += 1 + self.sim.modules['Demography'].do_death(individual_id=person, cause='severe_gestational_hypertension', originating_module=self.sim.modules['PregnancySupervisor']) @@ -1983,6 +1986,8 @@ def apply(self, individual_id): pregnancy_helper_functions.log_mni_for_maternal_death(self.module, individual_id) mni[individual_id]['delete_mni'] = True + self.module.mnh_outcome_counter[f'{self.cause}_m_death'] += 1 + self.module.mnh_outcome_counter['direct_mat_death'] += 1 self.sim.modules['Demography'].do_death(individual_id=individual_id, cause=f'{self.cause}', originating_module=self.sim.modules['PregnancySupervisor']) @@ -2264,15 +2269,20 @@ def rate (count, denom, multiplier): neonatal_deaths = len(df[(df['date_of_death'].dt.year == self.sim.date.year - 1) & (df['age_days'] <= 28)]) stillbirths = c['antenatal_stillbirth'] + c['intrapartum_stillbirth'] - logger.info(key='deaths_and_stillbirths', - data={'antenatal_sbr': rate(c['antenatal_stillbirth'], total_births, 1000), - 'intrapartum_sbr': rate(c['intrapartum_stillbirth'], total_births, 1000), - 'total_stillbirths': stillbirths, - 'sbr': rate(stillbirths, total_births, 1000), - 'neonatal_deaths': neonatal_deaths, - 'nmr' : rate(neonatal_deaths, live_births, 1000), - 'direct_maternal_deaths': c['direct_mat_death'], - 'direct_mmr': rate(c['direct_mat_death'], live_births, 100_000)}) + general_death_data = {'antenatal_sbr': rate(c['antenatal_stillbirth'], total_births, 1000), + 'intrapartum_sbr': rate(c['intrapartum_stillbirth'], total_births, 1000), + 'total_stillbirths': stillbirths, + 'sbr': rate(stillbirths, total_births, 1000), + 'neonatal_deaths': neonatal_deaths, + 'nmr' : rate(neonatal_deaths, live_births, 1000), + 'direct_maternal_deaths': c['direct_mat_death'], + 'direct_mmr': rate(c['direct_mat_death'], live_births, 100_000)} + + cause_specific_mmrs = {k: rate(c[k], total_births, 100_000) for k in c if 'm_death' in k} + cause_specific_nmrs = {k: rate(c[k], total_births, 1000) for k in c if 'n_death' in k} + general_death_data.update({**cause_specific_mmrs, **cause_specific_nmrs}) + + logger.info(key='deaths_and_stillbirths', data=general_death_data) # Finally log coverage of key health services anc1 = sum(c[f'anc{i}'] for i in range(1, 9)) + c['anc8+'] From 593f9cd12bdac930aeacd30dd1fcacafef7d0062 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:20:07 +0000 Subject: [PATCH 18/27] update calculate_risk_of_death_from_causes to allow for cause of death logging --- src/tlo/methods/pregnancy_helper_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 7e2bd7bed7..98b207a7ac 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -378,7 +378,7 @@ def log_mni_for_maternal_death(self, person_id): logger.info(key='death_mni', data=mni_to_log) -def calculate_risk_of_death_from_causes(self, risks): +def calculate_risk_of_death_from_causes(self, risks, target): """ This function calculates risk of death in the context of one or more 'death causing' complications in a mother of a newborn. In addition, it determines if the complication(s) will cause death or not. If death occurs the function @@ -518,7 +518,7 @@ def apply_effect_of_anaemia(cause): risks.update(risk) # Call return the result from calculate_risk_of_death_from_causes function - return calculate_risk_of_death_from_causes(self, risks) + return calculate_risk_of_death_from_causes(self, risks, target='m') # if she is not at risk of death as she has no complications we return false to the module return False @@ -588,7 +588,7 @@ def check_for_risk_of_death_from_cause_neonatal(self, individual_id): risks.update(risk) # Return the result from calculate_risk_of_death_from_causes function (returns primary cause of death or False) - return calculate_risk_of_death_from_causes(self, risks) + return calculate_risk_of_death_from_causes(self, risks, target='n') # if they is not at risk of death as they has no complications we return False to the module return False From f7c9e68a744310adea02ff132fbde0a535a2ce36 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:30:12 +0000 Subject: [PATCH 19/27] updates to resource files (new parameters, tweaked values) --- resources/ResourceFile_LabourSkilledBirthAttendance.xlsx | 4 ++-- resources/ResourceFile_PregnancySupervisor.xlsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/ResourceFile_LabourSkilledBirthAttendance.xlsx b/resources/ResourceFile_LabourSkilledBirthAttendance.xlsx index 9d61093db8..6ae417a123 100644 --- a/resources/ResourceFile_LabourSkilledBirthAttendance.xlsx +++ b/resources/ResourceFile_LabourSkilledBirthAttendance.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebd3aff1d77254f7cd7bb1dc019e2e6a86700dcd6009cecd11a70da4b85a3c93 -size 31777 +oid sha256:4bc6e58d3ebfadb58552a58fe7a0592195d28cb7d29fcff3c7c2859f2f3ea559 +size 31686 diff --git a/resources/ResourceFile_PregnancySupervisor.xlsx b/resources/ResourceFile_PregnancySupervisor.xlsx index 3ee3406d7a..c2d293b239 100644 --- a/resources/ResourceFile_PregnancySupervisor.xlsx +++ b/resources/ResourceFile_PregnancySupervisor.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbd0974016b374a3286e379ef3cb1f297307ef410880c3f902e9439194a6d37d -size 22225 +oid sha256:b3e5c31d51a35b370a0317866af3b955172dfc4780ef44d053054c772ac83d0a +size 22625 From 3940cce1669c3147bd10cf84e4fdfaa235d60c88 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:35:22 +0000 Subject: [PATCH 20/27] new params and updates for intervention analysis in pregnancy_supervisor.py --- src/tlo/methods/pregnancy_supervisor.py | 50 +++++++++++++------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index af27caaada..9635fa1a73 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -389,6 +389,17 @@ def __init__(self, name=None, resourcefilepath=None): 'sens_analysis_max': Parameter( Types.BOOL, 'Signals within the analysis event and code that sensitivity analysis is being undertaken in ' 'which the maximum coverage of ANC is enforced'), + + 'interventions_analysis': Parameter( + Types.BOOL, 'Signals within the analysis event and code that intervention-based analysis is being ' + 'undertaken in which the maximum coverage of ANC is enforced'), + 'interventions_under_analysis': Parameter( + Types.LIST, 'Intervention for which their availability is being changed during analysis'), + 'all_interventions': Parameter( + Types.LIST, 'Names of all maternal and newborn health interventions'), + 'intervention_analysis_availability': Parameter( + Types.REAL, 'Probability an intervention which is included in "interventions_under_analysis" will be' + 'available'), } PROPERTIES = { @@ -437,6 +448,8 @@ def __init__(self, name=None, resourcefilepath=None): } def read_parameters(self, data_folder): + p = self.parameters + # load parameters from the resource file parameter_dataframe = pd.read_excel(Path(self.resourcefilepath) / 'ResourceFile_PregnancySupervisor.xlsx', sheet_name='parameter_values') @@ -527,13 +540,19 @@ def initialise_simulation(self, sim): sim.date + DateOffset(years=1)) # ...and register and schedule the parameter update event - sim.schedule_event(ParameterUpdateEvent(self), - Date(2015, 1, 1)) + if self.sim.date.year < 2015: + sim.schedule_event(ParameterUpdateEvent(self), + Date(2015, 1, 1)) + else: + sim.schedule_event(ParameterUpdateEvent(self), + Date(self.sim.date.year, 1, 1)) # ... and finally register and schedule the parameter override event. This is used in analysis scripts to change # key parameters after the simulation 'burn in' period. The event is schedule to run even when analysis is not # conducted but no changes to parameters can be made. - sim.schedule_event(PregnancyAnalysisEvent(self), Date(params['analysis_year'], 1, 1)) + + if self.sim.date.year <= params['analysis_year']: + sim.schedule_event(PregnancyAnalysisEvent(self), Date(params['analysis_year'], 1, 1)) # ==================================== LINEAR MODEL EQUATIONS ================================================= # Next we scale linear models according to distribution of predictors in the dataframe at baseline @@ -1668,7 +1687,7 @@ def do_at_generic_first_appt_emergency( "PregnancySupervisor" ].abortion_complications if abortion_complications.has_any( - [person_id], "sepsis", "injury", "haemorrhage", first=True + [person_id], "sepsis", "injury", "haemorrhage", "other", first=True ): event = HSI_CareOfWomenDuringPregnancy_PostAbortionCaseManagement( module=self.sim.modules["CareOfWomenDuringPregnancy"], @@ -1997,7 +2016,7 @@ def apply(self, individual_id): df.at[individual_id, 'ps_ectopic_pregnancy'] = 'none' else: - self.module.abortion_complications.unset(individual_id, 'sepsis', 'haemorrhage', 'injury') + self.module.abortion_complications.unset(individual_id, 'sepsis', 'haemorrhage', 'injury', 'other') df.at[individual_id, 'ac_received_post_abortion_care'] = False if individual_id in mni: @@ -2121,7 +2140,8 @@ def apply(self, population): params['alternative_anc_quality'] or \ params['alternative_ip_anc_quality'] or \ params['sens_analysis_max'] or \ - params['sens_analysis_min']: + params['sens_analysis_min'] or \ + params['interventions_analysis']: # Update this parameter which is a signal used in the pregnancy_helper_function_file to ensure that # alternative functionality for determining availability of interventions only occurs when analysis is @@ -2147,11 +2167,6 @@ def apply(self, population): params['prob_anc1_months_2_to_4'] = [1.0, 0, 0] params['prob_late_initiation_anc4'] = 0 - # Finally, remove squeeze factor threshold for ANC attendance to ensure that higher levels of ANC - # coverage can be reached with current logic - self.sim.modules['CareOfWomenDuringPregnancy'].current_parameters['squeeze_factor_threshold_anc'] = \ - 10_000 - if params['alternative_anc_quality'] or params['sens_analysis_max']: # Override the availability of IPTp consumables with the set level of coverage @@ -2160,23 +2175,10 @@ def apply(self, population): self.sim.modules['HealthSystem'].override_availability_of_consumables( {iptp: params['anc_availability_probability']}) - # And then override the quality parameters in the model - for parameter in ['prob_intervention_delivered_urine_ds', 'prob_intervention_delivered_bp', - 'prob_intervention_delivered_syph_test', 'prob_intervention_delivered_gdm_test']: - self.sim.modules['CareOfWomenDuringPregnancy'].current_parameters[parameter] = \ - params['anc_availability_probability'] - - if params['alternative_ip_anc_quality']: - self.sim.modules['CareOfWomenDuringPregnancy'].current_parameters['squeeze_factor_threshold_an'] = \ - 10_000 - if params['sens_analysis_max']: for parameter in ['prob_seek_anc5', 'prob_seek_anc6', 'prob_seek_anc7', 'prob_seek_anc8']: self.sim.modules['CareOfWomenDuringPregnancy'].current_parameters[parameter] = 1.0 - self.sim.modules['CareOfWomenDuringPregnancy'].current_parameters['squeeze_factor_threshold_anc'] = \ - 10_000 - params['prob_seek_care_pregnancy_complication'] = 1.0 self.sim.modules['CareOfWomenDuringPregnancy'].current_parameters['prob_adherent_ifa'] = 1.0 From efca3d8c17038435a70190d0d33b247b881b2daa Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:39:02 +0000 Subject: [PATCH 21/27] Added new function to determine whether an intervention can be delivered or not (can be used for analysis). updates to care_of_women_during_pregnancy.py to use this function (and some small fixes) --- .../methods/care_of_women_during_pregnancy.py | 357 +++++++----------- src/tlo/methods/pregnancy_helper_functions.py | 233 ++++-------- 2 files changed, 217 insertions(+), 373 deletions(-) diff --git a/src/tlo/methods/care_of_women_during_pregnancy.py b/src/tlo/methods/care_of_women_during_pregnancy.py index d1d52f076a..61e2446594 100644 --- a/src/tlo/methods/care_of_women_during_pregnancy.py +++ b/src/tlo/methods/care_of_women_during_pregnancy.py @@ -281,7 +281,7 @@ def get_and_store_pregnancy_item_codes(self): # ---------------------------------- IRON AND FOLIC ACID ------------------------------------------------------ # 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 + {ic('Ferrous Salt + Folic Acid, tablet, 200 + 0.25 mg'): 1} # --------------------------------- BALANCED ENERGY AND PROTEIN ---------------------------------------------- # Dose changes at run time @@ -728,42 +728,26 @@ def screening_interventions_delivered_at_every_contact(self, hsi_event): params = self.current_parameters mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info - hypertension_diagnosed = False - proteinuria_diagnosed = False + proteinuria_diagnosed = pregnancy_helper_functions.check_int_deliverable( + self, int_name='urine_dipstick', + hsi_event=hsi_event, + q_param=[params['prob_intervention_delivered_urine_ds']], + cons=self.item_codes_preg_consumables['urine_dipstick'], + dx_test='urine_dipstick_protein') - # Delivery of the intervention is conditioned on a random draw against a probability that the intervention - # would be delivered (used to calibrate to SPA data - acts as proxy for clinical quality) - if self.rng.random_sample() < params['prob_intervention_delivered_urine_ds']: + hypertension_diagnosed = pregnancy_helper_functions.check_int_deliverable( + self, int_name='bp_measurement', + hsi_event=hsi_event, + q_param=[params['prob_intervention_delivered_bp']], + equipment={'Sphygmomanometer'}, + dx_test='blood_pressure_measurement') - # check consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, cons=self.item_codes_preg_consumables['urine_dipstick'], opt_cons=None) + if hypertension_diagnosed and not df.at[person_id, 'ac_gest_htn_on_treatment'] and \ + (df.at[person_id, 'ps_htn_disorders'] != 'none') and pd.isnull(mni[person_id]['hypertension_onset']): - # 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 - if avail and self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='urine_dipstick_protein', hsi_event=hsi_event): - - # We use a temporary variable to store if proteinuria is detected - proteinuria_diagnosed = True - logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'dipstick'}) - - # The process is repeated for blood pressure monitoring - if self.rng.random_sample() < params['prob_intervention_delivered_bp']: - hsi_event.add_equipment({'Sphygmomanometer'}) - - if self.sim.modules['HealthSystem'].dx_manager.run_dx_test(dx_tests_to_run='blood_pressure_measurement', - hsi_event=hsi_event): - hypertension_diagnosed = True - logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'bp_measurement'}) - - if not df.at[person_id, 'ac_gest_htn_on_treatment'] and\ - (df.at[person_id, 'ps_htn_disorders'] != 'none') and pd.isnull(mni[person_id]['hypertension' - '_onset']): - - # We store date of onset to calculate dalys- only women who are aware of diagnosis experience DALYs - # (see daly weight for hypertension) - pregnancy_helper_functions.store_dalys_in_mni(person_id, mni, 'hypertension_onset', self.sim.date) + # We store date of onset to calculate dalys- only women who are aware of diagnosis experience DALYs + # (see daly weight for hypertension) + pregnancy_helper_functions.store_dalys_in_mni(person_id, mni, 'hypertension_onset', self.sim.date) # If either high blood pressure or proteinuria are detected (or both) we assume this woman needs to be admitted # for further treatment following this ANC contact @@ -798,10 +782,12 @@ def iron_and_folic_acid_supplementation(self, hsi_event): 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, cons=updated_cons, opt_cons=None) + iron_folic_acid_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='iron_folic_acid', + hsi_event=hsi_event, + cons=updated_cons) - if avail: + if iron_folic_acid_delivered: logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'iron_folic_acid'}) # Importantly, only women who will be adherent to iron will experience the benefits of the @@ -835,11 +821,10 @@ def balance_energy_and_protein_supplementation(self, hsi_event): 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, cons=updated_cons, opt_cons=None) + bep_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='protein_supplement', hsi_event=hsi_event, cons=updated_cons) - # 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): + if bep_delivered and (df.at[person_id, 'li_bmi'] == 1): df.at[person_id, 'ac_receiving_bep_supplements'] = True logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'b_e_p'}) @@ -901,10 +886,10 @@ def calcium_supplementation(self, hsi_event): 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, cons=updated_cons, opt_cons=None) + calcium_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='calcium_supplement', hsi_event=hsi_event, cons=updated_cons) - if avail: + if calcium_delivered: df.at[person_id, 'ac_receiving_calcium_supplements'] = True logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'calcium'}) @@ -923,16 +908,13 @@ def point_of_care_hb_testing(self, hsi_event): logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'hb_screen'}) - # Run check against probability of testing being delivered - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + hb_test_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='hb_test', hsi_event=hsi_event, cons=self.item_codes_preg_consumables['hb_test'], - opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) + opt_cons=self.item_codes_preg_consumables['blood_test_equipment'], + dx_test='point_of_care_hb_test') - # We run the test through the dx_manager and if a woman has anaemia and its detected she will be admitted - # for further care - if avail and self.sim.modules['HealthSystem'].dx_manager.run_dx_test(dx_tests_to_run='point_of_care_hb_test', - hsi_event=hsi_event): + if hb_test_delivered: df.at[person_id, 'ac_to_be_admitted'] = True def albendazole_administration(self, hsi_event): @@ -972,7 +954,7 @@ def hep_b_testing(self, hsi_event): if not self.check_intervention_should_run_and_update_mni(person_id, 'hep_b_1', 'hep_b_2'): return - # This intervention is a place holder prior to the Hepatitis B module being coded + # This intervention is a placeholder prior to the Hepatitis B module being coded # Define the consumables avail = hsi_event.get_consumables(item_codes=cons['hep_b_test'], optional_item_codes=cons['blood_test_equipment']) @@ -996,30 +978,25 @@ def syphilis_screening_and_treatment(self, hsi_event): if not self.check_intervention_should_run_and_update_mni(person_id, 'syph_1', 'syph_2'): return - # See if she will receive testing - if self.rng.random_sample() < params['prob_intervention_delivered_syph_test']: - logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'syphilis_test'}) - - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, - cons=self.item_codes_preg_consumables['syphilis_test'], - opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) + syph_test_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='syphilis_test', hsi_event=hsi_event, + q_param=[params['prob_intervention_delivered_syph_test']], + cons=self.item_codes_preg_consumables['syphilis_test'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment'], + dx_test='blood_test_syphilis') - test = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='blood_test_syphilis', hsi_event=hsi_event) + if syph_test_delivered: - # If the testing occurs and detects syphilis she will get treatment (if consumables are available) - if avail and test: + syph_treatment_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='syphilis_treatment', hsi_event=hsi_event, + cons=self.item_codes_preg_consumables['syphilis_treatment'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, - cons=self.item_codes_preg_consumables['syphilis_treatment'], - opt_cons=self.item_codes_preg_consumables['blood_test_equipment']) + if syph_treatment_delivered: - if avail: - # We assume that treatment is 100% effective at curing infection - df.at[person_id, 'ps_syphilis'] = False - logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'syphilis_treat'}) + # We assume that treatment is 100% effective at curing infection + df.at[person_id, 'ps_syphilis'] = False + logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'syphilis_treat'}) def hiv_testing(self, hsi_event): """ @@ -1075,34 +1052,21 @@ def gdm_screening(self, hsi_event): if df.at[person_id, 'li_bmi'] >= 4 or df.at[person_id, 'ps_prev_gest_diab'] or df.at[person_id, 'ps_prev_stillbirth']: - # If they are available, the test is conducted - if self.rng.random_sample() < params['prob_intervention_delivered_gdm_test']: - - avail = pregnancy_helper_functions.return_cons_avail( - 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 - if avail: - hsi_event.add_equipment({'Glucometer'}) + gdm_screening_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='gdm_test', hsi_event=hsi_event, + q_param=[params['prob_intervention_delivered_gdm_test']], + cons=self.item_codes_preg_consumables['gdm_test'], + opt_cons=self.item_codes_preg_consumables['blood_test_equipment'], + dx_test='blood_test_glucose', + equipment={'Glucometer'}) - if ( - self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='blood_test_glucose', hsi_event=hsi_event) - ): - logger.info(key='anc_interventions', data={'mother': person_id, 'intervention': 'gdm_screen'}) - mni[person_id]['anc_ints'].append('gdm_screen') + if gdm_screening_delivered: + if df.at[person_id, 'ac_gest_diab_on_treatment'] == 'none': - # We assume women with a positive GDM screen will be admitted (if they are not already receiving - # outpatient care) - if df.at[person_id, 'ac_gest_diab_on_treatment'] == 'none': - - # Store onset after diagnosis as daly weight is tied to diagnosis - pregnancy_helper_functions.store_dalys_in_mni(person_id, mni, 'gest_diab_onset', - self.sim.date) - df.at[person_id, 'ac_to_be_admitted'] = True + # Store onset after diagnosis as daly weight is tied to diagnosis + pregnancy_helper_functions.store_dalys_in_mni(person_id, mni, 'gest_diab_onset', + self.sim.date) + df.at[person_id, 'ac_to_be_admitted'] = True def interventions_delivered_each_visit_from_anc2(self, hsi_event): """This function contains a collection of interventions that are delivered to women every time they attend ANC @@ -1217,20 +1181,16 @@ def full_blood_count_testing(self, hsi_event): df = self.sim.population.props person_id = hsi_event.target - # Run dx_test for anaemia... - # If a woman is not truly anaemic but the FBC returns a result of anaemia, due to tests specificity, we - # assume the reported anaemia is mild - hsi_event.get_consumables(item_codes=self.item_codes_preg_consumables['blood_test_equipment']) - hsi_event.add_equipment({'Analyser, Haematology'}) - - test_result = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='full_blood_count_hb', hsi_event=hsi_event) + full_blood_count_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='full_blood_count', hsi_event=hsi_event, + opt_cons=self.item_codes_preg_consumables['blood_test_equipment'], + equipment={'Analyser, Haematology'}, dx_test='full_blood_count_hb') - if test_result and (df.at[person_id, 'ps_anaemia_in_pregnancy'] == 'none'): - return 'non_severe' + if full_blood_count_delivered and (df.at[person_id, 'ps_anaemia_in_pregnancy'] == 'none'): + return 'none' # If the test correctly identifies a woman's anaemia we assume it correctly identifies its severity - if test_result and (df.at[person_id, 'ps_anaemia_in_pregnancy'] != 'none'): + if full_blood_count_delivered and (df.at[person_id, 'ps_anaemia_in_pregnancy'] != 'none'): return df.at[person_id, 'ps_anaemia_in_pregnancy'] # We return a none value if no anaemia was detected @@ -1247,24 +1207,18 @@ def antenatal_blood_transfusion(self, individual_id, hsi_event): """ df = self.sim.population.props params = self.current_parameters + l_params = self.sim.modules['Labour'].current_parameters store_dalys_in_mni = pregnancy_helper_functions.store_dalys_in_mni mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info - # Check for consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + blood_transfusion_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='blood_transfusion', hsi_event=hsi_event, + q_param=[l_params['prob_hcw_avail_blood_tran'], l_params['mean_hcw_competence_hp']], 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', - hsi_event=hsi_event) + opt_cons=self.item_codes_preg_consumables['blood_test_equipment'], + equipment={'Drip stand', 'Infusion pump'}) - # If the blood is available we assume the intervention can be delivered - if avail and sf_check: - pregnancy_helper_functions.log_met_need(self, 'blood_tran', hsi_event) - - hsi_event.add_equipment({'Drip stand', 'Infusion pump'}) + if blood_transfusion_delivered: # If the woman is receiving blood due to anaemia we apply a probability that a transfusion of 2 units # RBCs will correct this woman's severe anaemia @@ -1287,11 +1241,10 @@ def initiate_maintenance_anti_hypertensive_treatment(self, individual_id, hsi_ev 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, cons=updated_cons, opt_cons=None) + oral_anti_htns_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='oral_antihypertensives', hsi_event=hsi_event, cons=updated_cons) - # If the consumables are available then the woman is started on treatment - if avail: + if oral_anti_htns_delivered: df.at[individual_id, 'ac_gest_htn_on_treatment'] = True def initiate_treatment_for_severe_hypertension(self, individual_id, hsi_event): @@ -1305,16 +1258,13 @@ def initiate_treatment_for_severe_hypertension(self, individual_id, hsi_event): """ df = self.sim.population.props - # Define the consumables and check their availability - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + iv_anti_htns_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='iv_antihypertensives', hsi_event=hsi_event, cons=self.item_codes_preg_consumables['iv_antihypertensives'], - opt_cons=self.item_codes_preg_consumables['iv_drug_equipment']) + opt_cons=self.item_codes_preg_consumables['iv_drug_equipment'], + equipment={'Drip stand', 'Infusion pump'}) - # If they are available then the woman is started on treatment - if avail: - pregnancy_helper_functions.log_met_need(self, 'iv_htns', hsi_event) - hsi_event.add_equipment({'Drip stand', 'Infusion pump'}) + if iv_anti_htns_delivered: # We assume women treated with antihypertensives would no longer be severely hypertensive- meaning they # are not at risk of death from severe gestational hypertension in the PregnancySupervisor event @@ -1338,22 +1288,17 @@ def treatment_for_severe_pre_eclampsia_or_eclampsia(self, individual_id, hsi_eve :param hsi_event: HSI event in which the function has been called """ df = self.sim.population.props + l_params = self.sim.modules['Labour'].current_parameters - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + mag_sulph_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='mgso4', hsi_event=hsi_event, + q_param=[l_params['prob_hcw_avail_anticonvulsant'], l_params['mean_hcw_competence_hp']], cons=self.item_codes_preg_consumables['magnesium_sulfate'], - opt_cons=self.item_codes_preg_consumables['eclampsia_management_optional']) + opt_cons=self.item_codes_preg_consumables['eclampsia_management_optional'], + equipment={'Drip stand', 'Infusion pump'}) - # check HCW will deliver intervention - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.sim.modules['Labour'], - sf='anticonvulsant', - hsi_event=hsi_event) - - # If available deliver the treatment - if avail and sf_check: + if mag_sulph_delivered: df.at[individual_id, 'ac_mag_sulph_treatment'] = True - pregnancy_helper_functions.log_met_need(self, 'mag_sulph', hsi_event) - hsi_event.add_equipment({'Drip stand', 'Infusion pump'}) def antibiotics_for_prom(self, individual_id, hsi_event): """ @@ -1363,20 +1308,17 @@ def antibiotics_for_prom(self, individual_id, hsi_event): :param hsi_event: HSI event in which the function has been called """ df = self.sim.population.props + l_params = self.sim.modules['Labour'].current_parameters - # check consumables and whether HCW are available to deliver the intervention - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + abx_prom_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='abx_for_prom', hsi_event=hsi_event, + q_param=[l_params['prob_hcw_avail_iv_abx'], l_params['mean_hcw_competence_hp']], 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', - hsi_event=hsi_event) + opt_cons=self.item_codes_preg_consumables['iv_drug_equipment'], + equipment={'Drip stand', 'Infusion pump'}) - if avail and sf_check: + if abx_prom_delivered: df.at[individual_id, 'ac_received_abx_for_prom'] = True - hsi_event.add_equipment({'Drip stand', 'Infusion pump'}) def ectopic_pregnancy_treatment_doesnt_run(self, hsi_event): """ @@ -2378,14 +2320,15 @@ def apply(self, person_id, squeeze_factor): # Women for whom immediate delivery is indicated are schedule to move straight to the labour model where # they will have the appropriate properties set and facility delivery at a hospital scheduled (mode of # delivery will match the recommended mode here) - if df.at[person_id, 'ac_admitted_for_immediate_delivery'] in ('induction_now', 'caesarean_now'): + if df.at[person_id, 'ac_admitted_for_immediate_delivery'] in ('avd_now', 'induction_now', 'caesarean_now'): self.sim.schedule_event(LabourOnsetEvent(self.sim.modules['Labour'], person_id), self.sim.date) # Women who require delivery BUT are not in immediate risk of morbidity/mortality will remain as # inpatients until they can move to the labour model. Currently it is possible for women to go into # labour whilst as an inpatient - it is assumed they are delivered via the mode recommended here # (i.e induction/caesarean) - elif df.at[person_id, 'ac_admitted_for_immediate_delivery'] in ('caesarean_future', 'induction_future'): + elif df.at[person_id, 'ac_admitted_for_immediate_delivery'] in ('avd_future', 'caesarean_future', + 'induction_future'): # Here we calculate how many days this woman needs to remain on the antenatal ward before she can go # for delivery (assuming delivery is indicated to occur at 37 weeks) @@ -2522,11 +2465,11 @@ def schedule_gdm_event_and_checkup(): 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, cons=updated_cons, opt_cons=None) + gdm_orals_delivered = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='gdm_treatment_orals', hsi_event=self, + q_param=None, cons=updated_cons) - # If the meds are available women are started on that treatment - if avail: + if gdm_orals_delivered: df.at[person_id, 'ac_gest_diab_on_treatment'] = 'orals' # Assume new treatment is effective in controlling blood glucose on initiation @@ -2546,10 +2489,11 @@ def schedule_gdm_event_and_checkup(): 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, cons=updated_cons, opt_cons=None) + gdm_insulin_delivered = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='gdm_treatment_insulin', hsi_event=self, + q_param=None, cons=updated_cons) - if avail: + if gdm_insulin_delivered: df.at[person_id, 'ac_gest_diab_on_treatment'] = 'insulin' df.at[person_id, 'ps_gest_diab'] = 'controlled' @@ -2584,67 +2528,37 @@ def apply(self, person_id, squeeze_factor): df = self.sim.population.props mother = df.loc[person_id] abortion_complications = self.sim.modules['PregnancySupervisor'].abortion_complications + l_params = self.sim.modules['Labour'].current_parameters if not mother.is_alive or not abortion_complications.has_any([person_id], 'sepsis', 'haemorrhage', 'injury', 'other', first=True): return - # Request baseline PAC consumables - baseline_cons = pregnancy_helper_functions.return_cons_avail( - 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'], - sf='retained_prod', - hsi_event=self) + pac_cons = self.module.item_codes_preg_consumables['post_abortion_care_core'] + pac_opt_cons = self.module.item_codes_preg_consumables['post_abortion_care_optional'] - # Add used equipment if intervention can happen - if baseline_cons and sf_check: - self.add_equipment({'D&C set', 'Suction Curettage machine', 'Drip stand', 'Infusion pump'}) - - # Then we determine if a woman gets treatment for her complication depending on availability of the baseline - # consumables (misoprostol) or a HCW who can conduct MVA/DC (we dont model equipment) and additional - # consumables for management of her specific complication if abortion_complications.has_any([person_id], 'sepsis', first=True): + pac_cons.update(self.module.item_codes_preg_consumables['post_abortion_care_sepsis_core']) + pac_opt_cons.update(self.module.item_codes_preg_consumables['post_abortion_care_sepsis_optional']) - cons_for_sepsis_pac = pregnancy_helper_functions.return_cons_avail( - 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, - cons=self.module.item_codes_preg_consumables['blood_transfusion'], - opt_cons=self.module.item_codes_preg_consumables['iv_drug_equipment']) + if abortion_complications.has_any([person_id], 'haemorrhage', first=True): + pac_cons.update(self.module.item_codes_preg_consumables['blood_transfusion']) + pac_opt_cons.update(self.module.item_codes_preg_consumables['iv_drug_equipment']) - cons_for_shock = pregnancy_helper_functions.return_cons_avail( - 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 abortion_complications.has_any([person_id], 'injury', first=True): + pac_cons.update(self.module.item_codes_preg_consumables['post_abortion_care_shock']) + pac_opt_cons.update(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 + pac_delivered = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='post_abortion_care_core', hsi_event=self, + q_param=[l_params['prob_hcw_avail_retained_prod'], l_params['mean_hcw_competence_hp']], + cons=pac_cons, + opt_cons=pac_opt_cons, + equipment={'D&C set', 'Suction Curettage machine', 'Drip stand', 'Infusion pump'}) - elif abortion_complications.has_any([person_id], 'injury', first=True): - cons_for_shock = pregnancy_helper_functions.return_cons_avail( - 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 - - elif abortion_complications.has_any([person_id], 'other', first=True) and (baseline_cons or sf_check): + if pac_delivered: df.at[person_id, 'ac_received_post_abortion_care'] = True - if df.at[person_id, 'ac_received_post_abortion_care']: - pregnancy_helper_functions.log_met_need(self.module, 'pac', self) def did_not_run(self): logger.debug(key='message', data='HSI_CareOfWomenDuringPregnancy_PostAbortionCaseManagement: did not run') @@ -2674,21 +2588,20 @@ def __init__(self, module, person_id): def apply(self, person_id, squeeze_factor): df = self.sim.population.props mother = df.loc[person_id] + l_params = self.sim.modules['Labour'].current_parameters if not mother.is_alive or (mother.ps_ectopic_pregnancy == 'none'): return - # We define the required consumables and check their availability - avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, + ep_surg_delivered = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='ectopic_pregnancy_treatment', hsi_event=self, + q_param=[l_params['prob_hcw_avail_surg'], l_params['mean_hcw_competence_hp']], cons=self.module.item_codes_preg_consumables['ectopic_pregnancy_core'], - opt_cons=self.module.item_codes_preg_consumables['ectopic_pregnancy_optional']) + opt_cons=self.module.item_codes_preg_consumables['ectopic_pregnancy_optional'], + equipment={'Laparotomy Set'}) - # If they are available then treatment can go ahead - if avail: + if ep_surg_delivered: self.sim.modules['PregnancySupervisor'].mother_and_newborn_info[person_id]['delete_mni'] = True - pregnancy_helper_functions.log_met_need(self.module, 'ep_case_mang', self) - self.add_equipment({'Laparotomy Set'}) # For women who have sought care after they have experienced rupture we use this treatment variable to # reduce risk of death (women who present prior to rupture do not pass through the death event as we assume diff --git a/src/tlo/methods/pregnancy_helper_functions.py b/src/tlo/methods/pregnancy_helper_functions.py index 98b207a7ac..e12388ebf4 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -77,124 +77,79 @@ def get_list_of_items(self, item_list): return codes - -def return_cons_avail(self, hsi_event, cons, opt_cons): +def check_int_deliverable(self, int_name, hsi_event, + q_param=None, cons=None, opt_cons=None, equipment=None, dx_test=None): """ - 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 - analysis boolean parameters) then the availability is determined via the health system module. Otherwise a - fixed probability of availability is used to force a set level of intervention coverage - availability - :param self: module - :param hsi_event: hsi_event calling the intervention - :param cons_dict: dictionary containing the consumables for that module - :param info: information on core, optional and number of consumables requested - :return: BOOL + This function is called to determine if an intervention within the MNH modules can be delivered to an individual + during a given HSI. This applied to all MNH interventions. If analyses are being conducted in which the probability + of intervention delivery should be set explicitly, this is achieved during this function. Otherwise, probability of + intervention delivery is determined by any module-level quality parameters, consumable availability, and + (if applicable) the results of any dx_tests. Equipment is also declared. + + :param self: module + param int_name: items for code look up + param hsi_event: module + param q_param: items for code look up + param cons: module + param opt_cons: items for code look up + param equipment: module + param dx_test: items for code look up """ - mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info - ps_params = self.sim.modules['PregnancySupervisor'].current_parameters - la_params = self.sim.modules['Labour'].current_parameters - - 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=cons, - optional_item_codes=opt_cons) - - if not available and (hsi_event.target in mni) and (hsi_event != 'AntenatalCare_Outpatient'): - mni[hsi_event.target]['cons_not_avail'] = True - - return available - - else: - 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 - if 'AntenatalCare' in hsi_event.TREATMENT_ID: - params = self.sim.modules['PregnancySupervisor'].current_parameters - else: - params = self.sim.modules['Labour'].current_parameters - - # Store the names of the parameters which indicate that analysis is being conducted against specific HSIs - analysis_dict = {'AntenatalCare_Outpatient': ['alternative_anc_quality', 'anc_availability_probability'], - 'AntenatalCare_Inpatient': ['alternative_ip_anc_quality', 'ip_anc_availability_probability'], - 'AntenatalCare_FollowUp': ['alternative_ip_anc_quality', 'ip_anc_availability_probability'], - 'DeliveryCare_Basic': ['alternative_bemonc_availability', 'bemonc_cons_availability'], - 'DeliveryCare_Neonatal': ['alternative_bemonc_availability', 'bemonc_cons_availability'], - 'DeliveryCare_Comprehensive': ['alternative_cemonc_availability', 'cemonc_cons_availability'], - 'PostnatalCare_Maternal': ['alternative_pnc_quality', 'pnc_availability_probability'], - 'PostnatalCare_Comprehensive': ['alternative_pnc_quality', 'pnc_availability_probability'], - 'PostnatalCare_Neonatal': ['alternative_pnc_quality', 'pnc_availability_probability']} - # Cycle through each HSI of interest. If this HSI is requesting a consumable, analysis is being conducted and - # the simulation date is greater than the predetermined analysis date, then a random draw against a probability - # of consumable availability is used - for k in analysis_dict: - if (hsi_event.TREATMENT_ID == k) and params[analysis_dict[k][0]]: - if self.rng.random_sample() < params[analysis_dict[k][1]]: - available = True - else: - available = False + df = self.sim.population.props + individual_id = hsi_event.target + p_params = self.sim.modules['PregnancySupervisor'].current_parameters + l_params = self.sim.modules['Labour'].current_parameters - # If the consumable is not available this is stored in the MNI dictionary - if not available and (hsi_event.target in mni) and (hsi_event != 'AntenatalCare_Outpatient'): - mni[hsi_event.target]['cons_not_avail'] = True + # assert int_name in p_params['all_interventions'] - return available + # Firstly, we determine if an analysis is currently being conducted during which the probability of intervention + # delivery is being overridden + # To do: replace this parameter + if (p_params['interventions_analysis'] and p_params['ps_analysis_in_progress'] and + (int_name in p_params['interventions_under_analysis'])): + # If so, we determine if this intervention will be delivered given the set probability of delivery. + can_int_run_analysis = self.rng.random_sample() < p_params['intervention_analysis_availability'] -def check_emonc_signal_function_will_run(self, sf, hsi_event): - """ - Called during/after labour to determine if a B/CEmONC function will run depending on the availability of HCWs - trained in the intervention and their competence - :param self: module - :param sf: signal function of interest - :param hsi_event: hsi_event calling the intervention - :return: BOOL - """ - la_params = self.sim.modules['Labour'].current_parameters - ps_params = self.sim.modules['PregnancySupervisor'].current_parameters - mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info + # The intervention has no effect + if not can_int_run_analysis: + return False - def see_if_sf_will_run(): - # Determine competence parameter to use (higher presumed competence in hospitals) - if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a': - competence = la_params['mean_hcw_competence_hc'][0] else: - competence = la_params['mean_hcw_competence_hp'][1] + # The intervention will have an effect. If this is an intervention which leads to an outcome dependent on + # correct identification of a condition through a dx_test we account for that here. + if dx_test is not None: + test = self.sim.modules['HealthSystem'].dx_manager.dx_tests[dx_test] - comp_result = self.rng.random_sample() < competence - hcw_result = self.rng.random_sample() < la_params[f'prob_hcw_avail_{sf}'] + if test[0].target_categories is None and (df.at[individual_id, test[0].property]): + return True - # If HCW is available and staff are competent at delivering the intervention then it will be delivered - if comp_result and hcw_result: - return True + elif ((test[0].target_categories is not None) and + (df.at[individual_id, test[0].property] in test[0].target_categories)): + return True - # Otherwise log within the mni - if not comp_result and (hsi_event.target in mni): - mni[hsi_event.target]['comp_not_avail'] = True - if not hcw_result and (hsi_event.target in mni): - mni[hsi_event.target]['hcw_not_avail'] = True + else: + return False - return False + else: + return True - if not la_params['la_analysis_in_progress'] and not ps_params['ps_analysis_in_progress']: - return see_if_sf_will_run() + elif (l_params['la_analysis_in_progress'] or + (p_params['ps_analysis_in_progress'] and not p_params['interventions_under_analysis'])): - else: if 'AntenatalCare' in hsi_event.TREATMENT_ID: params = self.sim.modules['PregnancySupervisor'].current_parameters else: params = self.sim.modules['Labour'].current_parameters # Define HSIs and analysis parameters of interest - analysis_dict = {'AntenatalCare_Inpatient': ['alternative_ip_anc_quality', 'ip_anc_availability_probability'], - 'DeliveryCare_Basic': ['alternative_bemonc_availability', 'bemonc_availability'], - 'DeliveryCare_Neonatal': ['alternative_bemonc_availability', 'bemonc_availability'], - 'DeliveryCare_Comprehensive': ['alternative_cemonc_availability', 'cemonc_availability'], + analysis_dict = {'AntenatalCare_Outpatient': ['alternative_anc_quality', 'anc_availability_probability'], + 'AntenatalCare_Inpatient': ['alternative_ip_anc_quality', 'ip_anc_availability_probability'], + 'AntenatalCare_FollowUp': ['alternative_ip_anc_quality', 'ip_anc_availability_probability'], + 'DeliveryCare_Basic': ['alternative_bemonc_availability', 'bemonc_cons_availability'], + 'DeliveryCare_Neonatal': ['alternative_bemonc_availability', 'bemonc_cons_availability'], + 'DeliveryCare_Comprehensive': ['alternative_cemonc_availability', 'cemonc_cons_availability'], 'PostnatalCare_Maternal': ['alternative_pnc_quality', 'pnc_availability_probability'], 'PostnatalCare_Comprehensive': ['alternative_pnc_quality', 'pnc_availability_probability'], 'PostnatalCare_Neonatal': ['alternative_pnc_quality', 'pnc_availability_probability']} @@ -206,69 +161,42 @@ def see_if_sf_will_run(): if self.rng.random_sample() < params[analysis_dict[k][1]]: return True - # If the event wont run it is stored in the HSI for the relevant woman - elif hsi_event.target in mni: - barrier = self.rng.choice(['comp_not_avail', 'hcw_not_avail']) - mni[hsi_event.target][barrier] = True + else: return False - return see_if_sf_will_run() + else: + # If analysis is not being conducted, intervention delivery is dependent on quality parameters, consumable + # availability and dx_test results + quality = False + consumables = False + test = False -def log_met_need(self, intervention, hsi): - """ - This function is called whenever a woman receives an intervention used as treatment for a potentially fatal - complication within the maternal or neonatal health modules. The intervention is logged within the labour.detail - logging and is used to calculate treatment coverage/met need - :param self: module - :param intervention: intervention to be logged - :param hsi: hsi_event calling the intervention - """ - df = self.sim.population.props - logger = logging.getLogger("tlo.methods.labour.detail") - person = df.loc[hsi.target] - person_id = hsi.target - - # Interventions with single indications are simply logged - if intervention in ('ep_case_mang', 'pac', 'avd_other', 'avd_ol', 'avd_spe_ec', 'uterotonics', 'man_r_placenta', - 'pph_surg', 'ur_surg', 'neo_resus', 'neo_sep_supportive_care', 'neo_sep_abx'): - logger.info(key='intervention', data={'person_id': person_id, 'int': intervention}) - - # For interventions with multiple indications the HSI and properties of the individual are used to determine the - # correct name for the logged intervention. - elif (intervention == 'mag_sulph') or (intervention == 'iv_htns'): - if hsi.TREATMENT_ID == 'AntenatalCare_Inpatient': - tp = ['an', 'ps'] - elif hsi.TREATMENT_ID == 'DeliveryCare_Basic': - tp = ['la', 'ps'] - else: - tp = ['pn', 'pn'] + if ((q_param is None) or + all([self.rng.random_sample() < value for value in q_param])): + quality = True - logger.info(key='intervention', - data={'person_id': person_id, - 'int': f'{intervention}_{tp[0]}_{df.at[person_id, f"{tp[1]}_htn_disorders"]}'}) + # todo: should this only be if qual and cons are also true? + if equipment is not None: + hsi_event.add_equipment(equipment) - elif intervention == 'sepsis_abx': - if (hsi.TREATMENT_ID == 'DeliveryCare_Basic') or (hsi.TREATMENT_ID == 'AntenatalCare_Inpatient'): - logger.info(key='intervention', data={'person_id': person_id, 'int': 'abx_an_sepsis'}) + if ((cons is None) or + (hsi_event.get_consumables(item_codes=cons if not None else [], + optional_item_codes=opt_cons if not None else []))): + consumables = True - elif hsi.TREATMENT_ID == 'PostnatalCare_Maternal': - logger.info(key='intervention', data={'person_id': person_id, 'int': 'abx_pn_sepsis'}) + if cons is None and opt_cons is not None: + hsi_event.get_consumables(item_codes=[], optional_item_codes=opt_cons) - elif intervention == 'blood_tran': - if hsi.TREATMENT_ID == 'AntenatalCare_Inpatient': - logger.info(key='intervention', data={'person_id': person_id, 'int': 'blood_tran_anaemia'}) + if ((dx_test is None) or + (self.sim.modules['HealthSystem'].dx_manager.run_dx_test(dx_tests_to_run=dx_test, hsi_event=hsi_event))): + test = True - elif hsi.TREATMENT_ID == 'DeliveryCare_Comprehensive': - if ((person.la_antepartum_haem != 'none') or (person.ps_antepartum_haemorrhage != 'none')) and \ - not person.la_is_postpartum: - logger.info(key='intervention', data={'person_id': person_id, 'int': 'blood_tran_aph'}) - - elif person.la_uterine_rupture and not person.la_is_postpartum: - logger.info(key='intervention', data={'person_id': person_id, 'int': 'blood_tran_ur'}) + if quality and consumables and test: + return True - elif (person.la_postpartum_haem or person.pn_postpartum_haem_secondary) and person.la_is_postpartum: - logger.info(key='intervention', data={'person_id': person_id, 'int': 'blood_tran_pph'}) + else: + return False def scale_linear_model_at_initialisation(self, model, parameter_key): @@ -340,7 +268,10 @@ def update_current_parameter_dictionary(self, list_position): for key, value in self.parameters.items(): if isinstance(value, list): - self.current_parameters[key] = self.parameters[key][list_position] + if not value or (len(value)) == 1 or key in ('interventions_under_analysis', 'all_interventions'): + self.current_parameters[key] = self.parameters[key] + else: + self.current_parameters[key] = self.parameters[key][list_position] else: if list_position == 0: self.current_parameters[key] = self.parameters[key] From 5bbd1f3bbcfd9adffbf05bbdf695430a1b10492b Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:42:04 +0000 Subject: [PATCH 22/27] Added new function to determine whether updates to labour.py to use this function (and some small fixes) --- src/tlo/methods/labour.py | 340 +++++++++++++------------------------- 1 file changed, 115 insertions(+), 225 deletions(-) diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index e48b48ecba..70c5b3165e 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -153,8 +153,6 @@ def __init__(self, name=None, resourcefilepath=None): 'list_limits_for_defining_term_status': Parameter( Types.LIST, 'List of number of days of gestation used to define term, early preterm, late preterm and ' 'post term delivery'), - 'allowed_interventions': Parameter( - Types.LIST, 'list of interventions allowed to run, used in analysis'), # BIRTH WEIGHT... 'mean_birth_weights': Parameter( @@ -867,19 +865,17 @@ def get_and_store_labour_item_codes(self): {ic('Infant resuscitator, clear plastic + mask + bag_each_CMST'): 1} def initialise_simulation(self, sim): - # Update self.current_parameters - pregnancy_helper_functions.update_current_parameter_dictionary(self, list_position=0) - # We call the following function to store the required consumables for the simulation run within the appropriate # dictionary self.get_and_store_labour_item_codes() # We set the LoggingEvent to run on the last day of each year to produce statistics for that year - sim.schedule_event(LabourLoggingEvent(self), sim.date + DateOffset(days=1)) + sim.schedule_event(LabourLoggingEvent(self), sim.date + DateOffset(years=1)) # Schedule analysis event - sim.schedule_event(LabourAndPostnatalCareAnalysisEvent(self), - Date(self.current_parameters['analysis_year'], 1, 1)) + if self.sim.date.year <= self.current_parameters['analysis_year']: + sim.schedule_event(LabourAndPostnatalCareAnalysisEvent(self), + Date(self.current_parameters['analysis_year'], 1, 1)) # This list contains all the women who are currently in labour and is used for checks/testing self.women_in_labour = [] @@ -900,7 +896,6 @@ def initialise_simulation(self, sim): full_blood_count_hb_pn=DxTest( property='pn_anaemia_following_pregnancy', target_categories=['mild', 'moderate', 'severe'], - item_codes=self.item_codes_lab_consumables['full_blood_count'], sensitivity=1.0), ) @@ -1443,7 +1438,10 @@ def progression_of_hypertensive_disorders(self, individual_id, property_prefix): # Or from mild to severe gestational hypertension, risk reduced by treatment if df.at[individual_id, f'{property_prefix}_htn_disorders'] == 'gest_htn': - if df.at[individual_id, 'la_maternal_hypertension_treatment']: + if (df.at[individual_id, 'la_maternal_hypertension_treatment'] or + df.at[individual_id, 'la_gest_htn_on_treatment'] or + df.at[individual_id, 'ac_gest_htn_on_treatment']): + risk_prog_gh_sgh = params['prob_progression_gest_htn'] * params[ 'anti_htns_treatment_effect_progression'] else: @@ -1633,11 +1631,7 @@ def prophylactic_labour_interventions(self, hsi_event): params = self.current_parameters mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info person_id = hsi_event.target - - # params['allowed_interventions'] contains a list of interventions delivered in this module. Removal of - # interventions from this list within test/analysis will stop this intervention from running - if 'prophylactic_labour_interventions' not in params['allowed_interventions']: - return + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' # We determine if the HCW will administer antibiotics for women with premature rupture of membranes if df.at[person_id, 'ps_premature_rupture_of_membranes']: @@ -1648,20 +1642,14 @@ def prophylactic_labour_interventions(self, hsi_event): mni[person_id]['abx_for_prom_given'] = True else: - # Run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='iv_abx', - hsi_event=hsi_event) - # If she has not already receive antibiotics, we check for consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + abx_prom_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='abx_for_prom', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_iv_abx'], params[f'mean_hcw_competence_{deliv_location}']], 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 - # week of life - if avail and sf_check: + if abx_prom_delivered: mni[person_id]['abx_for_prom_given'] = True # ------------------------------ STEROIDS FOR PRETERM LABOUR ------------------------------- @@ -1669,14 +1657,13 @@ def prophylactic_labour_interventions(self, hsi_event): if mni[person_id]['labour_state'] == 'early_preterm_labour' or \ mni[person_id]['labour_state'] == 'late_preterm_labour': - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + steroids_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='antenatal_corticosteroids', hsi_event=hsi_event, + q_param=None, 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 - if avail: + if steroids_delivered: mni[person_id]['corticosteroids_given'] = True def determine_delivery_mode_in_spe_or_ec(self, person_id, hsi_event, complication): @@ -1715,12 +1702,10 @@ def assessment_and_treatment_of_severe_pre_eclampsia_mgso4(self, hsi_event, labo df = self.sim.population.props params = self.current_parameters person_id = hsi_event.target + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' # Women who have been admitted for delivery due to severe pre-eclampsia AND have already received magnesium # before moving to the labour ward do not receive the intervention again - if 'assessment_and_treatment_of_severe_pre_eclampsia' not in params['allowed_interventions']: - return - if (df.at[person_id, 'ps_htn_disorders'] == 'severe_pre_eclamp') or \ (df.at[person_id, 'pn_htn_disorders'] == 'severe_pre_eclamp'): @@ -1728,21 +1713,14 @@ def assessment_and_treatment_of_severe_pre_eclampsia_mgso4(self, hsi_event, labo if (df.at[person_id, 'ac_admitted_for_immediate_delivery'] == 'none') and (labour_stage == 'ip'): self.determine_delivery_mode_in_spe_or_ec(person_id, hsi_event, 'spe') - # Run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='anticonvulsant', - hsi_event=hsi_event) - - # Define and check for the required consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + mag_sulph_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='mgso4', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_anticonvulsant'], params[f'mean_hcw_competence_{deliv_location}']], 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 - if avail and sf_check: + if mag_sulph_delivered: df.at[person_id, 'la_severe_pre_eclampsia_treatment'] = True - pregnancy_helper_functions.log_met_need(self, 'mag_sulph', hsi_event) def assessment_and_treatment_of_hypertension(self, hsi_event, labour_stage): """ @@ -1754,26 +1732,17 @@ def assessment_and_treatment_of_hypertension(self, hsi_event, labour_stage): """ df = self.sim.population.props person_id = hsi_event.target - params = self.current_parameters - - # If the treatment is not allowed to be delivered or it has already been delivered the function won't run - if 'assessment_and_treatment_of_hypertension' not in params['allowed_interventions']: - return + # If the treatment has already been delivered the function won't run if (df.at[person_id, 'ps_htn_disorders'] != 'none') or (df.at[person_id, 'pn_htn_disorders'] != 'none'): - # Then query if these consumables are available during this HSI - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + iv_anti_htns_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='iv_antihypertensives', hsi_event=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 - # women with severe pre-eclampsia and eclampsia - if avail: + if iv_anti_htns_delivered: df.at[person_id, 'la_maternal_hypertension_treatment'] = True - pregnancy_helper_functions.log_met_need(self, 'iv_htns', hsi_event) if (labour_stage == 'ip') and (df.at[person_id, 'ps_htn_disorders'] == 'severe_gest_htn'): df.at[person_id, 'ps_htn_disorders'] = 'gest_htn' @@ -1782,9 +1751,11 @@ def assessment_and_treatment_of_hypertension(self, hsi_event, labour_stage): 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: + oral_anti_htns_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='oral_antihypertensives', hsi_event=hsi_event, cons=cons) + + if oral_anti_htns_delivered: df.at[person_id, 'la_gest_htn_on_treatment'] = True def assessment_and_treatment_of_eclampsia(self, hsi_event, labour_stage): @@ -1800,30 +1771,24 @@ def assessment_and_treatment_of_eclampsia(self, hsi_event, labour_stage): df = self.sim.population.props person_id = hsi_event.target params = self.current_parameters - - if 'assessment_and_treatment_of_eclampsia' not in params['allowed_interventions']: - return + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' if (df.at[person_id, 'ps_htn_disorders'] == 'eclampsia') or \ (df.at[person_id, 'pn_htn_disorders'] == 'eclampsia'): - # Run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='anticonvulsant', - hsi_event=hsi_event) - - # define and check required consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + mag_sulph_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='mgso4', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_anticonvulsant'], params[f'mean_hcw_competence_{deliv_location}']], 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') - if avail and sf_check: + if mag_sulph_delivered: + # Treatment with magnesium reduces a womans risk of death from eclampsia df.at[person_id, 'la_eclampsia_treatment'] = True - pregnancy_helper_functions.log_met_need(self, 'mag_sulph', hsi_event) def assessment_for_assisted_vaginal_delivery(self, hsi_event, indication): """ @@ -1838,6 +1803,7 @@ def assessment_for_assisted_vaginal_delivery(self, hsi_event, indication): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.current_parameters person_id = hsi_event.target + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' def refer_for_cs(): if not indication == 'other': @@ -1848,32 +1814,24 @@ def refer_for_cs(): elif indication == 'ol': mni[person_id]['cs_indication'] = indication - if ('assessment_and_treatment_of_obstructed_labour' not in params['allowed_interventions']) or \ - (df.at[person_id, 'ac_admitted_for_immediate_delivery'] == 'caesarean_now') or \ + if (df.at[person_id, 'ac_admitted_for_immediate_delivery'] == 'caesarean_now') or \ (df.at[person_id, 'ac_admitted_for_immediate_delivery'] == 'caesarean_future'): return # Define the consumables... if df.at[person_id, 'la_obstructed_labour'] or (indication in ('spe_ec', 'other')): + # We assume women with CPD cannot be delivered via AVD and will require a caesarean if not mni[person_id]['cpd']: - # 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, + avd_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='avd', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_avd'], params[f'mean_hcw_competence_{deliv_location}']], 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', - hsi_event=hsi_event) + opt_cons=self.item_codes_lab_consumables['obstructed_labour'], + equipment={'Delivery Forceps', 'Vacuum extractor'}) - if avail and sf_check: - # Add used equipment - hsi_event.add_equipment({'Delivery Forceps', 'Vacuum extractor'}) - - pregnancy_helper_functions.log_met_need(self, f'avd_{indication}', hsi_event) + if avd_delivered: # If AVD was successful then we record the mode of delivery. We use this variable to reduce # risk of intrapartum still birth when applying risk in the death event @@ -1902,9 +1860,7 @@ def assessment_and_treatment_of_maternal_sepsis(self, hsi_event, labour_stage): df = self.sim.population.props params = self.current_parameters person_id = hsi_event.target - - if 'assessment_and_treatment_of_maternal_sepsis' not in params['allowed_interventions']: - return + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' if ( df.at[person_id, 'la_sepsis'] or @@ -1912,20 +1868,14 @@ def assessment_and_treatment_of_maternal_sepsis(self, hsi_event, labour_stage): ((labour_stage == 'ip') and df.at[person_id, 'ps_chorioamnionitis']) or (labour_stage == 'pp' and df.at[person_id, 'pn_sepsis_late_postpartum'])): - # run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='iv_abx', - hsi_event=hsi_event) - - # Define and check available consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + sepsis_treatment_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='sepsis_treatment', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_iv_abx'], params[f'mean_hcw_competence_{deliv_location}']], 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: + if sepsis_treatment_delivered: df.at[person_id, 'la_sepsis_treatment'] = True - pregnancy_helper_functions.log_met_need(self, 'sepsis_abx', hsi_event) def assessment_and_plan_for_antepartum_haemorrhage(self, hsi_event): """ @@ -1936,13 +1886,9 @@ def assessment_and_plan_for_antepartum_haemorrhage(self, hsi_event): (STR) 'hc' == health centre, 'hp' == hospital """ df = self.sim.population.props - params = self.current_parameters mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info person_id = hsi_event.target - if 'assessment_and_plan_for_referral_antepartum_haemorrhage' not in params['allowed_interventions']: - return - # We assume that any woman who has been referred from antenatal inpatient care due to haemorrhage are # automatically scheduled for blood transfusion if (df.at[person_id, 'ps_antepartum_haemorrhage'] != 'none') and (df.at[person_id, @@ -1968,13 +1914,9 @@ def assessment_for_referral_uterine_rupture(self, hsi_event): (STR) 'hc' == health centre, 'hp' == hospital """ df = self.sim.population.props - params = self.current_parameters mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info person_id = hsi_event.target - if 'assessment_and_plan_for_referral_uterine_rupture' not in params['allowed_interventions']: - return - # When uterine rupture is present, the mni dictionary is updated to allow treatment to be scheduled if df.at[person_id, 'la_uterine_rupture']: mni[person_id]['referred_for_surgery'] = True @@ -1992,23 +1934,15 @@ def active_management_of_the_third_stage_of_labour(self, hsi_event): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.current_parameters person_id = hsi_event.target + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' - if 'active_management_of_the_third_stage_of_labour' not in params['allowed_interventions']: - return - - # Define and check available consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + amtsl_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='amtsl', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_uterotonic'], params[f'mean_hcw_competence_{deliv_location}']], 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', - hsi_event=hsi_event) - - # This treatment reduces a womans risk of developing uterine atony AND retained placenta, both of which are - # preceding causes of postpartum haemorrhage - if avail and sf_check: + if amtsl_delivered: mni[person_id]['amtsl_given'] = True def assessment_and_treatment_of_pph_uterine_atony(self, hsi_event): @@ -2023,24 +1957,17 @@ def assessment_and_treatment_of_pph_uterine_atony(self, hsi_event): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.current_parameters person_id = hsi_event.target - - if 'assessment_and_treatment_of_pph_uterine_atony' not in params['allowed_interventions']: - return + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' if df.at[person_id, 'la_postpartum_haem'] and not mni[person_id]['retained_placenta']: - # Define and check available consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + pph_treatment_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='pph_treatment_uterotonics', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_uterotonic'], params[f'mean_hcw_competence_{deliv_location}']], 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', - hsi_event=hsi_event) - - if avail and sf_check: - pregnancy_helper_functions.log_met_need(self, 'uterotonics', hsi_event) + if pph_treatment_delivered: # We apply a probability that this treatment will stop a womans bleeding in the first instance # meaning she will not require further treatment @@ -2067,33 +1994,24 @@ def assessment_and_treatment_of_pph_retained_placenta(self, hsi_event): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.current_parameters person_id = hsi_event.target - - if 'assessment_and_treatment_of_pph_retained_placenta' not in params['allowed_interventions']: - return + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' if ( (df.at[person_id, 'la_postpartum_haem'] and mni[person_id]['retained_placenta']) or df.at[person_id, 'pn_postpartum_haem_secondary'] ): - # Log the consumables but dont condition the treatment on their availability - the primary mechanism of this - # intervention doesnt require consumables - hsi_event.get_consumables(item_codes=self.item_codes_lab_consumables['pph_optional']) - - # run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='man_r_placenta', - hsi_event=hsi_event) + pph_mrrp_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='pph_treatment_mrrp', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_man_r_placenta'], params[f'mean_hcw_competence_{deliv_location}']], + opt_cons=self.item_codes_lab_consumables['pph_optional']) - # Similar to uterotonics we apply a probability that this intervention will successfully stop - # bleeding to ensure some women go on to require further care - if sf_check: - pregnancy_helper_functions.log_met_need(self, 'man_r_placenta', hsi_event) + if pph_mrrp_delivered: if params['prob_successful_manual_removal_placenta'] > self.rng.random_sample(): df.at[person_id, 'la_postpartum_haem'] = False mni[person_id]['retained_placenta'] = False - pregnancy_helper_functions.log_met_need(self, 'man_r_placenta', hsi_event) if df.at[person_id, 'pn_postpartum_haem_secondary']: df.at[person_id, 'pn_postpartum_haem_secondary'] = False @@ -2112,22 +2030,16 @@ def surgical_management_of_pph(self, hsi_event): person_id = hsi_event.target df = self.sim.population.props params = self.current_parameters + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' - # 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, + pph_surg_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='pph_treatment_surg', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_surg'], params[f'mean_hcw_competence_{deliv_location}']], 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', - hsi_event=hsi_event) - - if avail and sf_check: - # Add used equipment - hsi_event.add_equipment(hsi_event.healthcare_system.equipment.from_pkg_names('Major Surgery')) + opt_cons=self.item_codes_lab_consumables['obstetric_surgery_optional'], + equipment=hsi_event.healthcare_system.equipment.from_pkg_names('Major Surgery')) + if pph_surg_delivered: # determine if uterine preserving surgery will be successful treatment_success_pph = params['success_rate_pph_surgery'] > self.rng.random_sample() @@ -2139,10 +2051,6 @@ def surgical_management_of_pph(self, hsi_event): self.pph_treatment.set(person_id, 'hysterectomy') df.at[person_id, 'la_has_had_hysterectomy'] = True - # log intervention delivery - if self.pph_treatment.has_all(person_id, 'surgery') or df.at[person_id, 'la_has_had_hysterectomy']: - pregnancy_helper_functions.log_met_need(self, 'pph_surg', hsi_event) - def blood_transfusion(self, hsi_event): """ This function represents the blood transfusion during or after labour. This function is called during @@ -2154,22 +2062,17 @@ def blood_transfusion(self, hsi_event): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.current_parameters df = self.sim.population.props + deliv_location = 'hc' if hsi_event.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' - # Check consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + blood_transfusion_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='blood_transfusion', hsi_event=hsi_event, + q_param=[params['prob_hcw_avail_blood_tran'], params[f'mean_hcw_competence_{deliv_location}']], 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', - hsi_event=hsi_event) - - if avail and sf_check: - hsi_event.add_equipment({'Drip stand', 'Infusion pump'}) + opt_cons=self.item_codes_lab_consumables['blood_test_equipment'], + equipment={'Drip stand', 'Infusion pump'}) + if blood_transfusion_delivered: mni[person_id]['received_blood_transfusion'] = True - pregnancy_helper_functions.log_met_need(self, 'blood_tran', hsi_event) # We assume that anaemia is corrected by blood transfusion if df.at[person_id, 'pn_anaemia_following_pregnancy'] != 'none': @@ -2191,27 +2094,23 @@ def assessment_and_treatment_of_anaemia(self, hsi_event): mother = df.loc[person_id] mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info - # Add used equipment - hsi_event.add_equipment({'Analyser, Haematology'}) - - # Use dx_test function to assess anaemia status - test_result = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='full_blood_count_hb_pn', hsi_event=hsi_event) + full_blood_count_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='full_blood_count', hsi_event=hsi_event, + opt_cons=self.item_codes_lab_consumables['blood_test_equipment'], + equipment={'Analyser, Haematology'}, dx_test='full_blood_count_hb_pn') - hsi_event.get_consumables(item_codes=self.item_codes_lab_consumables['blood_test_equipment']) - - # Check consumables - if test_result: + if full_blood_count_delivered: # Start iron and folic acid supplementation for women not already receiving this if not mother.la_iron_folic_acid_postnatal: days = int((6 - df.at[person_id, 'pn_postnatal_period_in_weeks']) * 7) dose = days * 3 cons = {_i: dose for _i in self.item_codes_lab_consumables['iron_folic_acid']} - avail = hsi_event.get_consumables(item_codes=cons) - # Start iron and folic acid treatment - if avail: + iron_folic_acid_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='iron_folic_acid', hsi_event=hsi_event, cons=cons) + + if iron_folic_acid_delivered: df.at[person_id, 'la_iron_folic_acid_postnatal'] = True if self.rng.random_sample() < params['effect_of_ifa_for_resolving_anaemia']: @@ -2257,11 +2156,13 @@ def interventions_delivered_pre_discharge(self, hsi_event): # ------------------------------- Postnatal iron and folic acid --------------------------------------------- cons = {_i: params['number_ifa_tablets_required_postnatally'] for _i in self.item_codes_lab_consumables['iron_folic_acid']} - avail = hsi_event.get_consumables(item_codes=cons) + + iron_folic_acid_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='iron_folic_acid', hsi_event=hsi_event, cons=cons) # Women are started on iron and folic acid for the next three months which reduces risk of anaemia in the # postnatal period - if avail and (self.rng.random_sample() < params['prob_adherent_ifa']): + if iron_folic_acid_delivered and (self.rng.random_sample() < params['prob_adherent_ifa']): df.at[person_id, 'la_iron_folic_acid_postnatal'] = True def run_if_receives_skilled_birth_attendance_cant_run(self, hsi_event): @@ -2874,6 +2775,7 @@ def apply(self, person_id, squeeze_factor): mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info df = self.sim.population.props params = self.module.current_parameters + deliv_location = 'hc' if self.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' if not df.at[person_id, 'is_alive']: return @@ -2899,22 +2801,18 @@ def apply(self, person_id, squeeze_factor): elif df.at[person_id, 'ac_admitted_for_immediate_delivery'] == 'avd_now': self.module.assessment_for_assisted_vaginal_delivery(self, indication='spe_ec') - # LOG CONSUMABLES FOR DELIVERY... - # We assume all deliveries require this basic package of consumables - avail = pregnancy_helper_functions.return_cons_avail( - self.module, self, - cons=self.module.item_codes_lab_consumables['delivery_core'], + birth_kit_used = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='birth_kit', + hsi_event=self, cons=self.module.item_codes_lab_consumables['delivery_core'], opt_cons=self.module.item_codes_lab_consumables['delivery_optional']) - # Add used equipment - self.add_equipment({'Delivery set', 'Weighing scale', 'Stethoscope, foetal, monaural, Pinard, plastic', - 'Resuscitaire', 'Sphygmomanometer', 'Tray, emergency', 'Suction machine', - 'Thermometer', 'Drip stand', 'Infusion pump'}) - - # If the clean delivery kit consumable is available, we assume women benefit from clean delivery - if avail: + if birth_kit_used: mni[person_id]['clean_birth_practices'] = True + # Add used equipment + self.add_equipment({'Delivery set', 'Weighing scale', 'Stethoscope, foetal, monaural, Pinard, plastic', + 'Resuscitaire', 'Sphygmomanometer', 'Tray, emergency', 'Suction machine', + 'Thermometer', 'Drip stand', 'Infusion pump'}) # ===================================== PROPHYLACTIC CARE =================================================== # The following function manages the consumables and administration of prophylactic interventions in labour # (clean delivery practice, antibiotics for PROM, steroids for preterm labour) @@ -2982,14 +2880,13 @@ def apply(self, person_id, squeeze_factor): if not mni[person_id]['sought_care_for_complication']: # 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, 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, - sf='neo_resus', - hsi_event=self) - if sf_check and avail: + + neo_resus_delivered = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='neo_resus', hsi_event=self, + q_param=[params['prob_hcw_avail_neo_resus'], params[f'mean_hcw_competence_{deliv_location}']], + cons=self.module.item_codes_lab_consumables['resuscitation']) + + if neo_resus_delivered: mni[person_id]['neo_will_receive_resus_if_needed'] = True # ========================================== SCHEDULING CEMONC CARE ========================================= @@ -3190,6 +3087,7 @@ def apply(self, person_id, squeeze_factor): df = self.sim.population.props mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.module.current_parameters + deliv_location = 'hc' if self.ACCEPTED_FACILITY_LEVEL == '1a' else 'hp' # We use the variable self.timing to differentiate between women sent to this event during labour and women # sent after labour @@ -3199,22 +3097,18 @@ def apply(self, person_id, squeeze_factor): # delivered if mni[person_id]['referred_for_cs'] and self.timing == 'intrapartum': - # 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, + cs_delivered = pregnancy_helper_functions.check_int_deliverable( + self.module, int_name='caesarean_section', hsi_event=self, + q_param=[params['prob_hcw_avail_surg'], params[f'mean_hcw_competence_{deliv_location}']], 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', - hsi_event=self) - # Block CS delivery for this analysis if params['la_analysis_in_progress'] and (params['cemonc_availability'] == 0.0): logger.debug(key='message', data="cs delivery blocked for this analysis") - elif (avail and sf_check) or (mni[person_id]['cs_indication'] == 'other'): + elif cs_delivered or (mni[person_id]['cs_indication'] == 'other'): + # If intervention is delivered - add used equipment self.add_equipment(self.healthcare_system.equipment.from_pkg_names('Major Surgery')) @@ -3241,9 +3135,6 @@ def apply(self, person_id, squeeze_factor): # further checks and surgical repair is assume to have occurred during their caesarean surgery if mni[person_id]['mode_of_delivery'] == 'caesarean_section': - # log treatment is delivered - pregnancy_helper_functions.log_met_need(self.module, 'ur_surg', self) - # Determine if the uterus can be repaired treatment_success_ur = params['success_rate_uterine_repair'] > self.module.rng.random_sample() @@ -3394,7 +3285,6 @@ def apply(self, population): nb_params['prob_timings_pnc_newborns'] = [1.0, 0] if params['alternative_pnc_quality']: - nb_params['prob_kmc_available'] = params['pnc_availability_probability'] params['prob_intervention_delivered_anaemia_assessment_pnc'] = params['pnc_availability_probability'] if params['pnc_sens_analysis_max'] or params['pnc_sens_analysis_min']: From a89a0341323be5147a0b3bab6a51035af9be59d8 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:43:05 +0000 Subject: [PATCH 23/27] Added new function to determine whether updates to newborn_outcomes.py to use this function (and some small fixes) --- src/tlo/methods/newborn_outcomes.py | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/tlo/methods/newborn_outcomes.py b/src/tlo/methods/newborn_outcomes.py index e8f32ed7af..3b8cad4da0 100644 --- a/src/tlo/methods/newborn_outcomes.py +++ b/src/tlo/methods/newborn_outcomes.py @@ -900,8 +900,11 @@ def kangaroo_mother_care(self, hsi_event): if (df.at[person_id, 'nb_low_birth_weight_status'] != 'normal_birth_weight') or \ (df.at[person_id, 'nb_low_birth_weight_status'] != 'macrosomia'): + kmc_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='kmc', hsi_event=hsi_event, q_param=[params['prob_kmc_available']]) + # Check KMC can be delivered - if self.rng.random_sample() < params['prob_kmc_available']: + if kmc_delivered: # Store treatment as a property of the newborn used to apply treatment effect df.at[person_id, 'nb_kangaroo_mother_care'] = True @@ -952,6 +955,8 @@ def assessment_and_treatment_newborn_sepsis(self, hsi_event, facility_type): """ df = self.sim.population.props person_id = int(hsi_event.target) + l_params = self.sim.modules['Labour'].current_parameters + pnc_location = 'hc' if facility_type == '1a' else 'hp' # We assume that only hospitals are able to deliver full supportive care for neonatal sepsis, full supportive # care evokes a stronger treatment effect than injectable antibiotics alone @@ -959,35 +964,29 @@ def assessment_and_treatment_newborn_sepsis(self, hsi_event, facility_type): if df.at[person_id, 'nb_early_onset_neonatal_sepsis'] or df.at[person_id, 'pn_sepsis_late_neonatal'] or\ df.at[person_id, 'pn_sepsis_early_neonatal']: - # Run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self.sim.modules['Labour'], - sf='iv_abx', - hsi_event=hsi_event) if facility_type != '1a': - # check consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + neo_sepsis_treatment_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='neo_sepsis_treatment_supp_care', hsi_event=hsi_event, + q_param=[l_params['prob_hcw_avail_iv_abx'], l_params[f'mean_hcw_competence_{pnc_location}']], cons=self.item_codes_nb_consumables['sepsis_supportive_care_core'], - opt_cons=self.item_codes_nb_consumables['sepsis_supportive_care_optional']) + opt_cons=self.item_codes_nb_consumables['sepsis_supportive_care_optional'], + equipment={'Drip stand', 'Infusion pump'}) - # Then, if the consumables are available, treatment for sepsis is delivered - if avail and sf_check: + if neo_sepsis_treatment_delivered: df.at[person_id, 'nb_supp_care_neonatal_sepsis'] = True - pregnancy_helper_functions.log_met_need(self, 'neo_sep_supportive_care', hsi_event) - hsi_event.add_equipment({'Drip stand', 'Infusion pump'}) # The same pattern is then followed for health centre care else: - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, + neo_sepsis_treatment_delivered = pregnancy_helper_functions.check_int_deliverable( + self, int_name='neo_sepsis_treatment_abx', hsi_event=hsi_event, + q_param=[l_params['prob_hcw_avail_iv_abx'], l_params[f'mean_hcw_competence_{pnc_location}']], cons=self.item_codes_nb_consumables['sepsis_abx'], - opt_cons=self.item_codes_nb_consumables['iv_drug_equipment']) + opt_cons=self.item_codes_nb_consumables['iv_drug_equipment'], + equipment={'Drip stand', 'Infusion pump'}) - if avail and sf_check: + if neo_sepsis_treatment_delivered: df.at[person_id, 'nb_inj_abx_neonatal_sepsis'] = True - pregnancy_helper_functions.log_met_need(self, 'neo_sep_abx', hsi_event) - hsi_event.add_equipment({'Drip stand', 'Infusion pump', 'Oxygen cylinder, with regulator'}) def link_twins(self, child_one, child_two, mother_id): """ From cff9142fc3a9cb5d3b2a13d4872ad3eb8bce4770 Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:44:57 +0000 Subject: [PATCH 24/27] fixes to exsisting tests --- src/tlo/methods/postnatal_supervisor.py | 2 +- tests/test_antenatal_interventions.py | 27 +++++++++++++++++-------- tests/test_labour.py | 12 +++++------ tests/test_newborn_outcomes.py | 12 +++++------ tests/test_pregnancy_supervisor.py | 6 +++--- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/tlo/methods/postnatal_supervisor.py b/src/tlo/methods/postnatal_supervisor.py index 63cd9b2a60..7ee477e45a 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -655,7 +655,7 @@ def log_new_progressed_cases(disease): del self.sim.modules['PregnancySupervisor'].mother_and_newborn_info[person] # ----------------------------------------- CARE SEEKING ------------------------------------------------------ - # We now use the the pn_emergency_event_mother property that has just been set for women who are experiencing + # We now use the pn_emergency_event_mother property that has just been set for women who are experiencing # severe complications to select a subset of women who may choose to seek care can_seek_care = df.loc[df['is_alive'] & df['la_is_postpartum'] & (df['pn_postnatal_period_in_weeks'] == week) & df['pn_emergency_event_mother'] & ~df['hs_is_inpatient']] diff --git a/tests/test_antenatal_interventions.py b/tests/test_antenatal_interventions.py index ce10385ef4..a8c8bbbe68 100644 --- a/tests/test_antenatal_interventions.py +++ b/tests/test_antenatal_interventions.py @@ -593,7 +593,7 @@ def test_initiation_of_treatment_for_maternal_anaemia_during_antenatal_inpatient # and override quality parameters lparams = sim.modules['Labour'].current_parameters lparams['prob_hcw_avail_blood_tran'] = 1.0 - lparams['mean_hcw_competence_hp'] = [1.0, 1.0] + lparams['mean_hcw_competence_hp'] = 1.0 # Set anaemia status df.at[mother_id, 'ps_anaemia_in_pregnancy'] = 'severe' @@ -655,8 +655,8 @@ def test_initiation_of_treatment_for_hypertensive_disorder_during_antenatal_inpa # set key parameters params = sim.modules['Labour'].current_parameters - params['mean_hcw_competence_hc'] = [1, 1] - params['mean_hcw_competence_hp'] = [1, 1] + params['mean_hcw_competence_hc'] = 1 + params['mean_hcw_competence_hp'] = 1 params['prob_hcw_avail_anticonvulsant'] = 1 # set key pregnancy characteristics @@ -791,8 +791,8 @@ def test_initiation_of_treatment_for_prom_with_or_without_chorioamnionitis_durin # set key parameters params = sim.modules['Labour'].current_parameters - params['mean_hcw_competence_hc'] = [1, 1] - params['mean_hcw_competence_hp'] = [1, 1] + params['mean_hcw_competence_hc'] = 1 + params['mean_hcw_competence_hp'] = 1 params['prob_hcw_avail_iv_abx'] = 1 # set key pregnancy characteristics @@ -933,8 +933,8 @@ def test_scheduling_and_treatment_effect_of_post_abortion_care(seed): # set key parameters params = sim.modules['Labour'].current_parameters - params['mean_hcw_competence_hc'] = [1, 1] - params['mean_hcw_competence_hp'] = [1, 1] + params['mean_hcw_competence_hc'] = 1 + params['mean_hcw_competence_hp'] = 1 params['prob_hcw_avail_retained_prod'] = 1 # set complications @@ -1000,7 +1000,11 @@ def test_scheduling_and_treatment_effect_of_ectopic_pregnancy_case_management(se df.at[mother_id, 'ps_gestational_age_in_weeks'] = 8 # set prob care seeking to 1 - sim.modules['PregnancySupervisor'].current_parameters['prob_care_seeking_ectopic_pre_rupture'] = 1 + params = sim.modules['PregnancySupervisor'].current_parameters + l_params = sim.modules['Labour'].current_parameters + params['prob_care_seeking_ectopic_pre_rupture'] = 1 + l_params['prob_hcw_avail_surg'] = 1 + l_params['mean_hcw_competence_hp'] = 1 # define and run ectopic event ectopic_event = pregnancy_supervisor.EctopicPregnancyEvent(individual_id=mother_id, @@ -1022,6 +1026,13 @@ def test_scheduling_and_treatment_effect_of_ectopic_pregnancy_case_management(se assert care_of_women_during_pregnancy.HSI_CareOfWomenDuringPregnancy_TreatmentForEctopicPregnancy in hsi_list # Define and run treatment events + updated_cons = \ + {k: 1.0 for (k, v) in + sim.modules['CareOfWomenDuringPregnancy'].item_codes_preg_consumables['ectopic_pregnancy_core'].items()} + + sim.modules['HealthSystem'].override_availability_of_consumables(updated_cons) + sim.modules['HealthSystem'].consumables._refresh_availability_of_consumables(date=sim.date) + ectopic_treatment = care_of_women_during_pregnancy.HSI_CareOfWomenDuringPregnancy_TreatmentForEctopicPregnancy( module=sim.modules['CareOfWomenDuringPregnancy'], person_id=updated_mother_id) ectopic_treatment.apply(person_id=updated_mother_id, squeeze_factor=0.0) diff --git a/tests/test_labour.py b/tests/test_labour.py index 21f94c200d..64706494c8 100644 --- a/tests/test_labour.py +++ b/tests/test_labour.py @@ -297,8 +297,8 @@ def test_event_scheduling_for_admissions_from_antenatal_inpatient_ward_for_caesa # set key parameters params = sim.modules['Labour'].current_parameters - params['mean_hcw_competence_hc'] = [1.0, 1.0] - params['mean_hcw_competence_hp'] = [1.0, 1.0] + params['mean_hcw_competence_hc'] = 1.0 + params['mean_hcw_competence_hp'] = 1.0 params['prob_hcw_avail_surg'] = 1.0 # Run the labour onset, check she will correctly deliver at a hospital level facility @@ -514,8 +514,8 @@ def test_bemonc_treatments_are_delivered_correctly_with_no_cons_or_quality_const # set key parameters params = sim.modules['Labour'].current_parameters - params['mean_hcw_competence_hc'] = [1.0, 1.0] - params['mean_hcw_competence_hp'] = [1.0, 1.0] + params['mean_hcw_competence_hc'] = 1.0 + params['mean_hcw_competence_hp'] = 1.0 params['prob_hcw_avail_iv_abx'] = 1.0 params['prob_hcw_avail_uterotonic'] = 1.0 params['prob_hcw_avail_anticonvulsant'] = 1.0 @@ -658,8 +658,8 @@ def test_cemonc_event_and_treatments_are_delivered_correct_with_no_cons_or_quali # set key parameters params = sim.modules['Labour'].current_parameters - params['mean_hcw_competence_hc'] = [1.0, 1.0] - params['mean_hcw_competence_hp'] = [1.0, 1.0] + params['mean_hcw_competence_hc'] = 1.0 + params['mean_hcw_competence_hp'] = 1.0 params['prob_hcw_avail_surg'] = 1.0 params['prob_hcw_avail_blood_tran'] = 1.0 diff --git a/tests/test_newborn_outcomes.py b/tests/test_newborn_outcomes.py index 2511e14270..921b9b155c 100644 --- a/tests/test_newborn_outcomes.py +++ b/tests/test_newborn_outcomes.py @@ -336,21 +336,21 @@ def test_sba_hsi_deliveries_resuscitation_treatment_as_expected(seed): sim = Simulation(start_date=start_date, seed=seed) register_modules(sim) sim.make_initial_population(n=100) + sim.simulate(end_date=sim.date + pd.DateOffset(days=0)) # set risk of comps very high and force care seeking - params = sim.modules['NewbornOutcomes'].parameters + params = sim.modules['NewbornOutcomes'].current_parameters params['prob_encephalopathy'] = 1.0 - params['prob_enceph_severity'] = [[0, 0, 1], [0, 0, 1]] + params['prob_enceph_severity'] = [0, 0, 1] params['treatment_effect_resuscitation'] = 0.0 params['cfr_enceph'] = 1.0 params['prob_pnc_check_newborn'] = 0.0 - la_params = sim.modules['Labour'].parameters + la_params = sim.modules['Labour'].current_parameters la_params['prob_hcw_avail_neo_resus'] = 1.0 - la_params['mean_hcw_competence_hc'] = [[1.0, 1.0], [1.0, 1.0]] - la_params['mean_hcw_competence_hp'] = [[1.0, 1.0], [1.0, 1.0]] + la_params['mean_hcw_competence_hc'] = 1.0 + la_params['mean_hcw_competence_hp'] = 1.0 - sim.simulate(end_date=sim.date + pd.DateOffset(days=0)) df = sim.population.props mni = sim.modules['PregnancySupervisor'].mother_and_newborn_info diff --git a/tests/test_pregnancy_supervisor.py b/tests/test_pregnancy_supervisor.py index 4a1aafe0b7..b89656e797 100644 --- a/tests/test_pregnancy_supervisor.py +++ b/tests/test_pregnancy_supervisor.py @@ -529,9 +529,9 @@ def check_abortion_logic(abortion_type): params['treatment_effect_post_abortion_care'] = 0.0 lab_params = sim.modules['Labour'].current_parameters - lab_params['mean_hcw_competence_hc'] = [1, 1] - lab_params['mean_hcw_competence_hp'] = [1, 1] - lab_params['prob_hcw_avail_retained_prod'] = 1 + lab_params['mean_hcw_competence_hc'] = 1.0 + lab_params['mean_hcw_competence_hp'] = 1.0 + lab_params['prob_hcw_avail_retained_prod'] = 1.0 df = sim.population.props pregnant_women = df.loc[df.is_alive & df.is_pregnant] From 25c95f056d87091b88c4bfb5bbd36e9a77def22c Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 14:46:01 +0000 Subject: [PATCH 25/27] new tests to check new analysis function --- ...al_health_helper_and_analysis_functions.py | 138 ++++++++++++++++-- 1 file changed, 125 insertions(+), 13 deletions(-) diff --git a/tests/test_maternal_health_helper_and_analysis_functions.py b/tests/test_maternal_health_helper_and_analysis_functions.py index daea95a5e6..9230670cd7 100644 --- a/tests/test_maternal_health_helper_and_analysis_functions.py +++ b/tests/test_maternal_health_helper_and_analysis_functions.py @@ -49,13 +49,128 @@ def apply(self, person_id, squeeze_factor): return hsi_event +def test_interventions_are_delivered_as_expected_not_during_analysis(seed): + sim = Simulation(start_date=start_date, seed=seed) + sim.register(*fullmodel(resourcefilepath=resourcefilepath)) + sim.make_initial_population(n=100) + + cw_params = sim.modules['CareOfWomenDuringPregnancy'].parameters + cw_params['sensitivity_bp_monitoring'] = 1.0 + cw_params['specificity_bp_monitoring'] = 1.0 + cw_params['sensitivity_urine_protein_1_plus'] = 1.0 + cw_params['specificity_urine_protein_1_plus'] = 1.0 + cw_params['sensitivity_poc_hb_test'] = 1.0 + cw_params['specificity_poc_hb_test'] = 1.0 + cw_params['sensitivity_fbc_hb_test'] = 1.0 + cw_params['specificity_fbc_hb_test'] = 1.0 + cw_params['sensitivity_blood_test_glucose'] = 1.0 + cw_params['specificity_blood_test_glucose'] = 1.0 + cw_params['sensitivity_blood_test_syphilis'] = 1.0 + cw_params['specificity_blood_test_syphilis'] = 1.0 + + sim.simulate(end_date=Date(2010, 1, 2)) + + df = sim.population.props + women_repro = df.loc[df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50)] + mother_id = women_repro.index[0] + + int_function = pregnancy_helper_functions.check_int_deliverable + + hsi_event = get_dummy_hsi(sim, mother_id, id=0, fl=0) + + def override_dummy_cons(value): + updated_cons = {k: value for (k, v) in sim.modules['Labour'].item_codes_lab_consumables['delivery_core'].items()} + sim.modules['HealthSystem'].override_availability_of_consumables(updated_cons) + sim.modules['HealthSystem'].consumables._refresh_availability_of_consumables(date=sim.date) + return sim.modules['Labour'].item_codes_lab_consumables['delivery_core'] + + pparams = sim.modules['PregnancySupervisor'].current_parameters + + for intervention in pparams['all_interventions']: + assert not int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[0.0], + cons=override_dummy_cons(0.0)) + assert not int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[1.0], + cons=override_dummy_cons(0.0)) + assert not int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[0.0], + cons=override_dummy_cons(1.0)) + assert int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[1.0], + cons=override_dummy_cons(1.0)) + + dx_tests = [('ps_htn_disorders', 'severe_pre_eclamp', 'bp_measurement', 'blood_pressure_measurement'), + ('ps_htn_disorders', 'severe_pre_eclamp', 'urine_dipstick', 'urine_dipstick_protein'), + ('ps_anaemia_in_pregnancy', 'moderate', 'hb_test', 'point_of_care_hb_test'), + ('ps_anaemia_in_pregnancy', 'moderate', 'full_blood_count', 'full_blood_count_hb'), + ('ps_gest_diab', 'uncontrolled', 'gdm_test', 'blood_test_glucose'), + ('ps_syphilis', True, 'syphilis_test', 'blood_test_syphilis'), + ('pn_anaemia_following_pregnancy', 'moderate', 'full_blood_count', 'full_blood_count_hb_pn')] + + for test in dx_tests: + df.at[mother_id, test[0]] = test[1] + assert int_function(sim.modules['CareOfWomenDuringPregnancy'], + test[2], hsi_event, q_param=[1.0], + cons=override_dummy_cons(1.0), dx_test=test[3]) + + assert not int_function(sim.modules['CareOfWomenDuringPregnancy'], + test[2], hsi_event, q_param=[0.0], + cons=override_dummy_cons(1.0), dx_test=test[3]) + + assert not int_function(sim.modules['CareOfWomenDuringPregnancy'], + test[2], hsi_event, q_param=[1.0], + cons=override_dummy_cons(0.0), dx_test=test[3]) + + +def test_interventions_are_delivered_as_expected_during_analysis(seed): + sim = Simulation(start_date=start_date, seed=seed) + sim.register(*fullmodel(resourcefilepath=resourcefilepath)) + sim.make_initial_population(n=100) + + pparams = sim.modules['PregnancySupervisor'].parameters + pparams['analysis_year'] = 2010 + pparams['interventions_analysis'] = True + + sim.simulate(end_date=Date(2010, 1, 2)) + + pparams = sim.modules['PregnancySupervisor'].current_parameters + assert pparams['ps_analysis_in_progress'] + + df = sim.population.props + women_repro = df.loc[df.is_alive & (df.sex == 'F') & (df.age_years > 14) & (df.age_years < 50)] + mother_id = women_repro.index[0] + + int_function = pregnancy_helper_functions.check_int_deliverable + hsi_event = get_dummy_hsi(sim, mother_id, id=0, fl=0) + + def override_dummy_cons(value): + updated_cons = {k: value for (k, v) in sim.modules['Labour'].item_codes_lab_consumables['delivery_core'].items()} + sim.modules['HealthSystem'].override_availability_of_consumables(updated_cons) + sim.modules['HealthSystem'].consumables._refresh_availability_of_consumables(date=sim.date) + return sim.modules['Labour'].item_codes_lab_consumables['delivery_core'] + + for intervention in sim.modules['PregnancySupervisor'].parameters['all_interventions']: + pparams['interventions_under_analysis'] = [intervention] + pparams['intervention_analysis_availability'] = 1.0 + + assert int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[0.0], + cons=override_dummy_cons(0.0)) + + assert int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[1.0], + cons=override_dummy_cons(0.0)) + + assert int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[0.0], + cons=override_dummy_cons(1.0)) + + pparams['intervention_analysis_availability'] = 0.0 + + assert not int_function(sim.modules['PregnancySupervisor'], intervention, hsi_event, q_param=[1.0], + cons=override_dummy_cons(1.0)) + def test_analysis_analysis_events_run_as_expected_and_update_parameters(seed): """Test that the analysis events run when scheduled and that they update the correct parameters as expected when they run""" sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) + lparams = sim.modules['Labour'].parameters pparams = sim.modules['PregnancySupervisor'].parameters @@ -85,11 +200,11 @@ def test_analysis_analysis_events_run_as_expected_and_update_parameters(seed): unchanged_odds_anc = pparams['odds_early_init_anc4'][0] unchanged_odds_pnc = lparams['odds_will_attend_pnc'][0] + sim.make_initial_population(n=100) # run the model for 1 day sim.simulate(end_date=Date(2010, 1, 2)) p_current_params = sim.modules['PregnancySupervisor'].current_parameters - c_current_params = sim.modules['CareOfWomenDuringPregnancy'].current_parameters l_current_params = sim.modules['Labour'].current_parameters nbparams = sim.modules['NewbornOutcomes'].current_parameters @@ -101,10 +216,6 @@ def test_analysis_analysis_events_run_as_expected_and_update_parameters(seed): assert p_current_params['prob_late_initiation_anc4'] == 0 assert p_current_params['odds_early_init_anc4'] != unchanged_odds_anc - for parameter in ['prob_intervention_delivered_urine_ds', 'prob_intervention_delivered_bp', - 'prob_intervention_delivered_syph_test', 'prob_intervention_delivered_gdm_test']: - assert c_current_params[parameter] == new_avail_prob - # Now check corrent labour/newborn/postnatal parameters have been updated assert l_current_params['prob_intervention_delivered_anaemia_assessment_pnc'] == new_avail_prob @@ -119,7 +230,7 @@ def test_analysis_analysis_events_run_as_expected_and_update_parameters(seed): def test_analysis_analysis_events_run_as_expected_when_using_sensitivity_max_parameters(seed): sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) + lparams = sim.modules['Labour'].parameters pparams = sim.modules['PregnancySupervisor'].parameters @@ -135,6 +246,7 @@ def test_analysis_analysis_events_run_as_expected_when_using_sensitivity_max_par pnc_avail_prob = 1.0 lparams['pnc_availability_probability'] = pnc_avail_prob + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) p_current_params = sim.modules['PregnancySupervisor'].current_parameters @@ -170,7 +282,6 @@ def test_analysis_analysis_events_run_as_expected_when_using_sensitivity_max_par def test_analysis_analysis_events_run_as_expected_when_using_sensitivity_min_parameters(seed): sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) lparams = sim.modules['Labour'].parameters pparams = sim.modules['PregnancySupervisor'].parameters @@ -185,6 +296,7 @@ def test_analysis_analysis_events_run_as_expected_when_using_sensitivity_min_par pnc_avail_prob = 0.0 lparams['pnc_availability_probability'] = pnc_avail_prob + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) p_current_params = sim.modules['PregnancySupervisor'].current_parameters @@ -213,7 +325,6 @@ def test_analysis_events_force_availability_of_consumables_when_scheduled_in_anc via some pre-defined analysis parameter and not via the health system within the ANC HSIs""" sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) # Set the analysis event to run on the first day of the simulation pparams = sim.modules['PregnancySupervisor'].parameters @@ -225,6 +336,7 @@ def test_analysis_events_force_availability_of_consumables_when_scheduled_in_anc cparams['sensitivity_blood_test_syphilis'] = [1.0, 1.0] cparams['specificity_blood_test_syphilis'] = [1.0, 1.0] + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) # check the event ran @@ -296,7 +408,6 @@ def test_analysis_events_force_availability_of_consumables_for_sba_analysis(seed via some pre-defined analysis parameter and not via the health system within the SBA HSIs""" sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) # Set the analysis event to run at simulation start lparams = sim.modules['Labour'].parameters @@ -308,6 +419,7 @@ def test_analysis_events_force_availability_of_consumables_for_sba_analysis(seed lparams['bemonc_availability'] = 1.0 lparams['cemonc_availability'] = 1.0 + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) params = sim.modules['Labour'].current_parameters @@ -432,7 +544,6 @@ def test_analysis_events_force_availability_of_consumables_for_pnc_analysis(seed via some pre-defined analysis parameter and not via the health system within the PNC HSIs""" sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) # Set the analysis event to run at simulation start lparams = sim.modules['Labour'].parameters @@ -443,6 +554,7 @@ def test_analysis_events_force_availability_of_consumables_for_pnc_analysis(seed # Set availability lparams['pnc_availability_probability'] = 1.0 + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) params = sim.modules['Labour'].current_parameters @@ -500,7 +612,6 @@ def test_analysis_events_force_availability_of_consumables_for_newborn_hsi(seed) via some pre-defined analysis parameter and not via the health system within the newborn HSIs""" sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) # Set the analysis event to run at simulation start lparams = sim.modules['Labour'].parameters @@ -513,6 +624,7 @@ def test_analysis_events_force_availability_of_consumables_for_newborn_hsi(seed) lparams['pnc_availability_probability'] = 1.0 lparams['bemonc_availability'] = 1.0 + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) df = sim.population.props @@ -570,7 +682,6 @@ def test_analysis_events_circumnavigates_sf_and_competency_parameters(seed): functions can run""" sim = Simulation(start_date=start_date, seed=seed) sim.register(*fullmodel(resourcefilepath=resourcefilepath)) - sim.make_initial_population(n=100) # Set the analysis event to run at simulation start lparams = sim.modules['Labour'].parameters @@ -582,6 +693,7 @@ def test_analysis_events_circumnavigates_sf_and_competency_parameters(seed): lparams['bemonc_availability'] = 1.0 lparams['cemonc_availability'] = 1.0 + sim.make_initial_population(n=100) sim.simulate(end_date=Date(2010, 1, 2)) params = sim.modules['Labour'].current_parameters From 24dc669357a2f467a61de3aaa41861246b414eda Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 15:19:23 +0000 Subject: [PATCH 26/27] linitng --- src/tlo/methods/pregnancy_supervisor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 9635fa1a73..429590c84d 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -448,7 +448,6 @@ def __init__(self, name=None, resourcefilepath=None): } def read_parameters(self, data_folder): - p = self.parameters # load parameters from the resource file parameter_dataframe = pd.read_excel(Path(self.resourcefilepath) / 'ResourceFile_PregnancySupervisor.xlsx', From a232152339ba99e06617dd5eaae93edb8f2b003b Mon Sep 17 00:00:00 2001 From: joehcollins Date: Mon, 2 Dec 2024 16:36:01 +0000 Subject: [PATCH 27/27] update ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx after change to parameter type to fix test --- ...urceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx index 1586c251f4..1c5ec74576 100644 --- a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e63c16cbd0a069d9d10cf3c7212c8804fb1a047397227485adf348728fa5403b -size 48334 +oid sha256:aa5c11a228476e9d393ceabb5366a551602e4e43175349f9a175fc650b8dd70b +size 48309