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 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 diff --git a/src/tlo/methods/care_of_women_during_pregnancy.py b/src/tlo/methods/care_of_women_during_pregnancy.py index 69ce038299..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 @@ -484,21 +484,24 @@ 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 + 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 + + # 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 - 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} - - 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): @@ -725,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 - - # 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']: - - # check consumables - avail = pregnancy_helper_functions.return_cons_avail( - self, hsi_event, cons=self.item_codes_preg_consumables['urine_dipstick'], opt_cons=None) - - # If the intervention will be delivered the dx_manager runs, returning True if the consumables are - # available and the test detects protein in the urine - 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'}) + 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') - # The process is repeated for blood pressure monitoring - if self.rng.random_sample() < params['prob_intervention_delivered_bp']: - hsi_event.add_equipment({'Sphygmomanometer'}) + 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') - 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 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 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 @@ -795,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 @@ -832,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'}) @@ -898,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'}) @@ -920,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): @@ -969,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']) @@ -993,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'}) + 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') - 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']) + if syph_test_delivered: - test = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='blood_test_syphilis', hsi_event=hsi_event) - - # 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): """ @@ -1072,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'}) - - 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') + 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'}) - # 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': + if gdm_screening_delivered: + 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 @@ -1214,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'}) + 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') - test_result = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='full_blood_count_hb', hsi_event=hsi_event) - - 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 @@ -1244,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 @@ -1284,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): @@ -1302,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 @@ -1335,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']) - - # 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) + opt_cons=self.item_codes_preg_consumables['eclampsia_management_optional'], + equipment={'Drip stand', 'Infusion pump'}) - # 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): """ @@ -1360,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): """ @@ -2375,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) @@ -2519,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 @@ -2543,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' @@ -2581,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']) + 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'] - # 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) - - # 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') @@ -2671,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/labour.py b/src/tlo/methods/labour.py index 35081b7d27..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), ) @@ -1056,9 +1051,10 @@ 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 # Store only live births to a mother parity if not df.at[mother_id, 'la_intrapartum_still_birth']: @@ -1282,9 +1278,7 @@ 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': mni[individual_id]['cpd'] = True @@ -1293,9 +1287,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 elif complication == 'antepartum_haem': random_choice = self.rng.choice(['mild_moderate', 'severe'], @@ -1305,33 +1298,29 @@ 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 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': df.at[individual_id, 'la_sepsis'] = True 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): """ @@ -1384,9 +1373,7 @@ 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 if complication == 'pph_uterine_atony': @@ -1422,13 +1409,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 @@ -1454,13 +1434,14 @@ 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': - 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: @@ -1469,9 +1450,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': @@ -1479,9 +1458,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': @@ -1489,9 +1466,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): """ @@ -1519,9 +1494,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: @@ -1653,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']: @@ -1668,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 ------------------------------- @@ -1689,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): @@ -1735,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'): @@ -1748,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): """ @@ -1774,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' @@ -1802,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): @@ -1820,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): """ @@ -1858,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': @@ -1868,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']) + opt_cons=self.item_codes_lab_consumables['obstructed_labour'], + equipment={'Delivery Forceps', 'Vacuum extractor'}) - # run HCW check - sf_check = pregnancy_helper_functions.check_emonc_signal_function_will_run(self, sf='avd', - hsi_event=hsi_event) - - 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 @@ -1922,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 @@ -1932,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): """ @@ -1956,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, @@ -1988,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 @@ -2012,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): @@ -2043,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 @@ -2087,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 @@ -2132,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() @@ -2159,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 @@ -2174,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': @@ -2211,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'}) + 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') - # 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) - - 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']: @@ -2277,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): @@ -2423,10 +2304,7 @@ 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' '_for_defining' @@ -2434,18 +2312,12 @@ 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'] @@ -2612,9 +2484,7 @@ 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 self.module.progression_of_hypertensive_disorders(individual_id, property_prefix='ps') @@ -2686,6 +2556,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']) @@ -2729,8 +2600,7 @@ 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 if individual_id in mni: @@ -2818,13 +2688,10 @@ 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') @@ -2908,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 @@ -2933,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) @@ -2977,9 +2841,7 @@ 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 ========================== # Next, women in labour are assessed for complications and treatment delivered if a need is identified and @@ -3018,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 ========================================= @@ -3226,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 @@ -3235,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')) @@ -3277,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() @@ -3430,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']: diff --git a/src/tlo/methods/newborn_outcomes.py b/src/tlo/methods/newborn_outcomes.py index 3691bc6003..3b8cad4da0 100644 --- a/src/tlo/methods/newborn_outcomes.py +++ b/src/tlo/methods/newborn_outcomes.py @@ -523,28 +523,23 @@ 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): """ @@ -558,9 +553,7 @@ 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): """ @@ -592,8 +585,7 @@ 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 if df.at[child_id, 'nb_encephalopathy'] == 'none': @@ -616,9 +608,7 @@ 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): """ @@ -638,9 +628,7 @@ 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): """ @@ -912,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 @@ -964,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 @@ -971,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): """ @@ -1021,6 +1008,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 @@ -1157,14 +1145,14 @@ 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 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..7ee477e45a 100644 --- a/src/tlo/methods/postnatal_supervisor.py +++ b/src/tlo/methods/postnatal_supervisor.py @@ -354,10 +354,7 @@ 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 care_seeking_for_repair = self.pn_linear_models[ @@ -500,9 +497,7 @@ 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 ---------------------------------------------------------- # Next we determine if any women will experience postnatal bleeding @@ -514,10 +509,7 @@ 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 -------------------------------------------------- # We apply a risk of anaemia developing in this week, and determine its severity @@ -533,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 @@ -594,9 +581,7 @@ 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': mni[person]['new_onset_spe'] = True @@ -637,11 +622,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'] += (len(pre_eclampsia.loc[pre_eclampsia].index)) # -------------------------------- RISK OF GESTATIONAL HYPERTENSION -------------------------------------- gest_hypertension = self.apply_linear_model( @@ -650,11 +632,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'] += len(gest_hypertension.loc[gest_hypertension].index) # -------------------------------- RISK OF DEATH SEVERE HYPERTENSION ------------------------------------------ # Risk of death is applied to women with severe hypertensive disease @@ -667,13 +646,16 @@ 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']) 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']] @@ -744,9 +726,7 @@ 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): """ @@ -771,9 +751,7 @@ 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 care_seeking = pd.Series( @@ -830,6 +808,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] @@ -862,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']) @@ -886,6 +865,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 @@ -941,7 +921,20 @@ 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': + 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'], 'anaemia': df.at[person, 'pn_anaemia_following_pregnancy']}) @@ -961,6 +954,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']}) @@ -1027,9 +1027,7 @@ 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 if endo_result: @@ -1045,10 +1043,7 @@ 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 ------------------------------------------ # And then risk of developing anaemia... @@ -1065,11 +1060,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 @@ -1114,9 +1104,8 @@ 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': mni[person]['new_onset_spe'] = True @@ -1136,19 +1125,14 @@ 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: risk_gh_after_pregnancy = self.module.pn_linear_models['gest_htn_pn'].predict(df.loc[[ 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 ================================================== # 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..e12388ebf4 100644 --- a/src/tlo/methods/pregnancy_helper_functions.py +++ b/src/tlo/methods/pregnancy_helper_functions.py @@ -9,6 +9,63 @@ from tlo import logging +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', + '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', + + # 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', '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', + + # death outcomes + '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+'] + + mnh_outcome_counter = {k: 0 for k in outcome_list} + + return {'counter': mnh_outcome_counter, + 'outcomes': outcome_list} + def get_list_of_items(self, item_list): """ Uses get_item_code_from_item_name to return item codes for a list of named items @@ -20,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']} @@ -149,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: -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 analysis is not being conducted, intervention delivery is dependent on quality parameters, consumable + # availability and dx_test results + quality = False + consumables = False + test = False - logger.info(key='intervention', - data={'person_id': person_id, - 'int': f'{intervention}_{tp[0]}_{df.at[person_id, f"{tp[1]}_htn_disorders"]}'}) + if ((q_param is None) or + all([self.rng.random_sample() < value for value in q_param])): + quality = True - 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'}) + # todo: should this only be if qual and cons are also true? + if equipment is not None: + hsi_event.add_equipment(equipment) - elif hsi.TREATMENT_ID == 'PostnatalCare_Maternal': - logger.info(key='intervention', data={'person_id': person_id, 'int': 'abx_pn_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 intervention == 'blood_tran': - if hsi.TREATMENT_ID == 'AntenatalCare_Inpatient': - logger.info(key='intervention', data={'person_id': person_id, 'int': 'blood_tran_anaemia'}) + if cons is None and opt_cons is not None: + hsi_event.get_consumables(item_codes=[], optional_item_codes=opt_cons) - 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'}) + 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 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): @@ -283,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] @@ -321,7 +309,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 @@ -349,7 +337,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 @@ -460,7 +449,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 @@ -530,7 +519,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 diff --git a/src/tlo/methods/pregnancy_supervisor.py b/src/tlo/methods/pregnancy_supervisor.py index 7dd8819ab6..429590c84d 100644 --- a/src/tlo/methods/pregnancy_supervisor.py +++ b/src/tlo/methods/pregnancy_supervisor.py @@ -62,6 +62,10 @@ 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 + mnh_oc = pregnancy_helper_functions.generate_mnh_outcome_counter() + self.mnh_outcome_counter = mnh_oc['counter'] + INIT_DEPENDENCIES = {'Demography'} OPTIONAL_INIT_DEPENDENCIES = {'HealthBurden', 'Malaria', 'CardioMetabolicDisorders', 'Hiv'} @@ -385,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 = { @@ -433,6 +448,7 @@ def __init__(self, name=None, resourcefilepath=None): } def read_parameters(self, data_folder): + # load parameters from the resource file parameter_dataframe = pd.read_excel(Path(self.resourcefilepath) / 'ResourceFile_PregnancySupervisor.xlsx', sheet_name='parameter_values') @@ -523,13 +539,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 @@ -654,10 +676,8 @@ 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 - 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']}) + 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 # We currently assume that hyperglycemia due to gestational diabetes resolves following birth if df.at[mother_id, 'ps_gest_diab'] != 'none': @@ -967,9 +987,7 @@ 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 # determines post pregnancy contraception) @@ -996,9 +1014,7 @@ 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}') @@ -1016,31 +1032,23 @@ 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 pregnancy_helper_functions.store_dalys_in_mni(individual_id, mni, 'abortion_onset', self.sim.date) @@ -1099,10 +1107,8 @@ 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'] += len(gest_diab.loc[gest_diab].index) + def apply_risk_of_hypertensive_disorders(self, gestation_of_interest): """ @@ -1123,10 +1129,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'] += 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 @@ -1137,10 +1140,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'] += len(gest_hypertension.loc[gest_hypertension].index) def apply_risk_of_progression_of_hypertension(self, gestation_of_interest): """ @@ -1195,9 +1195,8 @@ 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': self.mother_and_newborn_info[person]['new_onset_spe'] = True @@ -1249,6 +1248,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']) @@ -1269,10 +1271,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'] += len(placenta_abruption.loc[placenta_abruption].index) def apply_risk_of_antepartum_haemorrhage(self, gestation_of_interest): """ @@ -1310,10 +1309,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'] += len(severe_women.loc[severe_women].index) non_severe_women = (df.loc[antepartum_haemorrhage.loc[antepartum_haemorrhage].index, 'ps_antepartum_haemorrhage'] != 'severe') @@ -1322,10 +1318,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'] += len(non_severe_women.loc[non_severe_women].index) def apply_risk_of_premature_rupture_of_membranes_and_chorioamnionitis(self, gestation_of_interest): """ @@ -1349,11 +1342,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'] += 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'], @@ -1366,9 +1355,7 @@ 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): """ @@ -1432,7 +1419,7 @@ 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 self.sim.modules['Labour'].reset_due_date(id_or_index=women.index, new_due_date=pd.NaT) @@ -1454,7 +1441,7 @@ 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 self.sim.modules['Contraception'].end_pregnancy(individual_id) @@ -1584,6 +1571,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 @@ -1698,7 +1686,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"], @@ -1765,9 +1753,7 @@ 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), (self.sim.date + pd.Timedelta(days=7 * 3 + self.module.rng.randint(0, 7 * 2)))) @@ -1782,11 +1768,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'] += 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 @@ -1796,11 +1778,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'] += len(placenta_praevia.loc[placenta_praevia].index) # ------------------------- APPLYING RISK OF SYPHILIS INFECTION DURING PREGNANCY --------------------------- # Finally apply risk that syphilis will develop during pregnancy @@ -1984,9 +1962,7 @@ 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 df.at[individual_id, 'ps_ectopic_pregnancy'] = 'ruptured' @@ -2028,6 +2004,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']) @@ -2037,7 +2015,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: @@ -2094,10 +2072,8 @@ 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): """This is ParameterUpdateEvent. It is scheduled to occur once on 2015 to update parameters being used by the @@ -2163,7 +2139,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 @@ -2189,11 +2166,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 @@ -2202,23 +2174,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 @@ -2235,37 +2194,120 @@ def __init__(self, module): def apply(self, population): df = self.sim.population.props + 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'] + + 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'] + + for denom in [live_births, pregnancies, comp_pregnancies, deliveries, total_births, total_preterm_birth]: + if denom == 0: + return - 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}) + # MATERNAL COMPLICATION INCIDENCE + # 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 + + 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_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={'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_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 + # 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'] + + 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+'] + 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, 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() + outcome_list = mnh_oc['outcomes'] + self.module.mnh_outcome_counter = {k:0 for k in outcome_list} 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_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 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 31adcf03b5..b89656e797 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): @@ -519,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] @@ -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)