Skip to content

Commit

Permalink
Kavitha | add api to fetch slots by patient list with custom includes
Browse files Browse the repository at this point in the history
  • Loading branch information
kavitha-sundararajan committed Feb 13, 2024
1 parent 80ad1b3 commit 482d0fa
Show file tree
Hide file tree
Showing 14 changed files with 472 additions and 0 deletions.
6 changes: 6 additions & 0 deletions api/src/main/java/org/openmrs/module/ipd/api/dao/SlotDAO.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openmrs.module.ipd.api.dao;

import org.openmrs.Concept;
import org.openmrs.Order;
import org.openmrs.Visit;
import org.openmrs.module.ipd.api.model.Reference;
import org.openmrs.module.ipd.api.model.Slot;
Expand Down Expand Up @@ -32,4 +33,9 @@ public interface SlotDAO {

List<Slot> getSlotsBySubjectIncludingAdministeredTimeFrame(Reference subject, LocalDateTime localStartDate, LocalDateTime localEndDate, Visit visit);

List<Slot> getSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate, LocalDateTime localEndDate);

List<Slot> getImmediatePreviousSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate);

List<Object[]> getSlotDurationForPatientsByOrder(List<Order> orders, List<Concept> serviceTypes);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.hibernate.query.Query;
import org.openmrs.Concept;
import org.openmrs.Order;
import org.openmrs.Visit;
import org.openmrs.module.ipd.api.dao.SlotDAO;
import org.openmrs.module.ipd.api.model.Reference;
Expand Down Expand Up @@ -134,4 +135,83 @@ public List<Slot> getSlotsBySubjectIncludingAdministeredTimeFrame(Reference subj

return query.getResultList();
}

@Override
public List<Slot> getSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate, LocalDateTime localEndDate) {
Query query = sessionFactory.getCurrentSession()
.createQuery("SELECT slot FROM Slot slot \n" +
"INNER JOIN slot.schedule.subject reference \n" +
"INNER JOIN slot.schedule.visit visit \n" +
"WHERE (slot.startDateTime BETWEEN :startDate and :endDate) \n" +
"and slot.voided=0 \n" +
"and visit.stopDatetime is NULL \n" +
"and reference.type = 'org.openmrs.Patient' \n" +
"and reference.targetUuid in (:patientUuidList)");

query.setParameterList("patientUuidList", patientUuidList);
query.setParameter("startDate", localStartDate);
query.setParameter("endDate", localEndDate);

return query.getResultList();
}

@Override
public List<Slot> getImmediatePreviousSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate) {
String maxDateTimeSubquery = "SELECT s.order, MAX(s.startDateTime) AS maxStartDateTime " +
"FROM Slot s " +
"INNER JOIN s.schedule.subject reference " +
"INNER JOIN s.schedule.visit visit " +
"WHERE s.startDateTime < :startDate " +
"AND s.voided = 0 " +
"AND visit.stopDatetime IS NULL " +
"AND reference.type = 'org.openmrs.Patient' " +
"AND reference.targetUuid IN (:patientUuidList) " +
"GROUP BY s.order";

String latestPreviousSlotsQuery = "SELECT slot " +
"FROM Slot slot " +
"INNER JOIN slot.schedule.subject reference " +
"INNER JOIN slot.schedule.visit visit " +
"WHERE slot.startDateTime < :startDate " +
"AND slot.voided = 0 " +
"AND visit.stopDatetime IS NULL " +
"AND reference.type = 'org.openmrs.Patient' " +
"AND reference.targetUuid IN (:patientUuidList) " +
"AND (slot.order, slot.startDateTime) IN " +
"( " + maxDateTimeSubquery + " ) ";

Query query = sessionFactory.getCurrentSession()
.createQuery(latestPreviousSlotsQuery);

query.setParameterList("patientUuidList", patientUuidList);
query.setParameter("startDate", localStartDate);

return query.getResultList();
}

