Skip to content

Commit

Permalink
EA-188 - Improve Java and Rest API for retrieving inpatient admission…
Browse files Browse the repository at this point in the history
… requests (#233)
  • Loading branch information
mseaton authored Jul 12, 2024
1 parent 96d8860 commit 49513d5
Show file tree
Hide file tree
Showing 16 changed files with 816 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,6 @@ public void shouldFindVisitEvenIfPatientHasMoreRecentVisitNoteWithoutAdmissionDi
.encounterType(emrApiProperties.getVisitNoteEncounterType())
.visit(visit)
.save();
testDataManager.obs()
.person(patient)
.encounter(secondVisitNoteEncounter)
.concept(dispositionDescriptor.getDispositionConcept())
.value(emrConceptService.getConcept("org.openmrs.module.emrapi:Death"))
.save();

VisitQueryResult result = visitQueryService.evaluate(query, null);
assertThat(result.getMemberIds().size(), is(1));
Expand Down
10 changes: 5 additions & 5 deletions api/src/main/java/org/openmrs/module/emrapi/adt/AdtService.java
Original file line number Diff line number Diff line change
Expand Up @@ -344,13 +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
@Deprecated
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<
* Returns all List of InpatientRequest that match the given search criteria
* @param criteria - represents the criteria by which inpatient requests are searched and returned
* @return List<InpatientRequest> of the matching InpatientRequests that match the criteria
*/
List<Visit> getVisitsAwaitingTransfer(Location transferLocation);
List<InpatientRequest> getInpatientRequests(InpatientRequestSearchCriteria criteria);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.apache.commons.lang.time.DateUtils;
import org.joda.time.DateTime;
import org.openmrs.Concept;
import org.openmrs.Encounter;
import org.openmrs.EncounterRole;
import org.openmrs.EncounterType;
Expand Down Expand Up @@ -45,7 +46,9 @@
import org.openmrs.module.emrapi.concept.EmrConceptService;
import org.openmrs.module.emrapi.db.EmrApiDAO;
import org.openmrs.module.emrapi.disposition.Disposition;
import org.openmrs.module.emrapi.disposition.DispositionDescriptor;
import org.openmrs.module.emrapi.disposition.DispositionService;
import org.openmrs.module.emrapi.disposition.DispositionType;
import org.openmrs.module.emrapi.domainwrapper.DomainWrapperFactory;
import org.openmrs.module.emrapi.merge.PatientMergeAction;
import org.openmrs.module.emrapi.merge.VisitMergeAction;
Expand All @@ -58,6 +61,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -920,8 +924,90 @@ public List<Visit> getVisitsAwaitingAdmission(Location location, Collection<Inte

@Override
@Transactional(readOnly = true)
public List<Visit> getVisitsAwaitingTransfer(Location transferLocation) {
// TODO implement!
return Collections.emptyList();
public List<InpatientRequest> getInpatientRequests(InpatientRequestSearchCriteria criteria) {

DispositionDescriptor descriptor = dispositionService.getDispositionDescriptor();

// Determine whether to filter visits at a particular location
Location visitLocation = null ;
if (criteria.getVisitLocation() != null ) {
visitLocation = getLocationThatSupportsVisits(criteria.getVisitLocation());
}

// Determine what type of dispositions to include. If none specified, default to all
List<DispositionType> dispositionTypes = criteria.getDispositionTypes();
if (dispositionTypes == null) {
dispositionTypes = Arrays.asList(DispositionType.values());
}

// Get all disposition concepts based on the given disposition type(s)
Map<Concept, DispositionType> dispositionValuesToType = new HashMap<>();
List<Disposition> dispositions = dispositionService.getDispositions();
if (dispositions != null) {
for (Disposition d : dispositions) {
if (dispositionTypes.contains(d.getType())) {
dispositionValuesToType.put(emrConceptService.getConcept(d.getConceptCode()), d.getType());
}
}
}

// Get all encounter types that might cause a request to be fulfilled
List<EncounterType> adtEncounterTypes = new ArrayList<>();
if (dispositionTypes.contains(DispositionType.ADMIT)) {
adtEncounterTypes.add(emrApiProperties.getAdmissionEncounterType());
}
if (dispositionTypes.contains(DispositionType.TRANSFER)) {
adtEncounterTypes.add(emrApiProperties.getTransferWithinHospitalEncounterType());
}
if (dispositionTypes.contains(DispositionType.DISCHARGE)) {
adtEncounterTypes.add(emrApiProperties.getExitFromInpatientEncounterType());
}

// Disposition Locations are stored as Obs where the valueText is the location id. Collect these values
List<String> dispositionLocationIds = null;
if (criteria.getDispositionLocations() != null) {
dispositionLocationIds = new ArrayList<>();
for (Location l : criteria.getDispositionLocations()) {
dispositionLocationIds.add(l.getLocationId().toString());
}
}

Map<String, Object> parameters = new HashMap<>();
parameters.put("dispositionConcept", descriptor.getDispositionConcept());
parameters.put("dispositionValues", dispositionValuesToType.keySet());
parameters.put("visitLocation", visitLocation);
parameters.put("adtEncounterTypes", adtEncounterTypes);
parameters.put("adtDecisionConcept", emrApiProperties.getAdmissionDecisionConcept());
parameters.put("denyConcept", emrApiProperties.getDenyAdmissionConcept());
parameters.put("dispositionLocationIds", dispositionLocationIds);
parameters.put("limitByDispositionLocation", dispositionLocationIds != null);
parameters.put("admitLocationConcept", descriptor.getAdmissionLocationConcept());
parameters.put("transferLocationConcept", descriptor.getInternalTransferLocationConcept());
parameters.put("patientIds", criteria.getPatientIds());
parameters.put("limitByPatient", criteria.getPatientIds() != null);
parameters.put("visitIds", criteria.getVisitIds());
parameters.put("limitByVisit", criteria.getVisitIds() != null);

List<?> reqs = emrApiDAO.executeHqlFromResource("hql/inpatient_request_dispositions.hql", parameters, List.class);
List<InpatientRequest> ret = new ArrayList<>();
for (Object req : reqs) {
Object[] o = (Object[]) req;
InpatientRequest r = new InpatientRequest();
r.setVisit((Visit)o[0]);
r.setPatient((Patient)o[1]);
r.setDispositionEncounter((Encounter)o[2]);
r.setDispositionObsGroup((Obs)o[3]);
Obs dispositionObs = (Obs)o[4];
if (dispositionObs != null) {
r.setDisposition(dispositionObs.getValueCoded());
r.setDispositionType(dispositionValuesToType.get(dispositionObs.getValueCoded()));
}
Obs locationObs = (Obs)(o[5] != null ? o[5] : o[6]);
if (locationObs != null) {
r.setDispositionLocation(locationService.getLocation(Integer.parseInt(locationObs.getValueText())));
}
ret.add(r);
}
return ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.openmrs.module.emrapi.adt;

import lombok.Data;
import org.openmrs.Concept;
import org.openmrs.Encounter;
import org.openmrs.Location;
import org.openmrs.Obs;
import org.openmrs.Patient;
import org.openmrs.Visit;
import org.openmrs.module.emrapi.disposition.DispositionType;

import java.util.Date;

/**
* Represents and Admission, Discharge, or Transfer request
*/
@Data
public class InpatientRequest {
private Visit visit;
private Patient patient;
private DispositionType dispositionType;
private Encounter dispositionEncounter;
private Obs dispositionObsGroup;
private Concept disposition;
private Location dispositionLocation;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.openmrs.module.emrapi.adt;

import lombok.Data;
import org.openmrs.Location;
import org.openmrs.module.emrapi.disposition.DispositionType;

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

/**
* Represents criteria for searching for AdtRequests
* Currently the assumption is that all requests returned are active, and this will be the default regardless
*/
@Data
public class InpatientRequestSearchCriteria {

private Location visitLocation;
private List<Location> dispositionLocations;
private List<DispositionType> dispositionTypes;
private List<Integer> patientIds;
private List<Integer> visitIds;

public void addDispositionLocation(Location location) {
if (dispositionLocations == null) {
dispositionLocations = new ArrayList<>();
}
dispositionLocations.add(location);
}

public void addDispositionType(DispositionType dispositionType) {
if (dispositionTypes == null) {
dispositionTypes = new ArrayList<>();
}
dispositionTypes.add(dispositionType);
}

public void addPatientId(Integer patientId) {
if (patientIds == null) {
patientIds = new ArrayList<>();
}
patientIds.add(patientId);
}

public void addVisitId(Integer visitId) {
if (visitIds == null) {
visitIds = new ArrayList<>();
}
visitIds.add(visitId);
}
}
63 changes: 63 additions & 0 deletions api/src/main/resources/hql/inpatient_request_dispositions.hql
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
select
visit,
dispoEncounter.patient,
dispoEncounter,
dispo.obsGroup,
dispo,
(select o from Obs o where o.obsGroup = dispo.obsGroup and o.voided = 0 and o.concept = :admitLocationConcept) as admitLocation,
(select o from Obs o where o.obsGroup = dispo.obsGroup and o.voided = 0 and o.concept = :transferLocationConcept) as transferLocation
from
Obs as dispo
inner join dispo.encounter as dispoEncounter
inner join dispoEncounter.visit as visit
inner join dispo.person as person
where
dispo.voided = false
and dispoEncounter.voided = false
and visit.voided = false
and dispo.concept = :dispositionConcept
and dispo.valueCoded in :dispositionValues
and (:visitLocation is null or visit.location = :visitLocation)
and person.dead = false
and visit.stopDatetime is null
and (
select count(*)
from Obs as laterDispoObs
where laterDispoObs.encounter.visit = visit
and laterDispoObs.voided = false
and laterDispoObs.concept = :dispositionConcept
and (
laterDispoObs.obsDatetime > dispo.obsDatetime or
(laterDispoObs.obsDatetime = dispo.obsDatetime and laterDispoObs.obsId > dispo.obsId)
)
) = 0
and (
select count(*)
from Encounter as adtEncounter
where adtEncounter.visit = visit
and adtEncounter.voided = false
and adtEncounter.encounterType in (:adtEncounterTypes)
and adtEncounter.encounterDatetime >= dispo.obsDatetime
) = 0
and (
select count(*)
from Obs as adtDecision
inner join adtDecision.encounter as encounterInVisit
where encounterInVisit.visit = visit
and encounterInVisit.voided = false
and adtDecision.voided = false
and adtDecision.concept = :adtDecisionConcept
and adtDecision.valueCoded = :denyConcept
and encounterInVisit.encounterDatetime > dispoEncounter.encounterDatetime
) = 0
and (
:limitByDispositionLocation = false or (
select count(*)
from Obs as locationObs
where locationObs.obsGroup = dispo.obsGroup
and locationObs.valueText in (:dispositionLocationIds)
) > 0
)
and (:limitByPatient is false or dispoEncounter.patient.patientId in (:patientIds))
and (:limitByVisit is false or visit.visitId in (:visitIds))
order by dispo.obsId
12 changes: 12 additions & 0 deletions api/src/main/resources/hql/visits_awaiting_admission.hql
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@ where
and (:visitIds is null or visit.visitId in :visitIds)
and person.dead = false
and visit.stopDatetime is null
and (
select count(*)
from Obs as laterDispoObs
where laterDispoObs.encounter.visit = visit
and laterDispoObs.voided = false
and laterDispoObs.concept = :dispositionConcept
and (
laterDispoObs.obsDatetime > dispo.obsDatetime or
(laterDispoObs.obsDatetime = dispo.obsDatetime and laterDispoObs.obsId > dispo.obsId)
)
) = 0
and (
select count(*)
from Encounter as admission
where admission.visit = visit
and admission.voided = false
and admission.encounterType = :admissionEncounterType
and admission.encounterDatetime >= dispo.obsDatetime
) = 0
and (
select count(*)
Expand Down
Loading

0 comments on commit 49513d5

Please sign in to comment.