Skip to content

Commit

Permalink
EA-193: REST API - create first pass at REST endpoints for ADT
Browse files Browse the repository at this point in the history
  • Loading branch information
mogoodrich authored Jun 26, 2024
1 parent 346b207 commit 0754882
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -344,5 +344,13 @@ VisitDomainWrapper createRetrospectiveVisit(Patient patient, Location location,
* @param visitIds - if non-null, only returns matches for visits with the given ids
* @return List<Visit></Visit> of the matching visits
*/
// TODO expand this to take in an admissionLocation parameter and limit to admissions at that location
List<Visit> getVisitsAwaitingAdmission(Location location, Collection<Integer> patientIds, Collection<Integer> visitIds);

/**
* Returns all patient awaiting transfer
* @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 transferLocation);
}
Original file line number Diff line number Diff line change
Expand Up @@ -917,4 +917,11 @@ public List<Visit> getVisitsAwaitingAdmission(Location location, Collection<Inte
parameters.put("visitIds", visitIds);
return emrApiDAO.executeHqlFromResource("hql/visits_awaiting_admission.hql", parameters, Visit.class);
}

@Override
@Transactional(readOnly = true)
public List<Visit> getVisitsAwaitingTransfer(Location transferLocation) {
// TODO implement!
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import org.openmrs.Encounter;
import org.openmrs.EncounterType;
import org.openmrs.Location;
Expand Down Expand Up @@ -532,6 +534,40 @@ public Location getInpatientLocation(Date onDate) {
return null;
}

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

public Integer getTimeAtCurrentInpatientLocationInMinutes() {

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

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

Location ward = null;
Integer time = null;

for (Encounter encounter : getSortedEncounters(SortOrder.MOST_RECENT_FIRST)) {
if (encounter.getEncounterType().equals(admissionEncounterType) || encounter.getEncounterType().equals(transferEncounterType)) {
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;
}

public Date getStartDatetime() {
return visit.getStartDatetime();
}
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
@@ -0,0 +1,106 @@
package org.openmrs.module.emrapi.web.controller;

import java.util.ArrayList;
import java.util.List;

import org.openmrs.Location;
import org.openmrs.Visit;
import org.openmrs.module.emrapi.adt.AdtAction;
import org.openmrs.module.emrapi.adt.AdtService;
import org.openmrs.module.emrapi.visit.VisitDomainWrapper;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.representation.Representation;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "/rest/emrapi/inpatient")
public class InpatientVisitsController {

@Autowired
private AdtService adtService;

@RequestMapping(method = RequestMethod.GET, value = "/visits")
@ResponseBody
public List<SimpleObject> getInpatientVisits(@RequestParam(value = "currentLocation") Location currentLocation) {

if (currentLocation == null) {
throw new IllegalArgumentException("currentLocation is required");
}

List<VisitDomainWrapper> visits = adtService.getInpatientVisits(adtService.getLocationThatSupportsVisits(currentLocation), currentLocation);
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.getTimeSinceAdmissionInMinutes());
inpatientVisit.put("timeAtInpatientLocationdInMinutes", visit.getTimeAtCurrentInpatientLocationInMinutes());

This comment has been minimized.

Copy link
@chibongho

chibongho Jun 27, 2024

Contributor

@mseaton typo timeAtInpatientLocationdInMinutes

response.add(inpatientVisit);
}

return response;
}

@RequestMapping(method = RequestMethod.GET, value = "/admissionRequests")
@ResponseBody
public List<SimpleObject> getVisitsAwaitingAdmission(@RequestParam("admissionLocation") Location admissionLocation) {
return getVisitsAwaitingAdmissionHelper(admissionLocation);
}

@RequestMapping(method = RequestMethod.GET, value = "/transferRequests")
@ResponseBody
public List<SimpleObject> getVisitsAwaitingTransfer(@RequestParam("transferLocation") Location transferLocation) {
return getVisitsAwaitingTransferHelper(transferLocation);
}

@RequestMapping(method = RequestMethod.GET, value = "/admissionAndTransferRequests")
@ResponseBody
public List<SimpleObject> getVisitsAwaitingAdminstOrTransfer(@RequestParam("location") Location location) {
List<SimpleObject> response = getVisitsAwaitingAdmissionHelper(location);
response.addAll(getVisitsAwaitingTransferHelper(location));
return response;
}

private List<SimpleObject> getVisitsAwaitingAdmissionHelper(Location admissionLocation) {
// 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) {
SimpleObject inpatientVisit = new SimpleObject();
inpatientVisit.put("visit", ConversionUtil.convertToRepresentation(visit, Representation.DEFAULT));
inpatientVisit.put("patient", ConversionUtil.convertToRepresentation(visit.getPatient(), Representation.DEFAULT));
inpatientVisit.put("type", AdtAction.Type.ADMISSION);
visitObjects.add(inpatientVisit);
}
return visitObjects;
}

private List<SimpleObject> getVisitsAwaitingTransferHelper(Location transferLocation) {
List<Visit> visits = adtService.getVisitsAwaitingTransfer(transferLocation);
List<SimpleObject> visitObjects = new ArrayList<SimpleObject>();
for (Visit visit : visits) {
SimpleObject inpatientVisit = new SimpleObject();
inpatientVisit.put("visit", ConversionUtil.convertToRepresentation(visit, Representation.DEFAULT));
inpatientVisit.put("patient", ConversionUtil.convertToRepresentation(visit.getPatient(), Representation.DEFAULT));
inpatientVisit.put("type", AdtAction.Type.TRANSFER);
visitObjects.add(inpatientVisit);
}
return visitObjects;
}

}



0 comments on commit 0754882

Please sign in to comment.