@Override
public List<Object[]> getSlotDurationForPatientsByOrder(List<Order> orders, List<Concept> serviceTypes) {
Query query = sessionFactory.getCurrentSession()
.createQuery("SELECT \n" +
" slot.order AS order,\n" +
" MIN(slot.startDateTime) AS minStartDateTime,\n" +
" MAX(slot.startDateTime) AS maxStartDateTime\n" +
"FROM\n" +
" Slot slot\n" +
"INNER JOIN\n" +
" slot.schedule.subject reference\n" +
"INNER JOIN\n" +
" slot.schedule.visit visit \n" +
"WHERE\n" +
" slot.voided = 0\n" +
" AND slot.serviceType IN (:serviceTypes)\n" +
" AND visit.stopDatetime is NULL \n" +
" AND slot.order IN (:orders)\n" +
"GROUP BY\n" +
" slot.order");

query.setParameterList("orders", orders);
query.setParameterList("serviceTypes", serviceTypes);
return query.getResultList();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openmrs.module.ipd.api.service;

import org.openmrs.Concept;
import org.openmrs.Order;
import org.openmrs.Visit;
import org.openmrs.module.ipd.api.model.Reference;
import org.openmrs.module.ipd.api.model.Slot;
Expand Down Expand Up @@ -37,4 +38,10 @@ public interface SlotService extends OpenmrsService {
List<Slot> getSlotsBySubjectReferenceIdAndForTheGivenTimeFrame(Reference reference, LocalDateTime localStartDate, LocalDateTime localEndDate, Visit visit);

List<Slot> getSlotsBySubjectReferenceIncludingAdministeredTimeFrame(Reference subject, LocalDateTime localStartDate, LocalDateTime localEndDate, Visit visit);

List<Slot> getSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate, LocalDateTime localEndDate);

List<Slot> getImmediatePreviousSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate);

List<Object[]> getSlotDurationForPatientsByOrder(List<Order> orders, List<Concept> serviceTypes);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openmrs.module.ipd.api.service.impl;

import org.openmrs.Concept;
import org.openmrs.Order;
import org.openmrs.api.ConceptService;
import org.openmrs.api.context.Context;
import org.openmrs.Visit;
Expand Down Expand Up @@ -95,4 +96,18 @@ public List<Slot> getSlotsBySubjectReferenceIdAndForTheGivenTimeFrame(Reference
public List<Slot> getSlotsBySubjectReferenceIncludingAdministeredTimeFrame(Reference subject, LocalDateTime localStartDate, LocalDateTime localEndDate, Visit visit) {
return slotDAO.getSlotsBySubjectIncludingAdministeredTimeFrame(subject, localStartDate, localEndDate, visit);
}

@Override
public List<Slot> getSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate, LocalDateTime localEndDate) {
return slotDAO.getSlotsForPatientListByTime(patientUuidList, localStartDate, localEndDate);
}

@Override
public List<Slot> getImmediatePreviousSlotsForPatientListByTime(List<String> patientUuidList, LocalDateTime localStartDate) {
return slotDAO.getImmediatePreviousSlotsForPatientListByTime(patientUuidList, localStartDate);
}

