Skip to content

Commit

Permalink
O3-3193: REST API for EMR API ADT functionality
Browse files Browse the repository at this point in the history
O3-3467: REST API - Create EmrApiConfig endpoint
  • Loading branch information
mogoodrich committed Jun 21, 2024
1 parent 002dc30 commit 4bcea85
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,8 @@ VisitDomainWrapper createRetrospectiveVisit(Patient patient, Location location,

/**
* Returns all patient awaiting transfer
* @param location - if non-null, only checks awaiting transfers at the visit location associated with this location TODO do we need this?
* @param transferLocation - if non-null, only returns matches for patients awaiting transfer to this location
* @param transferLocation - if non-null, only return matches for patients awaiting transfer to this location
* @return List<Visit> of the matching visits<
*/
List<Visit> getVisitsAwaitingTransfer(Location location, Location transferLocation);
List<Visit> getVisitsAwaitingTransfer(Location transferLocation);
}
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ public List<Visit> getVisitsAwaitingAdmission(Location location, Collection<Inte

@Override
@Transactional(readOnly = true)
public List<Visit> getVisitsAwaitingTransfer(Location location, Location transferLocation) {
public List<Visit> getVisitsAwaitingTransfer(Location transferLocation) {
// TODO implement!
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
import static java.util.Collections.EMPTY_LIST;
import static java.util.Collections.reverseOrder;
import static java.util.Collections.sort;
import static org.apache.commons.collections.CollectionUtils.filter;
import static org.apache.commons.collections.CollectionUtils.find;
import static org.apache.commons.collections.CollectionUtils.select;

Expand Down Expand Up @@ -535,15 +534,20 @@ public Location getInpatientLocation(Date onDate) {
return null;
}

public Integer timeSinceAdmissionInMinutes() {
if (getAdmissionEncounter() == null) {
public Integer getTimeSinceAdmissionInMinutes() {
if (!isAdmitted() || getAdmissionEncounter() == null) {
return null;
} else {
return Minutes.minutesBetween(new DateTime(getAdmissionEncounter().getEncounterDatetime()), new DateTime()).getMinutes();
}
}

public Integer timeAtLocationInMinutes() {
public Integer getTimeAtCurrentInpatientLocationInMinutes() {

if (!isAdmitted()) {
return null;
}

EncounterType admissionEncounterType = emrApiProperties.getAdmissionEncounterType();
EncounterType transferEncounterType = emrApiProperties.getTransferWithinHospitalEncounterType();

Expand All @@ -552,13 +556,13 @@ public Integer timeAtLocationInMinutes() {

for (Encounter encounter : getSortedEncounters(SortOrder.MOST_RECENT_FIRST)) {
if (encounter.getEncounterType().equals(admissionEncounterType) || encounter.getEncounterType().equals(transferEncounterType)) {
time = Minutes.minutesBetween(new DateTime(encounter.getEncounterDatetime()), new DateTime()).getMinutes();
if (ward == null) {
ward = encounter.getLocation();
}
else if (!ward.equals(encounter.getLocation())) {
break;
}
time = Minutes.minutesBetween(new DateTime(encounter.getEncounterDatetime()), new DateTime()).getMinutes();
}
}
return time;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,154 @@ public void shouldNotFailIfStopDatetimeNull() {
assertNull(visitDomainWrapper.getStopDate());
}

@Test
public void timeSinceAdmissionInMinutes_shouldReturnTimeSinceAdmissionInMinutes() {

EncounterType admitEncounterType = new EncounterType();
EncounterType transferEncounterType = new EncounterType();

Location icu = new Location();

when(visit.getStartDatetime()).thenReturn(DateUtils.addHours(new Date(), -5));

EmrApiProperties props = mock(EmrApiProperties.class);
when(props.getAdmissionEncounterType()).thenReturn(admitEncounterType);
when(props.getTransferWithinHospitalEncounterType()).thenReturn(transferEncounterType);
visitDomainWrapper.setEmrApiProperties(props);

Encounter admit = new Encounter();
admit.setEncounterType(admitEncounterType);
admit.setEncounterDatetime(DateUtils.addHours(new Date(), -3));
admit.setLocation(icu);

Encounter transfer = new Encounter();
transfer.setEncounterType(transferEncounterType);
transfer.setEncounterDatetime(DateUtils.addHours(new Date(), -2));
transfer.setLocation(icu);

Set<Encounter> encounters = new LinkedHashSet<Encounter>();
encounters.add(admit);
encounters.add(transfer);
when(visit.getEncounters()).thenReturn(encounters);

assertThat(visitDomainWrapper.getTimeSinceAdmissionInMinutes(), is(180));
}

@Test
public void timeSinceAdmissionInMinutes_shouldReturnNullIfPatientNeverAdmitted() {
when(visit.getStartDatetime()).thenReturn(DateUtils.addHours(new Date(), -5));
assertNull(visitDomainWrapper.getTimeSinceAdmissionInMinutes());
}

@Test
public void timeSinceAdmissionInMinutes_shouldReturnNullIfPatientNoLongerAdmitted() {

EncounterType admitEncounterType = new EncounterType();
EncounterType exitFromInpatientEncounterType = new EncounterType();

Location icu = new Location();

when(visit.getStartDatetime()).thenReturn(DateUtils.addHours(new Date(), -5));

EmrApiProperties props = mock(EmrApiProperties.class);
when(props.getAdmissionEncounterType()).thenReturn(admitEncounterType);
when(props.getExitFromInpatientEncounterType()).thenReturn(exitFromInpatientEncounterType);
visitDomainWrapper.setEmrApiProperties(props);

Encounter admit = new Encounter();
admit.setEncounterType(admitEncounterType);
admit.setEncounterDatetime(DateUtils.addHours(new Date(), -3));
admit.setLocation(icu);

Encounter discharge = new Encounter();
discharge.setEncounterType(exitFromInpatientEncounterType);
discharge.setEncounterDatetime(DateUtils.addHours(new Date(), -2));
discharge.setLocation(icu);

Set<Encounter> encounters = new LinkedHashSet<Encounter>();
encounters.add(admit);
encounters.add(discharge);
when(visit.getEncounters()).thenReturn(encounters);

assertNull(visitDomainWrapper.getTimeSinceAdmissionInMinutes());
}

@Test
public void timeAtCurrentInpatientLocationInMinutes_shouldReturnTimeAtCurrentInpatientLocationInMinutesEvenWhenMostRecentAdtEncounterDoesNotResultInLocationChange() {

EncounterType admitEncounterType = new EncounterType();
EncounterType transferEncounterType = new EncounterType();

Location icu = new Location();

when(visit.getStartDatetime()).thenReturn(DateUtils.addHours(new Date(), -5));

EmrApiProperties props = mock(EmrApiProperties.class);
when(props.getAdmissionEncounterType()).thenReturn(admitEncounterType);
when(props.getTransferWithinHospitalEncounterType()).thenReturn(transferEncounterType);
visitDomainWrapper.setEmrApiProperties(props);

Encounter admit = new Encounter();
admit.setEncounterType(admitEncounterType);
admit.setEncounterDatetime(DateUtils.addHours(new Date(), -3));
admit.setLocation(icu);

// not sure if this is a real use case, transfer to same location, but just in case
Encounter transfer = new Encounter();
transfer.setEncounterType(transferEncounterType);
transfer.setEncounterDatetime(DateUtils.addHours(new Date(), -2));
transfer.setLocation(icu);

Set<Encounter> encounters = new LinkedHashSet<Encounter>();
encounters.add(admit);
encounters.add(transfer);
when(visit.getEncounters()).thenReturn(encounters);

assertThat(visitDomainWrapper.getTimeAtCurrentInpatientLocationInMinutes(), is(180));
}



@Test
public void timeAtCurrentInpatientLocationInMinutes_shouldReturnNullIfPatientNeverAdmitted() {
when(visit.getStartDatetime()).thenReturn(DateUtils.addHours(new Date(), -5));
assertNull(visitDomainWrapper.getTimeAtCurrentInpatientLocationInMinutes());
}

@Test
public void timeAtCurrentInpatientLocationInMinutes_shouldReturnNullIfPatientNoLongerAdmitted() {

EncounterType admitEncounterType = new EncounterType();
EncounterType exitFromInpatientEncounterType = new EncounterType();

Location icu = new Location();

when(visit.getStartDatetime()).thenReturn(DateUtils.addHours(new Date(), -5));

EmrApiProperties props = mock(EmrApiProperties.class);
when(props.getAdmissionEncounterType()).thenReturn(admitEncounterType);
when(props.getExitFromInpatientEncounterType()).thenReturn(exitFromInpatientEncounterType);
visitDomainWrapper.setEmrApiProperties(props);

Encounter admit = new Encounter();
admit.setEncounterType(admitEncounterType);
admit.setEncounterDatetime(DateUtils.addHours(new Date(), -3));
admit.setLocation(icu);

Encounter discharge = new Encounter();
discharge.setEncounterType(exitFromInpatientEncounterType);
discharge.setEncounterDatetime(DateUtils.addHours(new Date(), -2));
discharge.setLocation(icu);

Set<Encounter> encounters = new LinkedHashSet<Encounter>();
encounters.add(admit);
encounters.add(discharge);
when(visit.getEncounters()).thenReturn(encounters);

assertNull(visitDomainWrapper.getTimeAtCurrentInpatientLocationInMinutes());
}


private class ExpectedDiagnosis extends ArgumentMatcher<Diagnosis> {

private Diagnosis expectedDiagnosis;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.openmrs.module.emrapi.web.controller;

import org.openmrs.module.emrapi.EmrApiProperties;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "/rest/emrapi/configuration")
public class EmrApiConfigController {

@Autowired
private EmrApiProperties emrApiProperties;
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public SimpleObject getEmrApiConfig() {
SimpleObject response = new SimpleObject();
response.put("admissionEncounterType", emrApiProperties.getAdmissionEncounterType().getUuid());
response.put("transferWithinHospitalEncounterType", emrApiProperties.getTransferWithinHospitalEncounterType().getUuid());
response.put("exitFromInpatientEncounterTpye", emrApiProperties.getExitFromInpatientEncounterType().getUuid());
return response;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,24 @@ public class InpatientVisitsController {
@ResponseBody
public List<SimpleObject> getInpatientVisits(@RequestParam(value = "currentLocation") Location currentLocation) {

// TODO expand to allow null current location
if (currentLocation == null) {
throw new IllegalArgumentException("currentLocation is required");
}

// TODO handle null response if possible
// TODO this getInpatentVisits method is almost certainly not performant enough for production use and will likely need to be refactored into a HQL query
List<VisitDomainWrapper> visits = adtService.getInpatientVisits(adtService.getLocationThatSupportsVisits(currentLocation), currentLocation);

// TODO type this?
List<SimpleObject> response = new ArrayList<SimpleObject>();

if (visits == null) {
return response;
}

for (VisitDomainWrapper visit : visits) {
SimpleObject inpatientVisit = new SimpleObject();
inpatientVisit.put("visit", ConversionUtil.convertToRepresentation(visit.getVisit(), Representation.DEFAULT));
inpatientVisit.put("patient", ConversionUtil.convertToRepresentation(visit.getVisit().getPatient(), Representation.DEFAULT));
inpatientVisit.put("currentLocation", ConversionUtil.convertToRepresentation(currentLocation, Representation.DEFAULT));
inpatientVisit.put("timeSinceAdmissionInMinutes", visit.timeSinceAdmissionInMinutes());
inpatientVisit.put("timeAtLocationdInMinutes", visit.timeAtLocationInMinutes());
inpatientVisit.put("timeSinceAdmissionInMinutes", visit.getTimeSinceAdmissionInMinutes());
inpatientVisit.put("timeAtInpatientLocationdInMinutes", visit.getTimeAtCurrentInpatientLocationInMinutes());
response.add(inpatientVisit);
}

Expand All @@ -71,10 +73,8 @@ public List<SimpleObject> getVisitsAwaitingAdminstOrTransfer(@RequestParam("loca
return response;
}


private List<SimpleObject> getVisitsAwaitingAdmissionHelper(Location admissionLocation) {
// TODO note that this service method *only* returns admission requests, while we will need to expand this to include transfer requests (which will be slightly non-trivial)
// TODO note also that this service method does *not* actually limit by admission location; we will need to expand the underlying service method/hql query to do this
// TODO note also that this service method does *not* actually limit by admission location; we will need to expand the underlying service method/hql query to do this, see: https://openmrs.atlassian.net/browse/O3-3464
List<Visit> visits = adtService.getVisitsAwaitingAdmission(admissionLocation, null, null);
List<SimpleObject> visitObjects = new ArrayList<SimpleObject>();
for (Visit visit : visits) {
Expand All @@ -88,7 +88,7 @@ private List<SimpleObject> getVisitsAwaitingAdmissionHelper(Location admissionLo
}

private List<SimpleObject> getVisitsAwaitingTransferHelper(Location transferLocation) {
List<Visit> visits = adtService.getVisitsAwaitingTransfer(null, transferLocation);
List<Visit> visits = adtService.getVisitsAwaitingTransfer(transferLocation);
List<SimpleObject> visitObjects = new ArrayList<SimpleObject>();
for (Visit visit : visits) {
SimpleObject inpatientVisit = new SimpleObject();
Expand Down

0 comments on commit 4bcea85

Please sign in to comment.