From 22f58751f6a3eb6d59b4fc33de28534d7bbf92f4 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Sun, 30 Jul 2023 13:59:05 +0300 Subject: [PATCH 01/17] WIP [ci skip] --- .../custom/hedley_stats/hedley_stats.module | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 2910d44116..6b3df9f655 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1076,25 +1076,25 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent } foreach ($pmtct_by_clinic as $pmtct_id) { - $pmtct_wrapper = entity_metadata_wrapper('node', $pmtct_id); - $child_id = $pmtct_wrapper->field_person->getIdentifier(); - $child_wrapper = $pmtct_wrapper->field_person; - $mother_wrapper = $pmtct_wrapper->field_adult; + $pmtct_node = node_load($pmtct_id); + $child_id = $pmtct_node->field_person[LANGUAGE_NONE][0]['target_id']; + $mother_id = $pmtct_node->field_adult[LANGUAGE_NONE][0]['target_id']; if (empty($child_wrapper) || empty($mother_wrapper)) { continue; } - $pmtct_join_date = $pmtct_wrapper->created->raw(); - $pmtct_schedule = $pmtct_wrapper->field_expected->value(); - $pmtct_graduate_date = strtotime($pmtct_schedule['value2']); + $child_node = node_load($child_id); + $mother_node = node_load($mother_id); + $pmtct_join_date = $pmtct_node->created; + $pmtct_graduate_date = strtotime($pmtct_node->field_expected[LANGUAGE_NONE][0]['value2']); $expected_person = [ - 'name' => $child_wrapper->label(), - 'gender' => $child_wrapper->field_gender->value(), - 'birth_date' => hedley_stats_convert_timestamp_to_nominal($child_wrapper->field_birth_date->value()), - 'mother_name' => $mother_wrapper->label(), - 'phone_number' => $mother_wrapper->field_phone_number->value(), + 'name' => $child_node->title, + 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], + 'birth_date' => hedley_stats_convert_timestamp_to_nominal($child_node->field_birth_date[LANGUAGE_NONE][0]['value']), + 'mother_name' => $mother_node->title, + 'phone_number' => $mother_wrapper->field_phone_number[LANGUAGE_NONE][0]['value'], 'expected_date' => NULL, ]; From 56a4be64525d1e0de6a0224b2aa3674a292a60cd Mon Sep 17 00:00:00 2001 From: Anatoly Date: Sun, 30 Jul 2023 14:06:03 +0300 Subject: [PATCH 02/17] Fix [ci skip] --- server/hedley/modules/custom/hedley_stats/hedley_stats.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 6b3df9f655..ad86955efe 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1080,7 +1080,7 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $child_id = $pmtct_node->field_person[LANGUAGE_NONE][0]['target_id']; $mother_id = $pmtct_node->field_adult[LANGUAGE_NONE][0]['target_id']; - if (empty($child_wrapper) || empty($mother_wrapper)) { + if (empty($child_id) || empty($mother_id)) { continue; } @@ -1094,7 +1094,7 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], 'birth_date' => hedley_stats_convert_timestamp_to_nominal($child_node->field_birth_date[LANGUAGE_NONE][0]['value']), 'mother_name' => $mother_node->title, - 'phone_number' => $mother_wrapper->field_phone_number[LANGUAGE_NONE][0]['value'], + 'phone_number' => $mother_node->field_phone_number[LANGUAGE_NONE][0]['value'], 'expected_date' => NULL, ]; From 847b6b8bb9b75eeb1332b51fa1350681e42935ae Mon Sep 17 00:00:00 2001 From: Anatoly Date: Sun, 30 Jul 2023 14:17:39 +0300 Subject: [PATCH 03/17] Another fix [ci skip] --- server/hedley/modules/custom/hedley_stats/hedley_stats.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index ad86955efe..2e4f8e867a 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1092,7 +1092,7 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $expected_person = [ 'name' => $child_node->title, 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], - 'birth_date' => hedley_stats_convert_timestamp_to_nominal($child_node->field_birth_date[LANGUAGE_NONE][0]['value']), + 'birth_date' => hedley_restful_timestamp_only_date($child_node->field_birth_date[LANGUAGE_NONE][0]['value']), 'mother_name' => $mother_node->title, 'phone_number' => $mother_node->field_phone_number[LANGUAGE_NONE][0]['value'], 'expected_date' => NULL, From eb58fe2e8f28e68db5e0c9a2b541ee1d3e558e10 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Sun, 30 Jul 2023 15:02:33 +0300 Subject: [PATCH 04/17] More fixes [ci skip] --- .../modules/custom/hedley_stats/hedley_stats.module | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 2e4f8e867a..9e70803f72 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -931,7 +931,7 @@ function hedley_stats_get_children_beneficiaries_individual_stats($health_center // has shown excessive memory usage, and did not respond when // drupal_static_reset() was invoked to free up memory. $child_node = node_load($row->field_person); - $birth_date = $child_node->field_birth_date[LANGUAGE_NONE][0]['value']; + $birth_date = strtotime($child_node->field_birth_date[LANGUAGE_NONE][0]['value']); if (empty($birth_date)) { // Child got no birthdate set. We can't process @@ -946,7 +946,7 @@ function hedley_stats_get_children_beneficiaries_individual_stats($health_center 'created' => hedley_stats_convert_timestamp_to_nominal($row->created), 'id' => $child_node->nid, 'name' => $child_node->title, - 'birth_date' => date("Y-m-d", strtotime($birth_date)), + 'birth_date' => date("Y-m-d", $birth_date), 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], 'graduation_date' => hedley_stats_convert_timestamp_to_nominal($graduation_date), // No need to provide mother details, because we do not present @@ -954,10 +954,10 @@ function hedley_stats_get_children_beneficiaries_individual_stats($health_center 'mother_name' => '', 'phone_number' => '', ]; - } - // Free up memory. - drupal_static_reset(); + // Free up memory. + drupal_static_reset(); + } return $items; } From 4ef29e4f921e39dbc7a45dac7d0546d598d54ae4 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Sun, 30 Jul 2023 15:03:22 +0300 Subject: [PATCH 05/17] Yet more fixes [ci skip] --- .../custom/hedley_stats/hedley_stats.module | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 2e4f8e867a..b9fede9dff 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -494,10 +494,9 @@ function hedley_stats_get_villages_with_residents($health_center_id) { $result = []; foreach ($villages_ids as $village_id) { - $wrapper = entity_metadata_wrapper('node', $village_id); - $village_uuid = $wrapper->field_uuid->value(); - $residents = hedley_chw_get_village_residents($village_id); - $result[$village_uuid] = $residents; + $village_node = node_load($villages_ids); + $village_uuid = $village_node->field_uuid[LANGUAGE_NONE][0]['value']; + $result[$village_uuid] = hedley_chw_get_village_residents($village_id); } // Store in cache. @@ -1106,21 +1105,20 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent // We know there's only one session, as we have set range to 1. $session_id = reset($sessions); - $session_wrapper = entity_metadata_wrapper('node', $session_id); - $session_schedule = $session_wrapper->field_scheduled_date->value(); - $session_start_date = strtotime($session_schedule['value']); + $session_node = node_load($session_id); + $session_start_date = strtotime($session_node->field_scheduled_date[LANGUAGE_NONE][0]['value']); // Participant has graduated before the session has begun. if ($pmtct_graduate_date < $session_start_date) { continue; } - if (!empty($session_schedule['value2'])) { + if (!empty($session_node->field_scheduled_date[LANGUAGE_NONE][0]['value2'])) { // Since start and end dates for session are always set to 00:00, // we add one day to session end date. // Otherwise, if session was started and ended at same day, it's // as if session was stated and ended on exact same time. - $session_end_date = strtotime($session_schedule['value2']) + 24 * 60 * 60; + $session_end_date = strtotime($session_node->field_scheduled_date[LANGUAGE_NONE][0]['value2']) + 24 * 60 * 60; // Participant has joined after the session has ended. if ($pmtct_join_date > $session_end_date) { continue; From 518db494fc81c7a0fe54fa34138f382d38a93a07 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Sun, 30 Jul 2023 15:52:13 +0300 Subject: [PATCH 06/17] More corrections --- .../hedley/modules/custom/hedley_stats/hedley_stats.module | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index c4f4f46737..7c6f20cdcd 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -494,7 +494,7 @@ function hedley_stats_get_villages_with_residents($health_center_id) { $result = []; foreach ($villages_ids as $village_id) { - $village_node = node_load($villages_ids); + $village_node = node_load($village_id); $village_uuid = $village_node->field_uuid[LANGUAGE_NONE][0]['value']; $result[$village_uuid] = hedley_chw_get_village_residents($village_id); } @@ -1087,13 +1087,14 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $mother_node = node_load($mother_id); $pmtct_join_date = $pmtct_node->created; $pmtct_graduate_date = strtotime($pmtct_node->field_expected[LANGUAGE_NONE][0]['value2']); + $phone_number = !empty($mother_node->field_phone_number) ? $mother_node->field_phone_number[LANGUAGE_NONE][0]['value'] : ''; $expected_person = [ 'name' => $child_node->title, 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], 'birth_date' => hedley_restful_timestamp_only_date($child_node->field_birth_date[LANGUAGE_NONE][0]['value']), 'mother_name' => $mother_node->title, - 'phone_number' => $mother_node->field_phone_number[LANGUAGE_NONE][0]['value'], + 'phone_number' => $phone_number, 'expected_date' => NULL, ]; From 267de0723220b2ade300264bc243b491dd18f990 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 00:22:11 +0300 Subject: [PATCH 07/17] Rewrite hedley_stats_get_session_attendance_stats_by_health_center() WIP [ci skip] --- .../hedley_activity/hedley_activity.module | 24 +++--- .../custom/hedley_stats/hedley_stats.module | 85 +++++++------------ 2 files changed, 44 insertions(+), 65 deletions(-) diff --git a/server/hedley/modules/custom/hedley_activity/hedley_activity.module b/server/hedley/modules/custom/hedley_activity/hedley_activity.module index 64edff8f91..4254ffb65c 100644 --- a/server/hedley/modules/custom/hedley_activity/hedley_activity.module +++ b/server/hedley/modules/custom/hedley_activity/hedley_activity.module @@ -340,12 +340,12 @@ function hedley_activity_node_by_peer_in_session(\EntityMetadataWrapper $wrapper } /** - * Get a measurement by the measurement types and period. + * Get measurements taken for person during one of sessions, within a period. * * @param int $person_id * The person node ID. - * @param array $measurement_bundles - * The measurement node bundle. + * @param array $sessions_ids + * The IDs of sessions. * @param array $period * The period of the measurement, contains a start and an end. * @param int $range @@ -354,24 +354,24 @@ function hedley_activity_node_by_peer_in_session(\EntityMetadataWrapper $wrapper * @return array * Node IDs if they exist or empty array if no nodes exist. */ -function hedley_activity_get_person_measurements_by_period($person_id, array $measurement_bundles, array $period, $range = 100) { +function hedley_activity_get_person_measurements_during_sessions_by_period($person_id, array $sessions_ids, array $period, $range = 100) { $query = db_select('node', 'node'); - $query->fields('node', ['nid', 'created', 'type']); + $query->fields('node', ['nid']); + $query->condition('status', NODE_PUBLISHED); $person_field = 'field_person'; hedley_general_join_field_to_query($query, 'node', $person_field); $person_field_name = $person_field . '.' . $person_field . '_target_id'; $query->condition($person_field_name, $person_id); - $query - ->condition('type', $measurement_bundles, 'IN') - ->condition('status', NODE_PUBLISHED); + $session_field = 'field_session'; + hedley_general_join_field_to_query($query, 'node', $session_field); + $session_field_name = $session_field . '.' . $session_field . '_target_id'; + $query->condition($session_field_name, $sessions_ids, 'IN'); - hedley_general_join_field_to_query($query, 'node', 'field_session'); - $date_field = 'field_scheduled_date'; - hedley_general_join_field_to_query($query, 'node', $date_field, TRUE, "field_session.field_session_target_id"); + $date_field = 'field_date_measured'; + hedley_general_join_field_to_query($query, 'node', $date_field); $date_field_name = $date_field . '.' . $date_field . '_value'; - $query->condition($date_field_name, [$period['start'], $period['end']], 'BETWEEN'); $query->range(0, $range); diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 7c6f20cdcd..6da9608b6a 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -925,7 +925,7 @@ function hedley_stats_get_children_beneficiaries_individual_stats($health_center $result = hedley_stats_run_node_query_in_batches($query); $items = []; - foreach ($result as $row) { + foreach ($result as $key => $row) { // Using node_load() instead of entity_metadata_wrapper(), because latter // has shown excessive memory usage, and did not respond when // drupal_static_reset() was invoked to free up memory. @@ -955,7 +955,9 @@ function hedley_stats_get_children_beneficiaries_individual_stats($health_center ]; // Free up memory. - drupal_static_reset(); + if ($key % 500 == 0) { + drupal_static_reset(); + } } return $items; @@ -1059,25 +1061,23 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $completed_program = []; $missed_session = []; - $measurements_types = array_keys(heldey_stats_get_nutrition_measurements_mapping_for_group_encounter()); - // Add photo the content types to make sure the person also has a photo. - $measurements_types[] = 'photo'; // Loop through the clinics and get sessions for each clinic and from there // get expected people for each clinic. foreach ($fbf_clinics as $clinic_id) { - // Get PMTCT participants by clinic and session dates - // (all expected people). - $pmtct_by_clinic = hedley_stats_get_pmtct_participants_by_clinic($clinic_id, 5000); - if (empty($pmtct_by_clinic)) { - // No participants were found. - continue; + $sessions_by_period = $periods_ranges = []; + foreach ($periods as $period) { + $sessions_by_period[$period] = hedley_stats_get_clinic_sessions_by_period($clinic_id, $period); + $periods_ranges[$period] = hedley_stats_get_period($period); } - foreach ($pmtct_by_clinic as $pmtct_id) { - $pmtct_node = node_load($pmtct_id); - $child_id = $pmtct_node->field_person[LANGUAGE_NONE][0]['target_id']; - $mother_id = $pmtct_node->field_adult[LANGUAGE_NONE][0]['target_id']; + // Get PMTCT participants by clinic. + $participants_ids = hedley_stats_get_pmtct_participants_by_clinic($clinic_id, 5000); + + foreach ($participants_ids as $participant_id) { + $participant_node = node_load($participant_id); + $child_id = $participant_node->field_person[LANGUAGE_NONE][0]['target_id']; + $mother_id = $participant_node->field_adult[LANGUAGE_NONE][0]['target_id']; if (empty($child_id) || empty($mother_id)) { continue; @@ -1085,8 +1085,8 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $child_node = node_load($child_id); $mother_node = node_load($mother_id); - $pmtct_join_date = $pmtct_node->created; - $pmtct_graduate_date = strtotime($pmtct_node->field_expected[LANGUAGE_NONE][0]['value2']); + $participant_join_date = $participant_node->created; + $participant_graduate_date = strtotime($participant_node->field_expected[LANGUAGE_NONE][0]['value2']); $phone_number = !empty($mother_node->field_phone_number) ? $mother_node->field_phone_number[LANGUAGE_NONE][0]['value'] : ''; $expected_person = [ @@ -1098,56 +1098,39 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent 'expected_date' => NULL, ]; - foreach ($periods as $period_name) { - $sessions = hedley_stats_get_clinic_sessions_by_period($clinic_id, $period_name, 1); - if (empty($sessions)) { + foreach ($periods as $period) { + $start_timestamp = strtotime($periods_ranges[$period]['start']); + // Participant has graduated before period has started. + if ($participant_graduate_date < $start_timestamp) { continue; } - // We know there's only one session, as we have set range to 1. - $session_id = reset($sessions); - $session_node = node_load($session_id); - $session_start_date = strtotime($session_node->field_scheduled_date[LANGUAGE_NONE][0]['value']); - - // Participant has graduated before the session has begun. - if ($pmtct_graduate_date < $session_start_date) { + $end_timestamp = strtotime($periods_ranges[$period]['end']); + // Participant has joined after the period has ended. + if ($participant_join_date > $end_timestamp) { continue; } - if (!empty($session_node->field_scheduled_date[LANGUAGE_NONE][0]['value2'])) { - // Since start and end dates for session are always set to 00:00, - // we add one day to session end date. - // Otherwise, if session was started and ended at same day, it's - // as if session was stated and ended on exact same time. - $session_end_date = strtotime($session_node->field_scheduled_date[LANGUAGE_NONE][0]['value2']) + 24 * 60 * 60; - // Participant has joined after the session has ended. - if ($pmtct_join_date > $session_end_date) { - continue; - } - } - - $period = hedley_stats_get_period($period_name); + $expected_person['expected_date'] = hedley_stats_convert_timestamp_to_nominal($start_timestamp); - // Check if patient has missed the session. This can happen - // only if session has started before patient has graduated the program. - if ($session_start_date < $pmtct_graduate_date) { - // Check if the person (patient) has any measurements. - $measurements = hedley_activity_get_person_measurements_by_period($child_id, $measurements_types, $period, 1); + $sessions_ids = $sessions_by_period[$period]; + if (!empty($sessions_ids)) { + $measurements = hedley_activity_get_person_measurements_during_sessions_by_period($child_id, $sessions_ids, $periods_ranges[$period]); if (empty($measurements)) { // This person missed the session because they don't have any // measurements found for that session on this date, so we add it to // the list of dates, to be able to filter it in the frontend. - $expected_person['expected_date'] = hedley_stats_convert_timestamp_to_nominal($session_start_date); $missed_session[] = $expected_person; } } - if ($pmtct_graduate_date >= $period['start'] && $pmtct_graduate_date <= $period['end']) { - $expected_person['expected_date'] = hedley_stats_convert_timestamp_to_nominal($session_start_date); + if ($participant_graduate_date <= $end_timestamp) { + // Graduation date is within the period. $completed_program[] = $expected_person; } } } + // Free up memory. drupal_static_reset(); } @@ -1738,11 +1721,7 @@ function hedley_stats_get_base_query($health_center_id, $node_type, $period = FA $query = db_select('node', 'node'); $query->fields('node', ['nid', 'type', 'created']); - $encounter_types = [ - 'acute_illness_encounter', - 'nutrition_encounter', - 'prenatal_encounter', - ]; + $encounter_types = hedley_general_get_individual_encounter_types(); $participant_types = [ 'individual_participant', 'pmtct_participant', From d150dc0d8cff9c9cc948ed1364ce09b924f82c15 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 09:47:27 +0300 Subject: [PATCH 08/17] Code improvements + documentation [ci skip] --- .../hedley_activity/hedley_activity.module | 25 +++-------- .../custom/hedley_stats/hedley_stats.module | 41 +++++++++++++------ 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/server/hedley/modules/custom/hedley_activity/hedley_activity.module b/server/hedley/modules/custom/hedley_activity/hedley_activity.module index 4254ffb65c..0452c52441 100644 --- a/server/hedley/modules/custom/hedley_activity/hedley_activity.module +++ b/server/hedley/modules/custom/hedley_activity/hedley_activity.module @@ -340,21 +340,17 @@ function hedley_activity_node_by_peer_in_session(\EntityMetadataWrapper $wrapper } /** - * Get measurements taken for person during one of sessions, within a period. + * Counts measurements taken for person during all given sessions. * * @param int $person_id * The person node ID. * @param array $sessions_ids - * The IDs of sessions. - * @param array $period - * The period of the measurement, contains a start and an end. - * @param int $range - * Optional; The query's range. + * The IDs of the sessions. * - * @return array - * Node IDs if they exist or empty array if no nodes exist. + * @return int + * Number of measurements. */ -function hedley_activity_get_person_measurements_during_sessions_by_period($person_id, array $sessions_ids, array $period, $range = 100) { +function hedley_activity_count_person_measurements_taken_during_sessions($person_id, array $sessions_ids) { $query = db_select('node', 'node'); $query->fields('node', ['nid']); $query->condition('status', NODE_PUBLISHED); @@ -369,14 +365,5 @@ function hedley_activity_get_person_measurements_during_sessions_by_period($pers $session_field_name = $session_field . '.' . $session_field . '_target_id'; $query->condition($session_field_name, $sessions_ids, 'IN'); - $date_field = 'field_date_measured'; - hedley_general_join_field_to_query($query, 'node', $date_field); - $date_field_name = $date_field . '.' . $date_field . '_value'; - $query->condition($date_field_name, [$period['start'], $period['end']], 'BETWEEN'); - - $query->range(0, $range); - - return $query - ->execute() - ->fetchAllAssoc('nid'); + return $query->countQuery()->execute()->fetchField(); } diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 6da9608b6a..8f2c478bba 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1026,9 +1026,23 @@ function hedley_stats_get_family_planning_stats_by_period($health_center_id, $pe } /** - * Return Session attendance related states by health center. - * - * The period of this statistic is hardcoded for the last three months. + * Return FBF sessions attendance related states by health center. + * + * The statistics we need are: + * - Children that have missed sessions by period. + * - Children that are graduating the program, by period. + * + * The logic: + * 1. Pull all participants (mother+child pair) of FBF clinics + * of the health center. + * 2. Pull all FBF sessions that have were performed during given periods. + * 3. For each participant, for each of periods: + * - Determine if participant was supposed to participate in a session. + * - If so: + * 3.1. Load all measurements taken for child during FFB sessions + * (resolved at point 2). If there are no measurements, we know that + * child has missed the session. + * 3.2. Determine if child graduates within given period. * * @param int $health_center_id * The Health center node ID. @@ -1036,9 +1050,7 @@ function hedley_stats_get_family_planning_stats_by_period($health_center_id, $pe * A list of FBF clinics IDs that belong to health center. * * @return array - * Array with the result. - * - * @throws \Exception + * Array with the results. */ function hedley_stats_get_session_attendance_stats_by_health_center($health_center_id, array $fbf_clinics) { // Return the cache if exists. @@ -1054,6 +1066,10 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent } $periods = [ + // On front-end, we have a selection only for 2 periods - + // this month and previous month. + // We add the 'two months ago' period, to support display of + // the '% from previous months' located on cards. HEDLEY_STATS_PERIOD_TWO_MONTHS_AGO, HEDLEY_STATS_PERIOD_LAST_MONTH, HEDLEY_STATS_PERIOD_THIS_MONTH, @@ -1062,8 +1078,6 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $completed_program = []; $missed_session = []; - // Loop through the clinics and get sessions for each clinic and from there - // get expected people for each clinic. foreach ($fbf_clinics as $clinic_id) { $sessions_by_period = $periods_ranges = []; foreach ($periods as $period) { @@ -1111,15 +1125,16 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent continue; } + // Setting expected date to first day in range, to allow proper + // filtering on front-end. $expected_person['expected_date'] = hedley_stats_convert_timestamp_to_nominal($start_timestamp); $sessions_ids = $sessions_by_period[$period]; if (!empty($sessions_ids)) { - $measurements = hedley_activity_get_person_measurements_during_sessions_by_period($child_id, $sessions_ids, $periods_ranges[$period]); - if (empty($measurements)) { - // This person missed the session because they don't have any - // measurements found for that session on this date, so we add it to - // the list of dates, to be able to filter it in the frontend. + $count = hedley_activity_count_person_measurements_taken_during_sessions($child_id, $sessions_ids); + if ($count == 0) { + // The child missed the session during given period, because + // no measurements was taken for him. $missed_session[] = $expected_person; } } From d9f16e27372f4d27caceeac4bef4d3949f204256 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 12:09:34 +0300 Subject: [PATCH 09/17] Improve generation time of Case Management data structure [ci skip] --- .../custom/hedley_stats/hedley_stats.module | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 8f2c478bba..e7856b1f2d 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1544,39 +1544,36 @@ function hedley_stats_get_all_clinic_types() { * @throws \Exception */ function hedley_stats_get_nutrition_measurements_grouped_by_person($health_center_id, $period, $extend = FALSE) { - $clinic_types = hedley_stats_get_all_clinic_types(); - - $result = []; - foreach ($clinic_types as $clinic_type) { - $result[$clinic_type] = hedley_stats_get_nutrition_measurements_for_clinic_type_grouped_by_person($health_center_id, $clinic_type, $period, $extend); - } - + $result = hedley_stats_get_nutrition_measurements_grouped_by_clinic_type_and_person($health_center_id, $period, $extend); $result['individual'] = hedley_stats_get_nutrition_measurements_for_individual_encounters_grouped_by_person($health_center_id, $period, $extend); return $result; } /** - * Gets all nutrition measurements by clinic type, for a health center. + * Gets all group encounter nutrition measurements, for a health center. * - * Results are for certain period, and grouped by person. + * Results are for certain period, and grouped by clinic type and person. * * @param int $health_center_id * The Health center node ID. - * @param string $clinic_type - * The type of the clinic. * @param string $period * The period to query. * @param bool $extend * Optional; If True, data goes back one more month for 'one year' period. * * @return array - * Nutrition measurements by clinic type, for a health center, - * within given period. + * Nutrition measurements grouped by clinic type and person. * * @throws \Exception */ -function hedley_stats_get_nutrition_measurements_for_clinic_type_grouped_by_person($health_center_id, $clinic_type, $period, $extend = FALSE) { +function hedley_stats_get_nutrition_measurements_grouped_by_clinic_type_and_person($health_center_id, $period, $extend = FALSE) { + $result = []; + $clinic_types = hedley_stats_get_all_clinic_types(); + foreach ($clinic_types as $clinic_type) { + $result[$clinic_type] = []; + } + // All group measurements with nutrition fields mapping. $node_types = heldey_stats_get_nutrition_measurements_mapping_for_group_encounter(); @@ -1598,23 +1595,19 @@ function hedley_stats_get_nutrition_measurements_for_clinic_type_grouped_by_pers hedley_general_join_field_to_query($query, 'node', 'field_muac', FALSE); hedley_general_join_field_to_query($query, 'node', 'field_zscore_length', FALSE); - // Filter by clinic type. - $query->condition('field_group_type.field_group_type_value', $clinic_type); - - $result = hedley_stats_run_node_query_in_batches($query); + $rows = hedley_stats_run_node_query_in_batches($query); // Group measurements by person. - $people = []; - foreach ($result as $measurement) { + foreach ($rows as $row) { // Get fields for current node type. - $measurement_fields = $node_types[$measurement->type]; + $measurement_fields = $node_types[$row->type]; foreach ($measurement_fields as $measurement_field) { - $people[$measurement->field_person][$measurement->type][$measurement->field_date_measured][$measurement_field] = $measurement->{$measurement_field}; + $result[$row->field_group_type][$row->field_person][$row->type][$row->field_date_measured][$measurement_field] = $row->{$measurement_field}; } } - return $people; + return $result; } /** From 0b8c2dafd319074089fd6e521a33e386b6441783 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 13:45:08 +0300 Subject: [PATCH 10/17] Load patients data with node_load_multiple() to save DB hits [ci skip] --- .../custom/hedley_stats/hedley_stats.module | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index e7856b1f2d..4bd5d3703a 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1398,8 +1398,28 @@ function hedley_stats_get_case_management_for_period($health_center_id, $period ]; $data = hedley_stats_get_nutrition_measurements_grouped_by_person($health_center_id, $period, $extend); - $result = []; + $people = []; + foreach ($data as $people_data) { + $people = array_merge($people, array_keys($people_data)); + } + $people = array_unique($people); + $people_info = []; + foreach (array_chunk($people, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $people_info[$node->nid] = [ + 'id' => $node->nid, + 'name' => $node->title, + 'birth_date' => $node->field_birth_date[LANGUAGE_NONE][0]['value'], + 'gender' => $node->field_gender[LANGUAGE_NONE][0]['value'], + ]; + } + // Free up memory. + drupal_static_reset(); + } + + $result = []; foreach ($data as $program_type => $people_data) { if (empty($people_data)) { $result[$program_type] = []; @@ -1408,19 +1428,8 @@ function hedley_stats_get_case_management_for_period($health_center_id, $period $people = []; foreach ($people_data as $id => $person) { - // Using node_load() instead of entity_metadata_wrapper(), because latter - // has shown excessive memory usage, and did not respond when - // drupal_static_reset() was invoked to free up memory. - $node = node_load($id); - $birth_date = $node->field_birth_date[LANGUAGE_NONE][0]['value']; - - $item = [ - 'id' => $id, - 'name' => $node->title, - 'birth_date' => date("Y-m-d", strtotime($birth_date)), - 'gender' => $node->field_gender[LANGUAGE_NONE][0]['value'], - 'nutrition' => $nutrition_per_person, - ]; + $item = $people_info[$id]; + $item['nutrition'] = $nutrition_per_person; foreach ($person as $type => $measurement) { $one_day = 24 * 60 * 60; @@ -1500,8 +1509,6 @@ function hedley_stats_get_case_management_for_period($health_center_id, $period } $result[$program_type] = $people; - // Free up memory. - drupal_static_reset(); } // Set the cache. From 75e94d0fba39aaf588794006f75ba1afdf071ca8 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 13:46:56 +0300 Subject: [PATCH 11/17] Satisfy coder --- server/hedley/modules/custom/hedley_stats/hedley_stats.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 4bd5d3703a..70751a63fd 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1134,7 +1134,7 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent $count = hedley_activity_count_person_measurements_taken_during_sessions($child_id, $sessions_ids); if ($count == 0) { // The child missed the session during given period, because - // no measurements was taken for him. + // no measurements was taken for them. $missed_session[] = $expected_person; } } From f3e60873ed90c0d9a8e3c061d5482f6abf99503a Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 18:07:14 +0300 Subject: [PATCH 12/17] More replacements of node_load() node_load_multiple() --- .../custom/hedley_stats/hedley_stats.module | 174 +++++++++++++----- 1 file changed, 127 insertions(+), 47 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 70751a63fd..d2904d95ad 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -855,43 +855,66 @@ function hedley_stats_get_children_beneficiaries_group_stats($health_center_id, $result = hedley_stats_run_node_query_in_batches($query); + $adults = $children = []; + foreach ($result as $row) { + $adults[] = $row->field_adult; + $children[] = $row->field_person; + } + $adults = array_unique($adults); + $children = array_unique($children); + + $adults_info = $children_info = []; + foreach (array_chunk($adults, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $adults_info[$node->nid] = [ + 'name' => $node->title, + 'phone_number' => empty($node->field_phone_number) ? '' : $node->field_phone_number[LANGUAGE_NONE][0]['value'], + ]; + } + // Free up memory. + drupal_static_reset(); + } + foreach (array_chunk($children, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $children_info[$node->nid] = [ + 'id' => $node->nid, + 'name' => $node->title, + 'birth_date' => $node->field_birth_date[LANGUAGE_NONE][0]['value'], + 'gender' => $node->field_gender[LANGUAGE_NONE][0]['value'], + ]; + } + // Free up memory. + drupal_static_reset(); + } + $items = []; foreach (hedley_stats_get_all_clinic_types() as $clinic_type) { $items[$clinic_type] = []; } foreach ($result as $row) { - $clinic_type = $row->field_group_type; - // Using node_load() instead of entity_metadata_wrapper(), because latter - // has shown excessive memory usage, and did not respond when - // drupal_static_reset() was invoked to free up memory. - $child_node = node_load($row->field_person); - $birth_date = $child_node->field_birth_date[LANGUAGE_NONE][0]['value']; - + $child_id = $row->field_person; + $birth_date = $children_info[$child_id]['birth_date']; if (empty($birth_date)) { // Child got no birthdate set. We can't process // its data on client, so we skip it. continue; } - // Using node_load() instead of entity_metadata_wrapper(), because latter - // has shown excessive memory usage, and did not respond when - // drupal_static_reset() was invoked to free up memory. - $adult_node = node_load($row->field_adult); - $phone_number = empty($adult_node->field_phone_number) ? '' : $adult_node->field_phone_number[LANGUAGE_NONE][0]['value']; - + $adult_id = $row->field_adult; + $clinic_type = $row->field_group_type; $items[$clinic_type][] = [ 'created' => hedley_stats_convert_timestamp_to_nominal($row->created), - 'id' => $child_node->nid, - 'name' => $child_node->title, + 'id' => $children_info[$child_id]['id'], + 'name' => $children_info[$child_id]['name'], 'birth_date' => date("Y-m-d", strtotime($birth_date)), - 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], - 'mother_name' => $adult_node->title, - 'phone_number' => $phone_number, + 'gender' => $children_info[$child_id]['gender'], + 'mother_name' => $adults_info[$adult_id]['name'], + 'phone_number' => $adults_info[$adult_id]['phone_number'], 'graduation_date' => date_format(date_create($row->field_expected_field_expected_value2), 'Y-m-d'), ]; - // Free up memory. - drupal_static_reset(); } return $items; @@ -924,40 +947,54 @@ function hedley_stats_get_children_beneficiaries_individual_stats($health_center $result = hedley_stats_run_node_query_in_batches($query); + $children = []; + foreach ($result as $row) { + $children[] = $row->field_person; + } + $children = array_unique($children); + + $children_info = []; + foreach (array_chunk($children, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $children_info[$node->nid] = [ + 'id' => $node->nid, + 'name' => $node->title, + 'birth_date' => $node->field_birth_date[LANGUAGE_NONE][0]['value'], + 'gender' => $node->field_gender[LANGUAGE_NONE][0]['value'], + ]; + } + // Free up memory. + drupal_static_reset(); + } + $items = []; - foreach ($result as $key => $row) { - // Using node_load() instead of entity_metadata_wrapper(), because latter - // has shown excessive memory usage, and did not respond when - // drupal_static_reset() was invoked to free up memory. - $child_node = node_load($row->field_person); - $birth_date = strtotime($child_node->field_birth_date[LANGUAGE_NONE][0]['value']); + foreach ($result as $row) { + $child_id = $row->field_person; + $birth_date = $children_info[$child_id]['birth_date']; if (empty($birth_date)) { // Child got no birthdate set. We can't process // its data on client, so we skip it. continue; } + $birth_date = strtotime($birth_date); // Child graduates at age of 13. $graduation_date = strtotime('+13 years', $birth_date); $items[] = [ 'created' => hedley_stats_convert_timestamp_to_nominal($row->created), - 'id' => $child_node->nid, - 'name' => $child_node->title, + 'id' => $children_info[$child_id]['id'], + 'name' => $children_info[$child_id]['name'], 'birth_date' => date("Y-m-d", $birth_date), - 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], + 'gender' => $children_info[$child_id]['gender'], 'graduation_date' => hedley_stats_convert_timestamp_to_nominal($graduation_date), // No need to provide mother details, because we do not present // it anywhere, when it comes to individual participant data. 'mother_name' => '', 'phone_number' => '', ]; - - // Free up memory. - if ($key % 500 == 0) { - drupal_static_reset(); - } } return $items; @@ -1087,28 +1124,71 @@ function hedley_stats_get_session_attendance_stats_by_health_center($health_cent // Get PMTCT participants by clinic. $participants_ids = hedley_stats_get_pmtct_participants_by_clinic($clinic_id, 5000); + $participants_info = []; + $children = $mothers = []; + foreach (array_chunk($participants_ids, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $children[] = $node->field_person[LANGUAGE_NONE][0]['target_id']; + $mothers[] = $node->field_adult[LANGUAGE_NONE][0]['target_id']; + $participants_info[$node->nid] = [ + 'child_id' => $node->field_person[LANGUAGE_NONE][0]['target_id'], + 'mother_id' => $node->field_adult[LANGUAGE_NONE][0]['target_id'], + 'join_date' => $node->created, + 'graduate_date' => strtotime($node->field_expected[LANGUAGE_NONE][0]['value2']), + ]; + } + // Free up memory. + drupal_static_reset(); + } + + $children = array_unique($children); + $mothers = array_unique($mothers); + $children_info = $mothers_info = []; + foreach (array_chunk($mothers, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $mothers_info[$node->nid] = [ + 'name' => $node->title, + 'phone_number' => empty($node->field_phone_number) ? '' : $node->field_phone_number[LANGUAGE_NONE][0]['value'], + ]; + } + // Free up memory. + drupal_static_reset(); + } + foreach (array_chunk($children, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $children_info[$node->nid] = [ + 'name' => $node->title, + 'birth_date' => $node->field_birth_date[LANGUAGE_NONE][0]['value'], + 'gender' => $node->field_gender[LANGUAGE_NONE][0]['value'], + ]; + } + // Free up memory. + drupal_static_reset(); + } foreach ($participants_ids as $participant_id) { - $participant_node = node_load($participant_id); - $child_id = $participant_node->field_person[LANGUAGE_NONE][0]['target_id']; - $mother_id = $participant_node->field_adult[LANGUAGE_NONE][0]['target_id']; + $participant_info = $participants_info[$participant_id]; + $child_id = $participant_info['child_id']; + $mother_id = $participant_info['mother_id']; if (empty($child_id) || empty($mother_id)) { continue; } - $child_node = node_load($child_id); - $mother_node = node_load($mother_id); - $participant_join_date = $participant_node->created; - $participant_graduate_date = strtotime($participant_node->field_expected[LANGUAGE_NONE][0]['value2']); - $phone_number = !empty($mother_node->field_phone_number) ? $mother_node->field_phone_number[LANGUAGE_NONE][0]['value'] : ''; + $child_info = $children_info[$child_id]; + $mother_info = $mothers_info[$mother_id]; + $participant_join_date = $participant_info['join_date']; + $participant_graduate_date = $participant_info['graduate_date']; $expected_person = [ - 'name' => $child_node->title, - 'gender' => $child_node->field_gender[LANGUAGE_NONE][0]['value'], - 'birth_date' => hedley_restful_timestamp_only_date($child_node->field_birth_date[LANGUAGE_NONE][0]['value']), - 'mother_name' => $mother_node->title, - 'phone_number' => $phone_number, + 'name' => $child_info['name'], + 'gender' => $child_info['gender'], + 'birth_date' => hedley_restful_timestamp_only_date($child_info['birth_date']), + 'mother_name' => $mother_info['name'], + 'phone_number' => $mother_info['phone_number'], 'expected_date' => NULL, ]; From 4bc50810fbcea4c93e20decde5572f5880b2357b Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 18:51:16 +0300 Subject: [PATCH 13/17] Replace last occurance of node_load() --- .../modules/custom/hedley_stats/hedley_stats.module | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index d2904d95ad..4264b60679 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -493,10 +493,12 @@ function hedley_stats_get_villages_with_residents($health_center_id) { $villages_ids = hedley_health_center_get_villages_by_health_center($health_center_id); $result = []; - foreach ($villages_ids as $village_id) { - $village_node = node_load($village_id); - $village_uuid = $village_node->field_uuid[LANGUAGE_NONE][0]['value']; - $result[$village_uuid] = hedley_chw_get_village_residents($village_id); + foreach (array_chunk($villages_ids, 500) as $ids) { + $nodes = node_load_multiple($ids); + foreach ($nodes as $node) { + $village_uuid = $node->field_uuid[LANGUAGE_NONE][0]['value']; + $result[$village_uuid] = hedley_chw_get_village_residents($node->nid); + } } // Store in cache. From a8b4f7a6721eee2877bad75f3488f97d4fd6ae68 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 31 Jul 2023 18:52:03 +0300 Subject: [PATCH 14/17] Free up memory --- server/hedley/modules/custom/hedley_stats/hedley_stats.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 4264b60679..fecd25ad74 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -499,6 +499,8 @@ function hedley_stats_get_villages_with_residents($health_center_id) { $village_uuid = $node->field_uuid[LANGUAGE_NONE][0]['value']; $result[$village_uuid] = hedley_chw_get_village_residents($node->nid); } + // Free up memory. + drupal_static_reset(); } // Store in cache. From e99a930332e8e164d214de8e0b549a16c01c06c6 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 1 Aug 2023 13:00:45 +0300 Subject: [PATCH 15/17] WIP [ci skip] --- .../custom/hedley_stats/hedley_stats.module | 131 ++++++++++-------- 1 file changed, 76 insertions(+), 55 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index fecd25ad74..16e031dfd0 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -1300,13 +1300,7 @@ function hedley_stats_get_total_encounters($health_center_id, $villages_with_res * @throws \Exception */ function hedley_stats_get_total_encounters_for_residents($health_center_id, $patients_ids = FALSE) { - $clinic_types = hedley_stats_get_all_clinic_types(); - - $result = []; - foreach ($clinic_types as $clinic_type) { - $result[$clinic_type] = hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $clinic_type, $patients_ids); - } - + $result = hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $patients_ids); $result['individual'] = hedley_stats_get_total_nutrition_encounters($health_center_id, $patients_ids); return $result; @@ -1330,45 +1324,56 @@ function hedley_stats_get_total_encounters_for_residents($health_center_id, $pat * * @throws \Exception */ -function hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $clinic_type, $patients_ids = FALSE) { - $result = [ - 'last_year' => 0, - 'this_year' => 0, - ]; - +function hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $patients_ids = FALSE) { + $result = []; + $clinic_types = hedley_stats_get_all_clinic_types(); if (is_array($patients_ids) && empty($patients_ids)) { - // Patients list is empty. Return empty result. + // Patients list is empty. No encounters. Return empty result. + foreach ($clinic_types as $clinic_type) { + $result[$clinic_type] = [ + 'last_year' => 0, + 'this_year' => 0, + ]; + } + return $result; } - $queries['last_year'] = hedley_stats_get_base_query($health_center_id, 'attendance', HEDLEY_STATS_PERIOD_LAST_YEAR); - $queries['this_year'] = hedley_stats_get_base_query($health_center_id, 'attendance', HEDLEY_STATS_PERIOD_ONE_YEAR); + $total_by_patient_at_clinic['last_year'] = hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, HEDLEY_STATS_PERIOD_LAST_YEAR); + $total_by_patient_at_clinic['this_year'] = hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, HEDLEY_STATS_PERIOD_ONE_YEAR); - foreach ($queries as $key => $query) { - // Get the group type. - hedley_general_join_field_to_query($query, 'node', 'field_session'); - hedley_general_join_field_to_query($query, 'node', 'field_attended'); - hedley_general_join_field_to_query($query, 'node', 'field_clinic', TRUE, 'field_session.field_session_target_id'); - hedley_general_join_field_to_query($query, 'node', 'field_group_type', TRUE, 'field_clinic.field_clinic_target_id'); + foreach ($clinic_types as $clinic_type) { + $result[$clinic_type] = [ + 'last_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient_at_clinic['last_year'][$clinic_type], $patients_ids), + 'this_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient_at_clinic['this_year'][$clinic_type], $patients_ids), + ]; + } - // Only participants who actually attended the sessions. - $query->condition('field_attended.field_attended_value', TRUE); + return $result; +} - // Only of specific clinic type. - $query->condition('field_group_type.field_group_type_value', $clinic_type); +function hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, $period) { + $query = hedley_stats_get_base_query($health_center_id, 'attendance', $period); + hedley_general_join_field_to_query($query, 'node', 'field_session'); + hedley_general_join_field_to_query($query, 'node', 'field_attended'); + hedley_general_join_field_to_query($query, 'node', 'field_clinic', TRUE, 'field_session.field_session_target_id'); + hedley_general_join_field_to_query($query, 'node', 'field_group_type', TRUE, 'field_clinic.field_clinic_target_id'); + hedley_general_join_field_to_query($query, 'node', 'field_person'); + // Only participants who actually attended the sessions. + $query->condition('field_attended.field_attended_value', TRUE); + $result = $query->execute()->fetchAll(); - // If patients list specified values. - if (is_array($patients_ids)) { - hedley_general_join_field_to_query($query, 'node', 'field_person'); - $query->condition('field_person.field_person_target_id', $patients_ids, 'IN'); - } + $total_by_patient_at_clinic = []; + $clinic_types = hedley_stats_get_all_clinic_types(); + foreach ($clinic_types as $clinic_type) { + $total_by_patient_at_clinic[$clinic_type] = []; + } - $result[$key] = $query - ->execute() - ->rowCount(); + foreach ($result as $row) { + $total_by_patient_at_clinic[$row->field_group_type][$row->field_person] = isset($total_by_patient_at_clinic[$row->field_group_type][$row->field_person]) ? $total_by_patient_at_clinic[$row->field_group_type][$row->field_person] + 1 : 1; } - return $result; + return $total_by_patient_at_clinic; } /** @@ -1388,34 +1393,50 @@ function hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $cl * @throws \Exception */ function hedley_stats_get_total_nutrition_encounters($health_center_id, $patients_ids = FALSE) { - $result = [ - 'last_year' => 0, - 'this_year' => 0, + if (is_array($patients_ids) && empty($patients_ids)) { + // Patients list is empty. No encounters. Return empty result. + return [ + 'last_year' => 0, + 'this_year' => 0, + ]; + } + + $total_by_patient['last_year'] = hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, HEDLEY_STATS_PERIOD_LAST_YEAR); + $total_by_patient['this_year'] = hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, HEDLEY_STATS_PERIOD_ONE_YEAR); + + return [ + 'last_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient['last_year'], $patients_ids), + 'this_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient['this_year'], $patients_ids), ]; +} - if (is_array($patients_ids) && empty($patients_ids)) { - // Patients list is empty. Return empty result. - return $result; +function hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient, $patients_ids = FALSE) { + if ($patients_ids === FALSE) { + return array_sum($total_by_patient); } - $queries['last_year'] = hedley_stats_get_base_query($health_center_id, 'nutrition_encounter', HEDLEY_STATS_PERIOD_LAST_YEAR); - $queries['this_year'] = hedley_stats_get_base_query($health_center_id, 'nutrition_encounter', HEDLEY_STATS_PERIOD_ONE_YEAR); + return array_sum( + array_filter( + $total_by_patient, + function ($key) use ($patients_ids) { return in_array($key, $patients_ids); }, + ARRAY_FILTER_USE_KEY + ) + ); +} - foreach ($queries as $key => $query) { - // If patients list specified values. - if (is_array($patients_ids)) { - hedley_general_join_field_to_query($query, 'node', 'field_individual_participant'); - hedley_general_join_field_to_query($query, 'node', 'field_person', TRUE, 'field_individual_participant.field_individual_participant_target_id'); +function hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, $period) { + $query = hedley_stats_get_base_query($health_center_id, 'nutrition_encounter', $period); + hedley_general_join_field_to_query($query, 'node', 'field_individual_participant'); + hedley_general_join_field_to_query($query, 'node', 'field_person', TRUE, 'field_individual_participant.field_individual_participant_target_id'); + $result = $query->execute()->fetchAll(); - $query->condition('field_person.field_person_target_id', $patients_ids, 'IN'); - } + $total_by_patient = []; + foreach ($result as $row) { + $total_by_patient[$row->field_person] = !empty($total_by_patient[$row->field_person]) ? $total_by_patient[$row->field_person] + 1 : 1; - $result[$key] = $query - ->execute() - ->rowCount(); } - return $result; + return $total_by_patient; } /** @@ -1495,7 +1516,7 @@ function hedley_stats_get_case_management_for_period($health_center_id, $period $people_info[$node->nid] = [ 'id' => $node->nid, 'name' => $node->title, - 'birth_date' => $node->field_birth_date[LANGUAGE_NONE][0]['value'], + 'birth_date' => hedley_restful_timestamp_only_date($node->field_birth_date[LANGUAGE_NONE][0]['value']), 'gender' => $node->field_gender[LANGUAGE_NONE][0]['value'], ]; } From 164588f185807342479f0f7d92d0a02fbdf3cfe0 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 1 Aug 2023 13:12:06 +0300 Subject: [PATCH 16/17] Complete logic rewrite [ci skip] --- .../custom/hedley_stats/hedley_stats.module | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 16e031dfd0..57c8ea175a 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -358,11 +358,11 @@ function hedley_stats_calculate_stats_for_health_center($health_center_id) { // Calculate statistics that are provided for FBF clinics only. hedley_stats_get_session_attendance_stats_by_health_center($health_center_id, $fbf_clinics); - $villages_with_residents = hedley_stats_get_villages_with_residents($health_center_id); hedley_stats_get_case_management($health_center_id); hedley_stats_get_children_beneficiaries_stats_by_period($health_center_id); hedley_stats_get_family_planning_stats_by_period($health_center_id, HEDLEY_STATS_PERIOD_PAST_THREE_MONTHS); + $villages_with_residents = hedley_stats_get_villages_with_residents($health_center_id); hedley_stats_get_total_encounters($health_center_id, $villages_with_residents); hedley_stats_get_acute_illness_data($health_center_id); hedley_stats_get_prenatal_data($health_center_id); @@ -1264,11 +1264,20 @@ function hedley_stats_get_total_encounters($health_center_id, $villages_with_res return $cache_data; } - $result['global'] = hedley_stats_get_total_encounters_for_residents($health_center_id); + $total_by_patient_at_clinic = [ + 'last_year' => hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, HEDLEY_STATS_PERIOD_LAST_YEAR), + 'this_year' => hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, HEDLEY_STATS_PERIOD_ONE_YEAR), + ]; + $total_by_patient_individual = [ + 'last_year' => hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, HEDLEY_STATS_PERIOD_LAST_YEAR), + 'this_year' => hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, HEDLEY_STATS_PERIOD_ONE_YEAR), + ]; + + $result['global'] = hedley_stats_get_total_encounters_for_residents($total_by_patient_at_clinic, $total_by_patient_individual); $result['villages'] = []; foreach ($villages_with_residents as $village_uuid => $residents_ids) { - $result['villages'][$village_uuid] = hedley_stats_get_total_encounters_for_residents($health_center_id, $residents_ids); + $result['villages'][$village_uuid] = hedley_stats_get_total_encounters_for_residents($total_by_patient_at_clinic, $total_by_patient_individual, $residents_ids); } // Free up memory. @@ -1299,9 +1308,9 @@ function hedley_stats_get_total_encounters($health_center_id, $villages_with_res * * @throws \Exception */ -function hedley_stats_get_total_encounters_for_residents($health_center_id, $patients_ids = FALSE) { - $result = hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $patients_ids); - $result['individual'] = hedley_stats_get_total_nutrition_encounters($health_center_id, $patients_ids); +function hedley_stats_get_total_encounters_for_residents($total_by_patient_at_clinic, $total_by_patient_individual, $patients_ids = FALSE) { + $result = hedley_stats_get_total_encounters_by_clinic_type($total_by_patient_at_clinic, $patients_ids); + $result['individual'] = hedley_stats_get_total_nutrition_encounters($total_by_patient_individual, $patients_ids); return $result; } @@ -1324,7 +1333,7 @@ function hedley_stats_get_total_encounters_for_residents($health_center_id, $pat * * @throws \Exception */ -function hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $patients_ids = FALSE) { +function hedley_stats_get_total_encounters_by_clinic_type(array $total_by_patient_at_clinic, $patients_ids = FALSE) { $result = []; $clinic_types = hedley_stats_get_all_clinic_types(); if (is_array($patients_ids) && empty($patients_ids)) { @@ -1339,9 +1348,6 @@ function hedley_stats_get_total_encounters_by_clinic_type($health_center_id, $pa return $result; } - $total_by_patient_at_clinic['last_year'] = hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, HEDLEY_STATS_PERIOD_LAST_YEAR); - $total_by_patient_at_clinic['this_year'] = hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, HEDLEY_STATS_PERIOD_ONE_YEAR); - foreach ($clinic_types as $clinic_type) { $result[$clinic_type] = [ 'last_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient_at_clinic['last_year'][$clinic_type], $patients_ids), @@ -1392,7 +1398,7 @@ function hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_ * * @throws \Exception */ -function hedley_stats_get_total_nutrition_encounters($health_center_id, $patients_ids = FALSE) { +function hedley_stats_get_total_nutrition_encounters(array $total_by_patient, $patients_ids = FALSE) { if (is_array($patients_ids) && empty($patients_ids)) { // Patients list is empty. No encounters. Return empty result. return [ @@ -1401,9 +1407,6 @@ function hedley_stats_get_total_nutrition_encounters($health_center_id, $patient ]; } - $total_by_patient['last_year'] = hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, HEDLEY_STATS_PERIOD_LAST_YEAR); - $total_by_patient['this_year'] = hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, HEDLEY_STATS_PERIOD_ONE_YEAR); - return [ 'last_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient['last_year'], $patients_ids), 'this_year' => hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient['this_year'], $patients_ids), From e26f31f4ffb091821e60bb29d0065640456f797b Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 1 Aug 2023 14:39:11 +0300 Subject: [PATCH 17/17] Docs --- .../custom/hedley_stats/hedley_stats.module | 69 +++++++++++++++---- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/server/hedley/modules/custom/hedley_stats/hedley_stats.module b/server/hedley/modules/custom/hedley_stats/hedley_stats.module index 57c8ea175a..c802f6a675 100644 --- a/server/hedley/modules/custom/hedley_stats/hedley_stats.module +++ b/server/hedley/modules/custom/hedley_stats/hedley_stats.module @@ -358,7 +358,6 @@ function hedley_stats_calculate_stats_for_health_center($health_center_id) { // Calculate statistics that are provided for FBF clinics only. hedley_stats_get_session_attendance_stats_by_health_center($health_center_id, $fbf_clinics); - hedley_stats_get_case_management($health_center_id); hedley_stats_get_children_beneficiaries_stats_by_period($health_center_id); hedley_stats_get_family_planning_stats_by_period($health_center_id, HEDLEY_STATS_PERIOD_PAST_THREE_MONTHS); @@ -1298,8 +1297,10 @@ function hedley_stats_get_total_encounters($health_center_id, $villages_with_res * Includes total number of encounters for each type of clinic, and * total number of individual nutrition encounters. * - * @param int $health_center_id - * The Health center node ID. + * @param array $total_by_patient_at_clinic + * For each of clinics, total number of sessions for each patient. + * @param array $total_by_patient_individual + * Total number of Nutrition encounters for each patient. * @param bool|array $patients_ids * Optional; a list of patients IDs, for which totals are counted. * @@ -1308,7 +1309,7 @@ function hedley_stats_get_total_encounters($health_center_id, $villages_with_res * * @throws \Exception */ -function hedley_stats_get_total_encounters_for_residents($total_by_patient_at_clinic, $total_by_patient_individual, $patients_ids = FALSE) { +function hedley_stats_get_total_encounters_for_residents(array $total_by_patient_at_clinic, array $total_by_patient_individual, $patients_ids = FALSE) { $result = hedley_stats_get_total_encounters_by_clinic_type($total_by_patient_at_clinic, $patients_ids); $result['individual'] = hedley_stats_get_total_nutrition_encounters($total_by_patient_individual, $patients_ids); @@ -1321,10 +1322,8 @@ function hedley_stats_get_total_encounters_for_residents($total_by_patient_at_cl * If patients list is not provided (defaults to False), * overall totals are calculated. * - * @param int $health_center_id - * The Health center node ID. - * @param string $clinic_type - * The type of the clinic. + * @param array $total_by_patient_at_clinic + * For each of clinics, total number of sessions for each patient. * @param bool|array $patients_ids * Optional; a list of patients IDs, for which totals are counted. * @@ -1358,6 +1357,22 @@ function hedley_stats_get_total_encounters_by_clinic_type(array $total_by_patien return $result; } +/** + * Generates data structure with number of Group encounters per patient. + * + * Keys - patients IDs, Values - number of encounters. + * Broken down by clinics types. + * + * @param int $health_center_id + * The health center node ID. + * @param string $period + * The period for the data. + * + * @return array + * Array with number of Nutrition encounters per patient, within period. + * + * @throws Exception + */ function hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_id, $period) { $query = hedley_stats_get_base_query($health_center_id, 'attendance', $period); hedley_general_join_field_to_query($query, 'node', 'field_session'); @@ -1388,8 +1403,8 @@ function hedley_stats_get_total_encounters_by_patients_at_clinic($health_center_ * If patients list is not provided (defaults to False), * overall totals are calculated. * - * @param int $health_center_id - * The Health center node ID. + * @param array $total_by_patient + * Total number of Nutrition encounters for each patient. * @param bool|array $patients_ids * Optional; a list of patients IDs, for which totals are counted. * @@ -1413,7 +1428,20 @@ function hedley_stats_get_total_nutrition_encounters(array $total_by_patient, $p ]; } -function hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patient, $patients_ids = FALSE) { +/** + * Counts total number of encounters from data structure. + * + * If patients IDs list is provided, counts only for those patients. + * + * @param array $total_by_patient + * Total number of Nutrition encounters for each patient. + * @param array|bool $patients_ids + * Optional; a list of patients IDs, for which total is counted. + * + * @return int + * Total number of encounters at data structure. + */ +function hedley_stats_total_encounters_from_breakdown_by_patient(array $total_by_patient, $patients_ids = FALSE) { if ($patients_ids === FALSE) { return array_sum($total_by_patient); } @@ -1421,12 +1449,29 @@ function hedley_stats_total_encounters_from_breakdown_by_patient($total_by_patie return array_sum( array_filter( $total_by_patient, - function ($key) use ($patients_ids) { return in_array($key, $patients_ids); }, + function ($key) use ($patients_ids) { + return in_array($key, $patients_ids); + }, ARRAY_FILTER_USE_KEY ) ); } +/** + * Generates data structure with number of Nutrition encounters per patient. + * + * Keys - patients IDs, Values - number of encounters. + * + * @param int $health_center_id + * The health center node ID. + * @param string $period + * The period for the data. + * + * @return array + * Array with number of Nutrition encounters per patient, within period. + * + * @throws Exception + */ function hedley_stats_get_total_nutrition_encounters_by_patient($health_center_id, $period) { $query = hedley_stats_get_base_query($health_center_id, 'nutrition_encounter', $period); hedley_general_join_field_to_query($query, 'node', 'field_individual_participant');