diff --git a/app/routes/medication-delivery.routes.js b/app/routes/medication-delivery.routes.js new file mode 100644 index 000000000..c01f73752 --- /dev/null +++ b/app/routes/medication-delivery.routes.js @@ -0,0 +1,51 @@ +const { + MedicationDeliveryService +} = require('../../service/medication-delivery-list.service'); +const Boom = require('boom'); + +const routes = [ + { + method: 'GET', + path: '/etl/medication-delivery-list', + config: { + plugins: {}, + handler: function (request, reply) { + const { locationUuids, startDate, endDate } = request.query; + + // Validate required parameters + if (!startDate || !endDate) { + return reply( + new Boom(400, 'Missing required parameters: startDate and endDate') + ); + } + + // Validate date format (basic validation) + const dateRegex = /^\d{4}-\d{2}-\d{2}$/; + if (!dateRegex.test(startDate) || !dateRegex.test(endDate)) { + return reply(new Boom(400, 'Invalid date format. Use YYYY-MM-DD')); + } + + // Validate that startDate is not after endDate + if (new Date(startDate) > new Date(endDate)) { + return reply(new Boom(400, 'startDate cannot be after endDate')); + } + + const medicationDeliveryService = new MedicationDeliveryService(); + medicationDeliveryService + .getMedicationDeliveryList(locationUuids, startDate, endDate) + .then(function (medicationDeliveryList) { + reply(medicationDeliveryList); + }) + .catch(function (error) { + reply(new Boom(500, 'Internal server error.', '', '', error)); + }); + }, + description: 'Get medication delivery list', + notes: + 'Returns the list of patients with their medication delivery status for a given date range. Requires uuid, startDate, and endDate query parameters.', + tags: ['api'] + } + } +]; + +exports.routes = (server) => server.route(routes); diff --git a/service/medication-delivery-list.service.js b/service/medication-delivery-list.service.js new file mode 100644 index 000000000..14a2c37e9 --- /dev/null +++ b/service/medication-delivery-list.service.js @@ -0,0 +1,161 @@ +var db = require('../etl-db'); + +export class MedicationDeliveryService { + getMedicationDeliveryList = (location_uuid, startDate, endDate) => { + return new Promise((resolve, reject) => { + let queryParts = {}; + const sql = ` + SELECT + p.patient_id, + per.uuid AS person_uuid, + e.encounter_datetime AS enrollment_date, + pi_ccc.identifier AS ccc_number, + pn.given_name, + pn.middle_name, + pn.family_name, + pi_nupi.identifier AS nupi_number, + ident.identifier AS identifier, + per.gender, + l.uuid AS location_uuid, + TIMESTAMPDIFF(YEAR, per.birthdate, CURDATE()) AS age, + + MAX(CASE WHEN pa.person_attribute_type_id = 10 THEN pa.value END) AS phone_number, + + MAX(CASE WHEN o.concept_id = 5096 THEN o.value_datetime END) AS 'Return to clinic date', + MAX(CASE WHEN o.concept_id = 9605 THEN o.value_datetime END) AS medication_pickup_date, + + CASE + WHEN EXISTS ( + SELECT 1 + FROM amrs.encounter e186 + WHERE e186.patient_id = p.patient_id + AND e186.encounter_type = 186 + AND e186.encounter_datetime >= MAX(CASE WHEN o.concept_id = 9605 THEN o.value_datetime END) + AND e186.voided = 0 + ) THEN 'Picked' + ELSE 'Not Picked' + END AS pickup_status, + + ( + SELECT obs.value_text + FROM amrs.encounter e186 + JOIN amrs.obs ON obs.encounter_id = e186.encounter_id + WHERE e186.patient_id = p.patient_id + AND e186.encounter_type = 186 + AND e186.encounter_datetime >= MAX(CASE WHEN o.concept_id = 9605 THEN o.value_datetime END) + AND e186.voided = 0 + AND obs.concept_id = 10869 + AND obs.voided = 0 + ORDER BY e186.encounter_datetime DESC + LIMIT 1 + ) AS health_worker, + + fhs.latest_vl, + fhs.latest_vl_date, + fhs.latest_rtc_date, + + GROUP_CONCAT(DISTINCT q_concept.name ORDER BY q_concept.name SEPARATOR ', ') AS current_regimen + +FROM + amrs.patient p +JOIN amrs.encounter e ON p.patient_id = e.patient_id AND e.encounter_type = 2 AND e.voided = 0 +JOIN amrs.location l ON e.location_id = l.location_id +JOIN amrs.obs o ON e.encounter_id = o.encounter_id AND o.voided = 0 +JOIN amrs.person per ON p.patient_id = per.person_id AND per.voided = 0 +JOIN ( + SELECT person_id, given_name, middle_name, family_name + FROM amrs.person_name + WHERE voided = 0 AND preferred = 1 +) pn ON pn.person_id = per.person_id + +LEFT JOIN amrs.person_attribute pa ON per.person_id = pa.person_id AND pa.voided = 0 AND pa.person_attribute_type_id = 10 + +LEFT JOIN ( + SELECT patient_id, MIN(identifier) as identifier + FROM amrs.patient_identifier + WHERE identifier_type IN (28, 29) AND voided = 0 + GROUP BY patient_id +) pi_ccc ON pi_ccc.patient_id = p.patient_id + +LEFT JOIN ( + SELECT patient_id, MIN(identifier) as identifier + FROM amrs.patient_identifier + WHERE identifier_type IN (8, 3, 1) AND voided = 0 + GROUP BY patient_id +) ident ON ident.patient_id = p.patient_id + +LEFT JOIN ( + SELECT patient_id, MIN(identifier) as identifier + FROM amrs.patient_identifier + WHERE identifier_type = 45 AND voided = 0 + GROUP BY patient_id +) pi_nupi ON pi_nupi.patient_id = p.patient_id + +LEFT JOIN ( + SELECT + f.person_id, + f.vl_1 AS latest_vl, + f.vl_1_date AS latest_vl_date, + f.prev_clinical_rtc_date_hiv AS latest_rtc_date, + f.cur_arv_meds AS current_regimen + FROM etl.flat_hiv_summary_v15b f + INNER JOIN ( + SELECT person_id, MAX(encounter_datetime) AS latest_visit + FROM etl.flat_hiv_summary_v15b + GROUP BY person_id + ) latest_summary + ON f.person_id = latest_summary.person_id AND f.encounter_datetime = latest_summary.latest_visit +) fhs ON fhs.person_id = p.patient_id + +LEFT JOIN amrs.drug d ON d.drug_id = o.value_drug +LEFT JOIN amrs.concept_name q_concept + ON q_concept.concept_id = d.concept_id + AND q_concept.locale = 'en' + AND q_concept.locale_preferred = 1 + AND q_concept.voided = 0 + +WHERE + l.uuid = '${location_uuid}' + AND EXISTS ( + SELECT 1 + FROM amrs.obs o2 + WHERE o2.encounter_id = e.encounter_id + AND o2.concept_id = 11932 + AND o2.value_coded = 11939 + AND o2.voided = 0 + ) + +GROUP BY + p.patient_id, + per.uuid, + pi_ccc.identifier, + pn.given_name, + pn.middle_name, + pn.family_name, + pi_nupi.identifier, + ident.identifier, + per.gender, + l.uuid, + per.birthdate, + fhs.latest_vl, + fhs.latest_vl_date, + fhs.latest_rtc_date + +HAVING + medication_pickup_date BETWEEN '${startDate}' AND '${endDate}' + +ORDER BY + pn.given_name, pn.family_name; + `; + + queryParts = { + sql: sql + }; + + return db.queryServer(queryParts, function (result) { + result.sql = sql; + resolve(result); + }); + }); + }; +}