From 1c3ea10af22b42ed12d8cebad63a22cab168f5d8 Mon Sep 17 00:00:00 2001 From: Angie-540 Date: Thu, 7 Sep 2023 16:40:00 +0300 Subject: [PATCH 1/6] weekly predictions --- programs/patient-data-resolver.service.js | 7 +++++-- programs/scope-builder.service.js | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/programs/patient-data-resolver.service.js b/programs/patient-data-resolver.service.js index 8a04056cc..558f19521 100755 --- a/programs/patient-data-resolver.service.js +++ b/programs/patient-data-resolver.service.js @@ -6,6 +6,7 @@ const etlHivSummary = require('../dao/patient/etl-patient-hiv-summary-dao'); const encounterService = require('../service/openmrs-rest/encounter'); const dcPatientvisitEvaluator = require('../service/dc-patient-visit-evaluator'); const covidAssessmentService = require('../service/covid-assessment-service'); +const MlWeeklyPredictionsService = require('..service/ml-weekly-predictions.service'); var _ = require('underscore'); const availableKeys = { @@ -20,7 +21,8 @@ const availableKeys = { dcQualifedVisits: getQualifiedDcVisits, validateMedicationRefill: getMedicationRefillVisits, latestCovidAssessment: getLatestCovidAssessment, - isViremicHighVL: getLatestVL + isViremicHighVL: getLatestVL, + weeklyPredictedPatients: getWeeklyPredictedPatients }; const def = { @@ -35,7 +37,8 @@ const def = { dcQualifedVisits: getQualifiedDcVisits, validateMedicationRefill: getMedicationRefillVisits, getLatestCovidAssessment: getLatestCovidAssessment, - isViremicHighVL: getLatestVL + isViremicHighVL: getLatestVL, + getWeeklyPredictedPatients: getWeeklyPredictedPatients }; module.exports = def; diff --git a/programs/scope-builder.service.js b/programs/scope-builder.service.js index a11505f5b..f8a992236 100755 --- a/programs/scope-builder.service.js +++ b/programs/scope-builder.service.js @@ -8,6 +8,7 @@ const def = { module.exports = def; function buildScope(dataDictionary) { + console.log(dataDictionary); const scope = { isPatientTransferredOut: false, isFirstAMPATHHIVVisit: true, From b2c2da2030981626f88b700e542122b7afecd064 Mon Sep 17 00:00:00 2001 From: Angie-540 Date: Wed, 13 Sep 2023 16:49:48 +0300 Subject: [PATCH 2/6] Restrict pre-appointment visit --- etl-routes.js | 4 +- programs/patient-data-resolver.service.js | 5 +- programs/patient-program-config.json | 72 ++++++++++++++--------- programs/scope-builder.service.js | 11 +++- service/ml-weekly-predictions.service.js | 35 ++++++++++- 5 files changed, 94 insertions(+), 33 deletions(-) diff --git a/etl-routes.js b/etl-routes.js index 907807096..b06da97e2 100755 --- a/etl-routes.js +++ b/etl-routes.js @@ -79,9 +79,11 @@ import { DefaulterListService } from './service/defaulter-list-service'; import { ClinicFlowService } from './service/clinic-flow-service'; import { getPatientCovidVaccinationStatus } from './service/covid-19/covid-19-vaccination-summary'; import { Covid19MonthlyReport } from './service/covid-19/covid-19-monthly-report'; -import { MlWeeklyPredictionsService } from './service/ml-weekly-predictions.service'; import { getPatientPredictedScore } from './service/predictions/ml-prediction-service'; import { CohortModuleService } from './app/otz/cohort-module.service'; +const { + default: MlWeeklyPredictionsService +} = require('./service/ml-weekly-predictions.service'); module.exports = (function () { var routes = [ diff --git a/programs/patient-data-resolver.service.js b/programs/patient-data-resolver.service.js index 558f19521..ab5c9992d 100755 --- a/programs/patient-data-resolver.service.js +++ b/programs/patient-data-resolver.service.js @@ -6,8 +6,11 @@ const etlHivSummary = require('../dao/patient/etl-patient-hiv-summary-dao'); const encounterService = require('../service/openmrs-rest/encounter'); const dcPatientvisitEvaluator = require('../service/dc-patient-visit-evaluator'); const covidAssessmentService = require('../service/covid-assessment-service'); -const MlWeeklyPredictionsService = require('..service/ml-weekly-predictions.service'); +const weeklyPredictionsService = require('../service/ml-weekly-predictions.service'); var _ = require('underscore'); +const { + default: MlWeeklyPredictionsService +} = require('../service/ml-weekly-predictions.service'); const availableKeys = { patient: getPatient, diff --git a/programs/patient-program-config.json b/programs/patient-program-config.json index 93955e2cc..b95fa7c39 100755 --- a/programs/patient-program-config.json +++ b/programs/patient-program-config.json @@ -6,7 +6,8 @@ "enrollment", "patientEncounters", "isPatientTransferredOut", - "latestCovidAssessment" + "latestCovidAssessment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -806,7 +807,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "programLocation === intendedVisitLocationUuid && MlLocations", + "allowedIf": "programLocation === intendedVisitLocationUuid && inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -1258,7 +1259,8 @@ "patientEnrollment", "patientEncounters", "isPatientTransferredOut", - "latestCovidAssessment" + "latestCovidAssessment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "enrollIf": "gender === 'F'", @@ -1968,7 +1970,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "programLocation === intendedVisitLocationUuid && MlLocations", + "allowedIf": "programLocation === intendedVisitLocationUuid && inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -2214,7 +2216,8 @@ "enrollment", "hivLastTenClinicalEncounters", "patientEncounters", - "latestCovidAssessment" + "latestCovidAssessment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -2383,7 +2386,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "programLocation === intendedVisitLocationUuid && MlLocations", + "allowedIf": "programLocation === intendedVisitLocationUuid && inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -2419,6 +2422,7 @@ } ] }, + "c19aec66-1a40-4588-9b03-b6be55a8dd1d": { "name": "PrEP PROGRAM", "dataDependencies": [ @@ -2426,7 +2430,9 @@ "enrollment", "hivLastTenClinicalEncounters", "patientEncounters", - "latestCovidAssessment" + "latestCovidAssessment", + "weeklyPredictedPatients", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -2611,7 +2617,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "MlLocations", + "allowedIf": "inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -2649,7 +2655,8 @@ "patient", "enrollment", "hivLastTenClinicalEncounters", - "latestCovidAssessment" + "latestCovidAssessment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -2812,7 +2819,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "MlLocations", + "allowedIf": "inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -2907,7 +2914,8 @@ "isPatientTransferredOut", "dcQualifedVisits", "validateMedicationRefill", - "latestCovidAssessment" + "latestCovidAssessment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -3189,7 +3197,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "programLocation === intendedVisitLocationUuid && MlLocations", + "allowedIf": "programLocation === intendedVisitLocationUuid && inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -4108,7 +4116,8 @@ "dataDependencies": [ "patient", "enrollment", - "hivLastTenClinicalEncounters" + "hivLastTenClinicalEncounters", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -4212,7 +4221,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "programLocation === intendedVisitLocationUuid && MlLocations", + "allowedIf": "programLocation === intendedVisitLocationUuid && inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -4245,7 +4254,8 @@ "dataDependencies": [ "patient", "enrollment", - "hivLastTenClinicalEncounters" + "hivLastTenClinicalEncounters", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -4351,7 +4361,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "MlLocations", + "allowedIf": "inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -4487,7 +4497,8 @@ "isPatientTransferredOut", "patientEncounters", "latestCovidAssessment", - "isViremicHighVL" + "isViremicHighVL", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -4725,7 +4736,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "MlLocations", + "allowedIf": "inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -4766,7 +4777,8 @@ "dataDependencies": [ "patient", "enrollment", - "hivLastTenClinicalEncounters" + "hivLastTenClinicalEncounters", + "weeklyPredictedPatients" ], "enrollmentOptions": { "stateChangeEncounterTypes": { @@ -4832,7 +4844,8 @@ "dataDependencies": [ "patient", "enrollment", - "hivLastTenClinicalEncounters" + "hivLastTenClinicalEncounters", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -4978,7 +4991,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "MlLocations", + "allowedIf": "inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -5047,7 +5060,8 @@ "dataDependencies": [ "patient", "enrollment", - "hivLastTenClinicalEncounters" + "hivLastTenClinicalEncounters", + "weeklyPredictedPatients" ], "incompatibleWith": [], "visitTypes": [ @@ -5082,7 +5096,7 @@ { "uuid": "6bdec0cd-d3fb-41da-9ef3-20076f349dc1", "name": "Pre Appointment Follow Up Visit", - "allowedIf": "programLocation === intendedVisitLocationUuid && MlLocations", + "allowedIf": "programLocation === intendedVisitLocationUuid && inPrediction", "encounterTypes": [ { "uuid": "d5e2d3b3-2061-4721-86f1-5852cde6475a", @@ -5441,7 +5455,8 @@ "patient", "enrollment", "patientEncounters", - "isPatientTransferredOut" + "isPatientTransferredOut", + "weeklyPredictedPatients" ], "enrollmentOptions": { "requiredProgramQuestions": [ @@ -5507,7 +5522,8 @@ "patient", "enrollment", "patientEncounters", - "patientEnrollment" + "patientEnrollment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "enrollIf": "gender === 'F'" @@ -5599,7 +5615,8 @@ "patient", "enrollment", "patientEncounters", - "patientEnrollment" + "patientEnrollment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "enrollIf": "gender === 'F'" @@ -5703,7 +5720,8 @@ "patient", "enrollment", "patientEncounters", - "patientEnrollment" + "patientEnrollment", + "weeklyPredictedPatients" ], "enrollmentOptions": { "enrollIf": "gender === 'F'" diff --git a/programs/scope-builder.service.js b/programs/scope-builder.service.js index f8a992236..3d9869ef6 100755 --- a/programs/scope-builder.service.js +++ b/programs/scope-builder.service.js @@ -8,7 +8,6 @@ const def = { module.exports = def; function buildScope(dataDictionary) { - console.log(dataDictionary); const scope = { isPatientTransferredOut: false, isFirstAMPATHHIVVisit: true, @@ -19,10 +18,16 @@ function buildScope(dataDictionary) { screenedForCovidToday: false, isViremicHighVL: false, isEligibleForMedicationRefill: false, - isEligibleForCommunityVisit: false + isEligibleForCommunityVisit: false, + inPrediction: false }; - let isStandardDcVisit = false; + if ( + dataDictionary.weeklyPredictedPatients && + dataDictionary.weeklyPredictedPatients.length > 0 + ) { + scope.inPrediction = true; + } // Restrict to Pilot locations scope.MlLocations = [ '08feb8ae-1352-11df-a1f1-0026b9348838', diff --git a/service/ml-weekly-predictions.service.js b/service/ml-weekly-predictions.service.js index 8ac737610..5f9d7ee73 100644 --- a/service/ml-weekly-predictions.service.js +++ b/service/ml-weekly-predictions.service.js @@ -1,8 +1,9 @@ const Promise = require('bluebird'); +const db = require('../etl-db'); import { BaseMysqlReport } from '../app/reporting-framework/base-mysql.report'; import { PatientlistMysqlReport } from '../app/reporting-framework/patientlist-mysql.report'; -export class MlWeeklyPredictionsService { +export default class MlWeeklyPredictionsService { getAggregateReport(reportParams) { return new Promise(function (resolve, reject) { const report = new BaseMysqlReport( @@ -44,4 +45,36 @@ export class MlWeeklyPredictionsService { }); }); } + getPatientsWithPredictions(patientUuId) { + return new Promise((resolve, reject) => { + const week = this.getNextWeek(); + const predictedPatients = `SELECT ml.person_id AS person_id, fi.ccc AS ccc_number, fi.ovcid AS ovcid_id, fi.nupi AS upi_number, p.name AS program, ml.predicted_risk AS predicted_risk, ml.week AS week, ml.predicted_prob_disengage AS predicted_prob_disengage, DATE_FORMAT(ml.prediction_generated_date, '%Y-%m-%d') AS prediction_generated_date, DATE_FORMAT(ml.rtc_date, '%Y-%m-%d') AS rtc_date, pre.follow_up_type AS follow_up_type, pre.follow_up_reason AS follow_up_reason, pre.rescheduled_date AS rescheduled_date, pre.reschedule_appointment AS reschedule_appointment, pre.contact_reached_phone_follow_up AS contact_reached, pre.attempted_home_visit AS attempted_home_visit, pre.reason_not_attempted_home_visit AS reason_not_attempted_home_visit, pre.was_client_found AS was_client_found, pre.reason_client_not_found AS reason_client_not_found, pre.home_visit_personnel AS home_visit_personnel, IF((pre.is_successful_phone_follow_up = 'YES' OR (pre.attempted_home_visit = 'YES' AND pre.was_client_found = 'YES')), 1, 0) AS was_follow_up_successful, pre.comments AS comments, t1.uuid AS patient_uuid, t1.uuid AS uuid, t1.gender AS gender, t1.birthdate AS birthdate, EXTRACT(YEAR FROM (FROM_DAYS(DATEDIFF(NOW(), t1.birthdate)))) AS age, CONCAT(COALESCE(person_name.given_name, ''), ' ', COALESCE(person_name.middle_name, ''), ' ', COALESCE(person_name.family_name, '')) AS person_name, GROUP_CONCAT(DISTINCT id.identifier SEPARATOR ', ') AS identifiers, GROUP_CONCAT(DISTINCT contacts.value SEPARATOR ', ') AS phone_number, DATE_FORMAT(fh.rtc_date, '%Y-%m-%d') AS latest_rtc_date, fli.vl_1 AS latest_vl, CASE WHEN (fli.vl_1 > 1000) THEN 'Suspected Treatment Failure' WHEN (fli.vl_1 >= 200 AND fli.vl_1 < 1000) THEN 'High Risk Low Level Viremia' WHEN (fli.vl_1 >= 50 AND fli.vl_1 < 200) THEN 'Low Risk Low Level Viremia' WHEN (fli.vl_1 < 50) THEN 'LDL' ELSE NULL END AS vl_category, DATE_FORMAT(fli.vl_1_date, '%Y-%m-%d') AS latest_vl_date, CONCAT(COALESCE(DATE_FORMAT(fh.encounter_datetime, '%Y-%m-%d'), ''), ' ', COALESCE(et.name, '')) AS last_appointment, fh.cur_arv_meds AS cur_meds, fh.vl_2 AS previous_vl, DATE_FORMAT(fh.vl_2_date, '%Y-%m-%d') AS previous_vl_date, pa.address3 AS nearest_center, CASE WHEN (c.received_covid_19_vaccine = 1066 AND EXTRACT(YEAR FROM (FROM_DAYS(DATEDIFF(NOW(), t1.birthdate)))) >= 15) THEN 'Not vaccinated' WHEN (c.person_id IS NULL AND c.second_dose_vaccine_administered IS NULL AND EXTRACT(YEAR FROM (FROM_DAYS(DATEDIFF(NOW(), t1.birthdate)))) >= 15) THEN 'Unknown Covid 19 Vaccination' WHEN (c.vaccination_status = 11907) THEN 'Partially Vaccinated' WHEN (c.vaccination_status = 2208) THEN 'Fully Vaccinated' ELSE NULL END AS covid_19_vaccination_status, CASE WHEN (consent.patient_sms_consent_provided = 1066) THEN 'NO' WHEN (consent.patient_sms_consent_provided = 1065) THEN 'YES' ELSE NULL END AS sms_consent_provided, CASE WHEN (fh.patient_category IS NULL) THEN 'N/A' WHEN (fh.patient_category = 0) THEN 'N/A' WHEN (fh.patient_category = 11550) THEN 'UnWell' WHEN (fh.patient_category = 9070) THEN 'Well' ELSE NULL END AS patient_category, consent.sms_receive_time AS sms_receive_time, NULL AS sms_delivery_status, DATE_FORMAT(tb.tb_screening_datetime, '%Y-%m-%d') AS tb_screening_date, CASE WHEN (tb.tb_screening_result = 10974) THEN 'INH PROPHYLAXIS' WHEN (tb.tb_screening_result = 10922) THEN 'ISONIAZID PREVENTIVE TREATMENT PROGRAM' WHEN (tb.tb_screening_result = 10767) THEN 'ON TREATMENT FOR DISEASE' WHEN (tb.tb_screening_result = 6621) THEN 'NOT ASSESSED' WHEN (tb.tb_screening_result = 10678) THEN 'NO SIGNS OR SYMPTOMS OF DISEASE' WHEN (tb.tb_screening_result = 656) THEN 'ISONIAZID' WHEN (tb.tb_screening_result = 6137) THEN 'CONFIRMED' WHEN (tb.tb_screening_result = 6176) THEN 'CURRENTLY ON TUBERCULOSIS TREATMENT' WHEN (tb.tb_screening_result = 1118) THEN 'NOT DONE' WHEN (tb.tb_screening_result = 6971) THEN 'POSSIBLE' WHEN (tb.tb_screening_result = 1107) THEN 'NONE' ELSE NULL END AS tb_screening_result, DATE_FORMAT(cs.encounter_datetime, '%Y-%m-%d') AS cervical_screening_date, CASE WHEN (cs.observations_from_positive_via_or_via_vili_test IS NOT NULL) THEN 'VIA or VIA/VILI' WHEN (cs.screening_method = 2322) THEN 'HPV' WHEN (cs.screening_method = 885) THEN 'PAP SMEAR' WHEN (cs.screening_method = 9434) THEN 'VIA or VIA/VILI' ELSE NULL END AS cervical_screening_method, CASE WHEN (cs.observations_from_positive_via_or_via_vili_test = 9593) THEN 'FRIABLE TISSUE' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7472) THEN 'ATYPICAL BLOOD VESSELS' WHEN (cs.observations_from_positive_via_or_via_vili_test = 6497) THEN 'DYSFUNCTIONAL UTERINE BLEEDING' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7293) THEN 'ULCER' WHEN (cs.observations_from_positive_via_or_via_vili_test = 9592) THEN 'BRIGHT WHITE LESION' WHEN (cs.observations_from_positive_via_or_via_vili_test = 9591) THEN 'OYSTERWHITE LESION' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7470) THEN 'PUNCTUATED CAPILLARIES' WHEN (cs.observations_from_positive_via_or_via_vili_test = 5245) THEN 'PALLOR' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7469) THEN 'ACETOWHITE LESION' WHEN (cs.observations_from_positive_via_or_via_vili_test = 1115) THEN 'NORMAL' WHEN (cs.pap_smear_test_result = 6) THEN 'INVASIVE CANCER' WHEN (cs.pap_smear_test_result = 5) THEN 'AGUS' WHEN (cs.pap_smear_test_result = 4) THEN 'HSIL/CIS' WHEN (cs.pap_smear_test_result = 3) THEN 'LSIL' WHEN (cs.pap_smear_test_result = 2) THEN 'ASCUS' WHEN (cs.pap_smear_test_result = 1) THEN 'NORMAL' WHEN (cs.hpv_test_result = 3) THEN 'INDETERMINATE' WHEN (cs.hpv_test_result = 2) THEN 'POSITIVE' WHEN (cs.hpv_test_result = 1) THEN 'NEGATIVE' WHEN (cs.via_or_via_vili_test_result = 3) THEN 'SUSPICIOUS OF CANCER' WHEN (cs.via_or_via_vili_test_result = 2) THEN 'POSITIVE' WHEN (cs.via_or_via_vili_test_result = 1) THEN 'NEGATIVE' ELSE NULL END AS cervical_screening_result FROM predictions.ml_weekly_predictions ml LEFT JOIN etl.flat_hiv_summary_v15b de ON (de.encounter_id = ml.encounter_id) LEFT JOIN etl.flat_patient_identifiers_v1 fi ON (de.person_id = fi.patient_id) LEFT JOIN etl.program_visit_map pm ON (de.visit_type = pm.visit_type_id) LEFT JOIN amrs.program p ON (p.program_id = pm.program_type_id) LEFT JOIN etl.pre_appointment_summary pre ON (pre.person_id = ml.person_id) INNER JOIN amrs.person t1 ON (ml.person_id = t1.person_id) LEFT JOIN (SELECT fli.person_id, fli.hiv_viral_load AS vl_1, fli.test_datetime AS vl_1_date FROM etl.flat_labs_and_imaging fli INNER JOIN (SELECT person_id, MAX(test_datetime) AS max_vl_1_date, MAX(encounter_id) AS encounter_id FROM etl.flat_labs_and_imaging fli WHERE fli.hiv_viral_load IS NOT NULL GROUP BY person_id) max_dates ON fli.person_id = max_dates.person_id AND fli.encounter_id = max_dates.encounter_id) fli ON (fli.person_id = t1.person_id) LEFT JOIN amrs.person_name person_name ON (t1.person_id = person_name.person_id AND (person_name.voided IS NULL || person_name.voided = 0) AND person_name.preferred = 1) LEFT JOIN amrs.patient_identifier id ON (t1.person_id = id.patient_id AND (id.voided IS NULL || id.voided = 0)) LEFT JOIN amrs.person_attribute contacts ON (t1.person_id = contacts.person_id AND (contacts.voided IS NULL || contacts.voided = 0) AND contacts.person_attribute_type_id IN (10 , 48)) LEFT JOIN etl.flat_hiv_summary_v15b fh ON (t1.person_id = fh.person_id AND fh.next_clinical_location_id IS NULL AND fh.encounter_type NOT IN (99999)) LEFT JOIN amrs.encounter_type et ON (fh.encounter_type = et.encounter_type_id) LEFT JOIN amrs.person_address pa ON (t1.person_id = pa.person_id) LEFT JOIN etl.flat_covid_extract c ON (t1.person_id = c.person_id AND c.next_encounter_datetime IS NULL) LEFT JOIN etl.flat_consent consent ON (consent.person_id = t1.person_id) LEFT JOIN etl.flat_hiv_summary_v15b tb ON (t1.person_id = tb.person_id AND tb.next_clinical_datetime_hiv IS NULL AND tb.is_clinical_encounter = 1) LEFT JOIN etl.flat_cervical_cancer_screening_rc cs ON (cs.person_id = t1.person_id AND cs.next_clinical_datetime_cervical_cancer_screening IS NULL) LEFT JOIN etl.sms_delivery_report delivery_report ON (delivery_report.person_id = t1.person_id AND DATE_FORMAT(delivery_report.date_created, '%Y-%m-%d') = '{startDate}') WHERE t1.uuid='${patientUuId}' AND (ml.week = '${week}') AND (ml.predicted_risk IS NOT NULL) GROUP BY t1.person_id ORDER BY ml.predicted_prob_disengage DESC , ml.prediction_generated_date DESC , pre.encounter_datetime DESC`; + const queryParts = { + sql: predictedPatients + }; + console.log('query', queryParts); + db.queryServer(queryParts, function (result) { + result.sql = predictedPatients; + resolve(result.result); + }); + }); + } + getNextWeek() { + const currentDate = new Date(); + currentDate.setDate(currentDate.getDate()); // Add 7 days + + const year = currentDate.getFullYear(); + const weekNumber = this.getISOWeek(currentDate); + + return `${year}-W${weekNumber.toString().padStart(2, '0')}`; + } + + // Helper function to get ISO week number + getISOWeek(date) { + const dayOfWeek = date.getUTCDay() || 7; + date.setUTCDate(date.getUTCDate() + 4 - dayOfWeek); + const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); + const weekNumber = Math.ceil(((date - yearStart) / 86400000 + 1) / 7); + return weekNumber; + } } From 1f87a75f3ffdc415c026fe365d74ad9e994eb3b9 Mon Sep 17 00:00:00 2001 From: Angie-540 Date: Thu, 14 Sep 2023 10:49:27 +0300 Subject: [PATCH 3/6] pre-appointment --- service/ml-weekly-predictions.service.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/service/ml-weekly-predictions.service.js b/service/ml-weekly-predictions.service.js index 5f9d7ee73..7ca75511e 100644 --- a/service/ml-weekly-predictions.service.js +++ b/service/ml-weekly-predictions.service.js @@ -45,10 +45,26 @@ export default class MlWeeklyPredictionsService { }); }); } + getPatientsWithPredictions(patientUuId) { return new Promise((resolve, reject) => { const week = this.getNextWeek(); - const predictedPatients = `SELECT ml.person_id AS person_id, fi.ccc AS ccc_number, fi.ovcid AS ovcid_id, fi.nupi AS upi_number, p.name AS program, ml.predicted_risk AS predicted_risk, ml.week AS week, ml.predicted_prob_disengage AS predicted_prob_disengage, DATE_FORMAT(ml.prediction_generated_date, '%Y-%m-%d') AS prediction_generated_date, DATE_FORMAT(ml.rtc_date, '%Y-%m-%d') AS rtc_date, pre.follow_up_type AS follow_up_type, pre.follow_up_reason AS follow_up_reason, pre.rescheduled_date AS rescheduled_date, pre.reschedule_appointment AS reschedule_appointment, pre.contact_reached_phone_follow_up AS contact_reached, pre.attempted_home_visit AS attempted_home_visit, pre.reason_not_attempted_home_visit AS reason_not_attempted_home_visit, pre.was_client_found AS was_client_found, pre.reason_client_not_found AS reason_client_not_found, pre.home_visit_personnel AS home_visit_personnel, IF((pre.is_successful_phone_follow_up = 'YES' OR (pre.attempted_home_visit = 'YES' AND pre.was_client_found = 'YES')), 1, 0) AS was_follow_up_successful, pre.comments AS comments, t1.uuid AS patient_uuid, t1.uuid AS uuid, t1.gender AS gender, t1.birthdate AS birthdate, EXTRACT(YEAR FROM (FROM_DAYS(DATEDIFF(NOW(), t1.birthdate)))) AS age, CONCAT(COALESCE(person_name.given_name, ''), ' ', COALESCE(person_name.middle_name, ''), ' ', COALESCE(person_name.family_name, '')) AS person_name, GROUP_CONCAT(DISTINCT id.identifier SEPARATOR ', ') AS identifiers, GROUP_CONCAT(DISTINCT contacts.value SEPARATOR ', ') AS phone_number, DATE_FORMAT(fh.rtc_date, '%Y-%m-%d') AS latest_rtc_date, fli.vl_1 AS latest_vl, CASE WHEN (fli.vl_1 > 1000) THEN 'Suspected Treatment Failure' WHEN (fli.vl_1 >= 200 AND fli.vl_1 < 1000) THEN 'High Risk Low Level Viremia' WHEN (fli.vl_1 >= 50 AND fli.vl_1 < 200) THEN 'Low Risk Low Level Viremia' WHEN (fli.vl_1 < 50) THEN 'LDL' ELSE NULL END AS vl_category, DATE_FORMAT(fli.vl_1_date, '%Y-%m-%d') AS latest_vl_date, CONCAT(COALESCE(DATE_FORMAT(fh.encounter_datetime, '%Y-%m-%d'), ''), ' ', COALESCE(et.name, '')) AS last_appointment, fh.cur_arv_meds AS cur_meds, fh.vl_2 AS previous_vl, DATE_FORMAT(fh.vl_2_date, '%Y-%m-%d') AS previous_vl_date, pa.address3 AS nearest_center, CASE WHEN (c.received_covid_19_vaccine = 1066 AND EXTRACT(YEAR FROM (FROM_DAYS(DATEDIFF(NOW(), t1.birthdate)))) >= 15) THEN 'Not vaccinated' WHEN (c.person_id IS NULL AND c.second_dose_vaccine_administered IS NULL AND EXTRACT(YEAR FROM (FROM_DAYS(DATEDIFF(NOW(), t1.birthdate)))) >= 15) THEN 'Unknown Covid 19 Vaccination' WHEN (c.vaccination_status = 11907) THEN 'Partially Vaccinated' WHEN (c.vaccination_status = 2208) THEN 'Fully Vaccinated' ELSE NULL END AS covid_19_vaccination_status, CASE WHEN (consent.patient_sms_consent_provided = 1066) THEN 'NO' WHEN (consent.patient_sms_consent_provided = 1065) THEN 'YES' ELSE NULL END AS sms_consent_provided, CASE WHEN (fh.patient_category IS NULL) THEN 'N/A' WHEN (fh.patient_category = 0) THEN 'N/A' WHEN (fh.patient_category = 11550) THEN 'UnWell' WHEN (fh.patient_category = 9070) THEN 'Well' ELSE NULL END AS patient_category, consent.sms_receive_time AS sms_receive_time, NULL AS sms_delivery_status, DATE_FORMAT(tb.tb_screening_datetime, '%Y-%m-%d') AS tb_screening_date, CASE WHEN (tb.tb_screening_result = 10974) THEN 'INH PROPHYLAXIS' WHEN (tb.tb_screening_result = 10922) THEN 'ISONIAZID PREVENTIVE TREATMENT PROGRAM' WHEN (tb.tb_screening_result = 10767) THEN 'ON TREATMENT FOR DISEASE' WHEN (tb.tb_screening_result = 6621) THEN 'NOT ASSESSED' WHEN (tb.tb_screening_result = 10678) THEN 'NO SIGNS OR SYMPTOMS OF DISEASE' WHEN (tb.tb_screening_result = 656) THEN 'ISONIAZID' WHEN (tb.tb_screening_result = 6137) THEN 'CONFIRMED' WHEN (tb.tb_screening_result = 6176) THEN 'CURRENTLY ON TUBERCULOSIS TREATMENT' WHEN (tb.tb_screening_result = 1118) THEN 'NOT DONE' WHEN (tb.tb_screening_result = 6971) THEN 'POSSIBLE' WHEN (tb.tb_screening_result = 1107) THEN 'NONE' ELSE NULL END AS tb_screening_result, DATE_FORMAT(cs.encounter_datetime, '%Y-%m-%d') AS cervical_screening_date, CASE WHEN (cs.observations_from_positive_via_or_via_vili_test IS NOT NULL) THEN 'VIA or VIA/VILI' WHEN (cs.screening_method = 2322) THEN 'HPV' WHEN (cs.screening_method = 885) THEN 'PAP SMEAR' WHEN (cs.screening_method = 9434) THEN 'VIA or VIA/VILI' ELSE NULL END AS cervical_screening_method, CASE WHEN (cs.observations_from_positive_via_or_via_vili_test = 9593) THEN 'FRIABLE TISSUE' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7472) THEN 'ATYPICAL BLOOD VESSELS' WHEN (cs.observations_from_positive_via_or_via_vili_test = 6497) THEN 'DYSFUNCTIONAL UTERINE BLEEDING' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7293) THEN 'ULCER' WHEN (cs.observations_from_positive_via_or_via_vili_test = 9592) THEN 'BRIGHT WHITE LESION' WHEN (cs.observations_from_positive_via_or_via_vili_test = 9591) THEN 'OYSTERWHITE LESION' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7470) THEN 'PUNCTUATED CAPILLARIES' WHEN (cs.observations_from_positive_via_or_via_vili_test = 5245) THEN 'PALLOR' WHEN (cs.observations_from_positive_via_or_via_vili_test = 7469) THEN 'ACETOWHITE LESION' WHEN (cs.observations_from_positive_via_or_via_vili_test = 1115) THEN 'NORMAL' WHEN (cs.pap_smear_test_result = 6) THEN 'INVASIVE CANCER' WHEN (cs.pap_smear_test_result = 5) THEN 'AGUS' WHEN (cs.pap_smear_test_result = 4) THEN 'HSIL/CIS' WHEN (cs.pap_smear_test_result = 3) THEN 'LSIL' WHEN (cs.pap_smear_test_result = 2) THEN 'ASCUS' WHEN (cs.pap_smear_test_result = 1) THEN 'NORMAL' WHEN (cs.hpv_test_result = 3) THEN 'INDETERMINATE' WHEN (cs.hpv_test_result = 2) THEN 'POSITIVE' WHEN (cs.hpv_test_result = 1) THEN 'NEGATIVE' WHEN (cs.via_or_via_vili_test_result = 3) THEN 'SUSPICIOUS OF CANCER' WHEN (cs.via_or_via_vili_test_result = 2) THEN 'POSITIVE' WHEN (cs.via_or_via_vili_test_result = 1) THEN 'NEGATIVE' ELSE NULL END AS cervical_screening_result FROM predictions.ml_weekly_predictions ml LEFT JOIN etl.flat_hiv_summary_v15b de ON (de.encounter_id = ml.encounter_id) LEFT JOIN etl.flat_patient_identifiers_v1 fi ON (de.person_id = fi.patient_id) LEFT JOIN etl.program_visit_map pm ON (de.visit_type = pm.visit_type_id) LEFT JOIN amrs.program p ON (p.program_id = pm.program_type_id) LEFT JOIN etl.pre_appointment_summary pre ON (pre.person_id = ml.person_id) INNER JOIN amrs.person t1 ON (ml.person_id = t1.person_id) LEFT JOIN (SELECT fli.person_id, fli.hiv_viral_load AS vl_1, fli.test_datetime AS vl_1_date FROM etl.flat_labs_and_imaging fli INNER JOIN (SELECT person_id, MAX(test_datetime) AS max_vl_1_date, MAX(encounter_id) AS encounter_id FROM etl.flat_labs_and_imaging fli WHERE fli.hiv_viral_load IS NOT NULL GROUP BY person_id) max_dates ON fli.person_id = max_dates.person_id AND fli.encounter_id = max_dates.encounter_id) fli ON (fli.person_id = t1.person_id) LEFT JOIN amrs.person_name person_name ON (t1.person_id = person_name.person_id AND (person_name.voided IS NULL || person_name.voided = 0) AND person_name.preferred = 1) LEFT JOIN amrs.patient_identifier id ON (t1.person_id = id.patient_id AND (id.voided IS NULL || id.voided = 0)) LEFT JOIN amrs.person_attribute contacts ON (t1.person_id = contacts.person_id AND (contacts.voided IS NULL || contacts.voided = 0) AND contacts.person_attribute_type_id IN (10 , 48)) LEFT JOIN etl.flat_hiv_summary_v15b fh ON (t1.person_id = fh.person_id AND fh.next_clinical_location_id IS NULL AND fh.encounter_type NOT IN (99999)) LEFT JOIN amrs.encounter_type et ON (fh.encounter_type = et.encounter_type_id) LEFT JOIN amrs.person_address pa ON (t1.person_id = pa.person_id) LEFT JOIN etl.flat_covid_extract c ON (t1.person_id = c.person_id AND c.next_encounter_datetime IS NULL) LEFT JOIN etl.flat_consent consent ON (consent.person_id = t1.person_id) LEFT JOIN etl.flat_hiv_summary_v15b tb ON (t1.person_id = tb.person_id AND tb.next_clinical_datetime_hiv IS NULL AND tb.is_clinical_encounter = 1) LEFT JOIN etl.flat_cervical_cancer_screening_rc cs ON (cs.person_id = t1.person_id AND cs.next_clinical_datetime_cervical_cancer_screening IS NULL) LEFT JOIN etl.sms_delivery_report delivery_report ON (delivery_report.person_id = t1.person_id AND DATE_FORMAT(delivery_report.date_created, '%Y-%m-%d') = '{startDate}') WHERE t1.uuid='${patientUuId}' AND (ml.week = '${week}') AND (ml.predicted_risk IS NOT NULL) GROUP BY t1.person_id ORDER BY ml.predicted_prob_disengage DESC , ml.prediction_generated_date DESC , pre.encounter_datetime DESC`; + const predictedPatients = `SELECT + ml.person_id AS person_id, + ml.predicted_risk AS predicted_risk, + ml.week AS week +FROM + predictions.ml_weekly_predictions ml + LEFT JOIN + etl.pre_appointment_summary pre ON (pre.person_id = ml.person_id) + INNER JOIN + amrs.person t1 ON (ml.person_id = t1.person_id) +WHERE + t1.uuid = '${patientUuId}' + AND (ml.week = '${week}' ) + AND (ml.predicted_risk IS NOT NULL) +GROUP BY t1.person_id + `; const queryParts = { sql: predictedPatients }; From 25e2703cf85adac4c42bd91fd34e631c277b2bf6 Mon Sep 17 00:00:00 2001 From: Angie-540 Date: Wed, 20 Sep 2023 13:52:03 +0300 Subject: [PATCH 4/6] pre-appointment visit --- programs/scope-builder.service.js | 11 +++-------- service/ml-weekly-predictions.service.js | 1 - 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/programs/scope-builder.service.js b/programs/scope-builder.service.js index 3d9869ef6..363f17e4b 100755 --- a/programs/scope-builder.service.js +++ b/programs/scope-builder.service.js @@ -12,6 +12,7 @@ function buildScope(dataDictionary) { isPatientTransferredOut: false, isFirstAMPATHHIVVisit: true, qualifiesForStandardVisit: false, + isStandardDcVisit: false, qualifiesMedicationRefillVisit: false, lastCovidScreeningDate: '', retroSpective: false, @@ -199,16 +200,10 @@ function buildScope(dataDictionary) { if (result) { isStandardDcVisit = true; } - if ( - dataDictionary.dcQualifedVisits.qualifies_for_standard_visit === 1 || - isStandardDcVisit - ) { + if (dataDictionary.dcQualifedVisits.qualifies_for_standard_visit === 1) { scope.qualifiesForStandardVisit = true; } - if ( - dataDictionary.dcQualifedVisits.qualifies_for_medication_refill === 1 && - !isStandardDcVisit - ) { + if (dataDictionary.dcQualifedVisits.qualifies_for_medication_refill === 1) { scope.qualifiesMedicationRefillVisit = true; } } diff --git a/service/ml-weekly-predictions.service.js b/service/ml-weekly-predictions.service.js index 7ca75511e..02827b2f9 100644 --- a/service/ml-weekly-predictions.service.js +++ b/service/ml-weekly-predictions.service.js @@ -68,7 +68,6 @@ GROUP BY t1.person_id const queryParts = { sql: predictedPatients }; - console.log('query', queryParts); db.queryServer(queryParts, function (result) { result.sql = predictedPatients; resolve(result.result); From 0780ae074e89510c0837a84e53b5b0c8d14c27c9 Mon Sep 17 00:00:00 2001 From: Angie-540 Date: Wed, 20 Sep 2023 17:07:45 +0300 Subject: [PATCH 5/6] pre-appointment visit --- programs/scope-builder.service.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/programs/scope-builder.service.js b/programs/scope-builder.service.js index 363f17e4b..2c797e5ef 100755 --- a/programs/scope-builder.service.js +++ b/programs/scope-builder.service.js @@ -12,7 +12,6 @@ function buildScope(dataDictionary) { isPatientTransferredOut: false, isFirstAMPATHHIVVisit: true, qualifiesForStandardVisit: false, - isStandardDcVisit: false, qualifiesMedicationRefillVisit: false, lastCovidScreeningDate: '', retroSpective: false, @@ -23,6 +22,8 @@ function buildScope(dataDictionary) { inPrediction: false }; + let isStandardDcVisit = false; + if ( dataDictionary.weeklyPredictedPatients && dataDictionary.weeklyPredictedPatients.length > 0 @@ -200,10 +201,16 @@ function buildScope(dataDictionary) { if (result) { isStandardDcVisit = true; } - if (dataDictionary.dcQualifedVisits.qualifies_for_standard_visit === 1) { + if ( + dataDictionary.dcQualifedVisits.qualifies_for_standard_visit === 1 || + isStandardDcVisit + ) { scope.qualifiesForStandardVisit = true; } - if (dataDictionary.dcQualifedVisits.qualifies_for_medication_refill === 1) { + if ( + dataDictionary.dcQualifedVisits.qualifies_for_medication_refill === 1 && + !isStandardDcVisit + ) { scope.qualifiesMedicationRefillVisit = true; } } From bee1350394c3b66a78ed0d035084d25ee55819b0 Mon Sep 17 00:00:00 2001 From: Denzel Kipkemoi Date: Fri, 13 Jun 2025 15:29:18 +0300 Subject: [PATCH 6/6] POC-496: rebased poc 496 --- programs/patient-data-resolver.service.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/programs/patient-data-resolver.service.js b/programs/patient-data-resolver.service.js index ab5c9992d..99385ff4e 100755 --- a/programs/patient-data-resolver.service.js +++ b/programs/patient-data-resolver.service.js @@ -263,3 +263,20 @@ function getLatestVL(patientUuid) { }); }); } + +function getWeeklyPredictedPatients(patientUuid) { + return new Promise((resolve, reject) => { + let ml = new MlWeeklyPredictionsService(); + ml.getPatientsWithPredictions(patientUuid) + .then((result) => { + if (result.length > 0) { + resolve(result); + } else { + resolve([]); + } + }) + .catch((error) => { + reject(error); + }); + }); +}