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

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 96d8860 commit 49513d5
Showing 16 changed files with 816 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -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));
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
@@ -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
@@ -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;
@@ -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;
@@ -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;
@@ -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
@@ -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(*)
Loading

0 comments on commit 49513d5

Please sign in to comment.