public List<Object[]> getSlotDurationForPatientsByOrder(List<Order> orders, List<Concept> serviceTypes) {
return slotDAO.getSlotDurationForPatientsByOrder(orders, serviceTypes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.junit.jupiter.api.Assertions;
import org.openmrs.Concept;
import org.openmrs.DrugOrder;
import org.openmrs.Order;
import org.openmrs.Patient;
import org.openmrs.Visit;
import org.openmrs.VisitType;
Expand All @@ -21,6 +22,7 @@
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
Expand Down Expand Up @@ -428,4 +430,55 @@ public void shouldGetTheSavedSlotsForPatientBySubjectReferenceAndAGivenTimeFrame
sessionFactory.getCurrentSession().delete(visit);

}

@Test
public void shouldGetSlotsForPatientListByTime() {
executeDataSet("scheduleMedicationsTestData.xml");

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime startTime = LocalDateTime.parse("2024-01-28 11:00:00", formatter);
LocalDateTime endTime = LocalDateTime.parse("2024-01-29 11:00:00", formatter);
List<String> patientUuids = new ArrayList<>();
patientUuids.add("75e04d42-3ca8-11e3-bf2b-0800271c1b75");

List<Slot> slotsForPatientListByTime = slotDAO.getSlotsForPatientListByTime(patientUuids, startTime, endTime);

Assertions.assertEquals(3, slotsForPatientListByTime.size());
}

@Test
public void shouldGetImmediatePreviousSlotsForPatientListByTime() {
executeDataSet("scheduleMedicationsTestData.xml");

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime startTime = LocalDateTime.parse("2024-01-28 11:00:00", formatter);
List<String> patientUuids = new ArrayList<>();
patientUuids.add("75e04d42-3ca8-11e3-bf2b-0800271c1b75");

List<Slot> previousSlots = slotDAO.getImmediatePreviousSlotsForPatientListByTime(patientUuids, startTime);

Assertions.assertEquals(1, previousSlots.size());
}

@Test
public void shouldGetSlotDurationForPatientsByOrder() {
executeDataSet("scheduleMedicationsTestData.xml");

List<Order> orders = new ArrayList<>();
Order order = Context.getOrderService().getOrderByUuid("6d0ae386-707a-4629-9850-f15206e63ab0");
orders.add(order);
List<Concept> serviceTypes = new ArrayList<>();
serviceTypes.add(Context.getConceptService().getConceptByName("MedicationRequest"));

List<Object[]> slotsDuration = slotDAO.getSlotDurationForPatientsByOrder(orders, serviceTypes);

Assertions.assertEquals(1, slotsDuration.size());

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime startTime = LocalDateTime.parse("2024-01-27 20:00:00", formatter);
LocalDateTime endTime = LocalDateTime.parse("2024-01-29 08:00:00", formatter);

Assertions.assertEquals(startTime, slotsDuration.get(0)[1]);
Assertions.assertEquals(endTime, slotsDuration.get(0)[2]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.mockito.junit.MockitoJUnitRunner;
import org.openmrs.Concept;
import org.openmrs.ConceptName;
import org.openmrs.Order;
import org.openmrs.Patient;
import org.openmrs.Visit;
import org.openmrs.module.ipd.api.dao.SlotDAO;
Expand Down Expand Up @@ -137,4 +138,55 @@ public void shouldInvokeGetSlotsBySubjectReferenceAndAdministeredTimeWithGivenTi

Mockito.verify(slotDAO, Mockito.times(1)).getSlotsBySubjectIncludingAdministeredTimeFrame(patientReference, startTime, endTime, visit);
}

@Test
public void shouldInvokeGetSlotsForPatientListByTime() {
List<Slot> slots = new ArrayList<>();
LocalDateTime startTime= LocalDateTime.now();
LocalDateTime endTime = startTime.plusHours(3);
List<String> patientUuids = new ArrayList<>();
patientUuids.add("patientUuid1");
patientUuids.add("patientUuid2");

Mockito.when(slotDAO.getSlotsForPatientListByTime(patientUuids, startTime, endTime)).thenReturn(slots);
slotService.getSlotsForPatientListByTime(patientUuids, startTime, endTime);

Mockito.verify(slotDAO, Mockito.times(1)).getSlotsForPatientListByTime(patientUuids, startTime, endTime);
}

@Test
public void shouldInvokeGetImmediatePreviousSlotsForPatientListByTime() {
List<Slot> slots = new ArrayList<>();
LocalDateTime startTime= LocalDateTime.now();
List<String> patientUuids = new ArrayList<>();
patientUuids.add("patientUuid1");
patientUuids.add("patientUuid2");

Mockito.when(slotDAO.getImmediatePreviousSlotsForPatientListByTime(patientUuids, startTime)).thenReturn(slots);
slotService.getImmediatePreviousSlotsForPatientListByTime(patientUuids, startTime);

Mockito.verify(slotDAO, Mockito.times(1)).getImmediatePreviousSlotsForPatientListByTime(patientUuids, startTime);
}

@Test
public void shouldInvokeGetSlotDurationForPatientsByOrder() {
List<Object[]> slotDurationObjects = new ArrayList<>();
List<Order> orders = new ArrayList<>();
Order order = new Order();
order.setUuid("orderUuid1");
orders.add(order);
order = new Order();
order.setUuid("orderUuid2");
orders.add(order);

List<Concept> serviceTypes = new ArrayList<>();
Concept concept = new Concept();
concept.setUuid("serviceType1");
serviceTypes.add(concept);

Mockito.when(slotDAO.getSlotDurationForPatientsByOrder(orders, serviceTypes)).thenReturn(slotDurationObjects);
slotService.getSlotDurationForPatientsByOrder(orders, serviceTypes);

Mockito.verify(slotDAO, Mockito.times(1)).getSlotDurationForPatientsByOrder(orders, serviceTypes);
}
}
41 changes: 41 additions & 0 deletions api/src/test/resources/scheduleMedicationsTestData.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<location location_id="2" name="General Ward" description="General Ward" creator="1" retired="0" retired_by=" 0" uuid="8d6c993e-c2cc-11de-7921-0010c6affd0f" date_created="2015-09-22 00:00:00" />

<encounter_type encounter_type_id="1" name="REG" description="Some desc" creator="1" date_created="2005-01-01 00:00:00.0" retired="false" uuid="759799ab-c9a5-435e-b671-77773ada74e4"/>
<encounter_type encounter_type_id="2" name="Admission" description="Some desc 2" creator="1" date_created="2005-01-01 00:00:00.0" retired="false" uuid="4ee21921-01cc-4720-a6bf-a61a17c4d05b"/>

<person person_id="1001" gender="M" dead="false" creator="1" birthdate_estimated="0" date_created="2008-08-15 15:57:09.0" voided="false" void_reason="" uuid="75e04d42-3ca8-11e3-bf2b-0800271c1b75"/>
<patient patient_id="1001" creator="1" date_created="2005-09-22 00:00:00.0" changed_by="1" date_changed="2008-08-18 12:29:59.0" voided="false" void_reason=""/>
<person_name person_name_id="1" preferred="true" person_id="1001" prefix="" given_name="Abc" middle_name="" family_name="Def" family_name_suffix="" creator="1" date_created="2005-09-22 00:00:00.0" voided="false" void_reason="" uuid="399e3a7b-6482-409d-94ce-c87bb3ca3cca"/>

<visit visit_id="1" date_started="2024-01-27 00:00:00" date_created="2024-01-22 00:00:00" patient_id="1001" visit_type_id="1" location_id="2" creator="1" changed_by="1" voided_by="1" uuid="84d8b838-1111-11e3-b47b-c6959a448789" voided="0"/>
<encounter encounter_id="1" encounter_type="2" patient_id="1001" visit_id="1" location_id="2" encounter_datetime="2024-01-27 00:00:00.0" creator="1" date_created="2024-02-01 14:09:05.0" voided="false" uuid="b12798aa-f5f4-11e3-b47b-c6959a448567"/>

<concept concept_id="100" retired="false" datatype_id="3" class_id="11" is_set="false" creator="1" date_created="2024-01-27 15:48:00.0" version="" uuid="0abca361-f6bf-49cc-97de-b2f37f099122"/>
<orders order_id="1" order_type_id="15" patient_id="1001" encounter_id="1" urgency="ROUTINE" concept_id="100" orderer="1" care_setting="1" order_action="NEW" order_number="1" instructions="non-expiring" date_activated="2024-01-27 00:00:00.0" scheduled_date="2024-01-27 00:00:00.0" creator="1" date_created="2024-01-27 12:20:22.5" voided="false" uuid="6d0ae386-707a-4629-9850-f15206e63ab0"/>

<drug_order order_id="1" drug_inventory_id="3" dose="125.0" dispense_as_written="true" as_needed="false" frequency="2"/>

<ipd_reference reference_id="1" target_type="org.openmrs.Provider" target_uuid="c1c26908-3f10-11e4-adec-0800271c1b75" creator="1" date_created="2024-01-27 15:48:00.0" name="org.openmrs.Provider/c1c26908-3f10-11e4-adec-0800271c1b75" retired="false" uuid="a4119c8a-9804-495a-b557-49b89dcccf01" />
<ipd_reference reference_id="2" target_type="org.openmrs.Patient" target_uuid="75e04d42-3ca8-11e3-bf2b-0800271c1b75" creator="1" date_created="2024-01-27 15:48:00.0" name="org.openmrs.Patient/75e04d42-3ca8-11e3-bf2b-0800271c1b75" retired="false" uuid="a4119c8a-9804-495a-b557-49b89dcccf02" />

<concept concept_id="101" retired="false" datatype_id="3" class_id="11" is_set="false" creator="1" date_created="2024-01-27 15:48:00.0" version="" uuid="0abca361-f6bf-49cc-97de-b2f37f099123"/>
<concept_name concept_id="101" name="MedicationRequest" locale="en" creator="1" date_created="2024-01-27 15:48:00.0" concept_name_id="1" voided="false" uuid="5d2d4cb7-955b-4837-80f7-0ebb94092a01" concept_name_type="FULLY_SPECIFIED" locale_preferred="1"/>

<concept concept_id="102" retired="false" datatype_id="3" class_id="11" is_set="false" creator="1" date_created="2024-01-27 15:48:00.0" version="" uuid="0abca361-f6bf-49cc-97de-b2f37f099124"/>
<concept_name concept_id="102" name="EmergencyMedicationRequest" locale="en" creator="1" date_created="2024-01-27 15:48:00.0" concept_name_id="2" voided="false" uuid="5d2d4cb7-955b-4837-80f7-0ebb94092a02" concept_name_type="FULLY_SPECIFIED" locale_preferred="1"/>

<ipd_schedule schedule_id="1" uuid="23255323d-e887-4485-bc19-756cdbf00001" subject_reference_id="2" actor_reference_id="1" service_type_id="101" active="true" start_date="2024-01-27 15:48:00.0" creator="1" date_created="2024-01-27 15:48:00.0" voided="false" visit_id="1"/>

<concept concept_id="103" retired="false" datatype_id="4" class_id="11" is_set="false" creator="1" date_created="2024-01-27 15:48:00.0" version="" uuid="0abca361-f6bf-49cc-97de-b2f37f099125"/>
<medication_administration medication_administration_id="1" uuid="23255323d-e887-4485-bc19-756cdbf10101" status="103" creator="1" date_created="2024-01-27 20:00:00.0" voided="false"/>
<medication_administration medication_administration_id="2" uuid="23255323d-e887-4485-bc19-756cdbf10102" status="103" creator="1" date_created="2024-01-28 14:00:00.0" voided="false"/>

<ipd_slot slot_id="1" uuid="23255323d-e887-4485-bc19-756cdbf00101" service_type_id="101" schedule_id="1" status="COMPLETED" start_date_time="2024-01-27 20:00:00.0" creator="1" date_created="2024-01-27 15:48:00.0" voided="false" order_id="1" medication_administration_id="1"/>
<ipd_slot slot_id="2" uuid="23255323d-e887-4485-bc19-756cdbf00102" service_type_id="101" schedule_id="1" status="SCHEDULED" start_date_time="2024-01-28 08:00:00.0" creator="1" date_created="2024-01-27 15:48:00.0" voided="false" order_id="1"/>
<ipd_slot slot_id="3" uuid="23255323d-e887-4485-bc19-756cdbf00103" service_type_id="101" schedule_id="1" status="SCHEDULED" start_date_time="2024-01-28 20:00:00.0" creator="1" date_created="2024-01-27 15:48:00.0" voided="false" order_id="1"/>
<ipd_slot slot_id="4" uuid="23255323d-e887-4485-bc19-756cdbf00104" service_type_id="101" schedule_id="1" status="SCHEDULED" start_date_time="2024-01-29 08:00:00.0" creator="1" date_created="2024-01-27 15:48:00.0" voided="false" order_id="1"/>
<ipd_slot slot_id="5" uuid="23255323d-e887-4485-bc19-756cdbf00105" service_type_id="102" schedule_id="1" status="COMPLETED" start_date_time="2024-01-28 14:00:00.0" creator="1" date_created="2024-01-27 15:48:00.0" voided="false" medication_administration_id="2"/>

</dataset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.openmrs.module.ipd.contract;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.openmrs.module.ipd.model.PatientMedicationSummary;

import java.util.List;
import java.util.stream.Collectors;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PatientMedicationSummaryResponse {

private String patientUuid;
private List<PrescribedOrderSlotSummaryResponse> prescribedOrderSlots;
private List<MedicationSlotResponse> emergencyMedicationSlots;

public static PatientMedicationSummaryResponse createFrom(PatientMedicationSummary patientMedicationSummary) {
List<PrescribedOrderSlotSummaryResponse> prescribedOrderSlots = patientMedicationSummary.getPrescribedOrderSlots() != null
? patientMedicationSummary.getPrescribedOrderSlots().stream().map(PrescribedOrderSlotSummaryResponse::createFrom).collect(Collectors.toList())
: null;
List<MedicationSlotResponse> emergencyMedicationSlots = patientMedicationSummary.getEmergencyMedicationSlots() != null
? patientMedicationSummary.getEmergencyMedicationSlots().stream().map(MedicationSlotResponse::createFrom).collect(Collectors.toList())
: null;
return PatientMedicationSummaryResponse.builder()
.patientUuid(patientMedicationSummary.getPatientUuid())
.prescribedOrderSlots(prescribedOrderSlots)
.emergencyMedicationSlots(emergencyMedicationSlots)
.build();
}
}
Loading

0 comments on commit 482d0fa

Please sign in to comment.