Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

import javax.validation.constraints.NotNull;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.openmrs.Location;
Expand Down Expand Up @@ -129,6 +131,20 @@ public interface QueueEntryService {
@Authorized({ PrivilegeConstants.GET_QUEUE_ENTRIES })
List<QueueEntry> getQueueEntries(@NotNull QueueEntrySearchCriteria searchCriteria);

/**
* Paginated variant of {@link #getQueueEntries(QueueEntrySearchCriteria)}. Nulls disable the
* corresponding pagination bound.
*/
@Authorized({ PrivilegeConstants.GET_QUEUE_ENTRIES })
List<QueueEntry> getQueueEntries(@NotNull QueueEntrySearchCriteria searchCriteria, Integer startIndex, Integer limit);

/**
* Batch-resolves the uuid of the previous queue entry for each input entry that has a non-null
* {@code queueComingFrom}. Entries without a resolvable predecessor are absent from the result.
*/
@Authorized({ PrivilegeConstants.GET_QUEUE_ENTRIES })
Map<QueueEntry, String> getPreviousQueueEntryUuids(Collection<QueueEntry> entries);
Comment thread
UjjawalPrabhat marked this conversation as resolved.

/**
* @return {@link Long} count of queue entries that match the given
* %{@link QueueEntrySearchCriteria}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

import javax.validation.constraints.NotNull;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.openmrs.module.queue.api.search.QueueEntrySearchCriteria;
import org.openmrs.module.queue.model.QueueEntry;
Expand All @@ -24,6 +26,20 @@ public interface QueueEntryDao extends BaseQueueDao<QueueEntry> {
*/
List<QueueEntry> getQueueEntries(@NotNull QueueEntrySearchCriteria searchCriteria);

/**
* Paginated variant of {@link #getQueueEntries(QueueEntrySearchCriteria)}. Nulls disable the
* corresponding pagination bound.
*/
List<QueueEntry> getQueueEntries(@NotNull QueueEntrySearchCriteria searchCriteria, Integer startIndex, Integer limit);

/**
* Batch-resolves the uuid of the previous queue entry for each of the given entries (i.e. the entry
* the patient was transitioned from, identified by matching patient + visit, queue =
* input.queueComingFrom, endedAt = input.startedAt). Entries with no {@code queueComingFrom} are
* absent from the returned map.
*/
Map<QueueEntry, String> getPreviousQueueEntryUuids(Collection<QueueEntry> entries);

/**
* @return {@link Long} of the number of queue entries that match the given
* %{@link QueueEntrySearchCriteria}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Session;
Expand All @@ -26,6 +32,7 @@
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.openmrs.Patient;
import org.openmrs.Visit;
import org.openmrs.module.queue.api.dao.QueueEntryDao;
import org.openmrs.module.queue.api.search.QueueEntrySearchCriteria;
import org.openmrs.module.queue.model.Queue;
Expand All @@ -41,14 +48,87 @@ public QueueEntryDaoImpl(@Qualifier("sessionFactory") SessionFactory sessionFact

@Override
public List<QueueEntry> getQueueEntries(QueueEntrySearchCriteria searchCriteria) {
return getQueueEntries(searchCriteria, null, null);
}

@Override
public List<QueueEntry> getQueueEntries(QueueEntrySearchCriteria searchCriteria, Integer startIndex, Integer limit) {
Criteria c = createCriteriaFromSearchCriteria(searchCriteria);
c.addOrder(Order.desc("qe.sortWeight"));
c.addOrder(Order.asc("qe.startedAt"));
c.addOrder(Order.asc("qe.dateCreated"));
c.addOrder(Order.asc("qe.queueEntryId"));
if (startIndex != null) {
c.setFirstResult(startIndex);
}
if (limit != null) {
c.setMaxResults(limit);
}
return c.list();
}

@Override
public Map<QueueEntry, String> getPreviousQueueEntryUuids(Collection<QueueEntry> entries) {
if (entries == null || entries.isEmpty()) {
return Collections.emptyMap();
}

List<QueueEntry> candidates = new ArrayList<>();
Set<Queue> queuesComingFrom = new HashSet<>();
Set<Patient> patients = new HashSet<>();
Set<Visit> visits = new HashSet<>();
Set<Date> startedAts = new HashSet<>();
for (QueueEntry e : entries) {
if (e.getQueueComingFrom() == null || e.getPatient() == null || e.getStartedAt() == null) {
continue;
}
candidates.add(e);
queuesComingFrom.add(e.getQueueComingFrom());
patients.add(e.getPatient());
startedAts.add(e.getStartedAt());
if (e.getVisit() != null) {
visits.add(e.getVisit());
}
}
if (candidates.isEmpty()) {
return Collections.emptyMap();
}

// Filter via IN clauses; post-match the composite tuple in Java to avoid an OR-AND explosion.
StringBuilder hql = new StringBuilder();
hql.append("FROM QueueEntry prev WHERE prev.voided = false ");
hql.append("AND prev.endedAt IN (:startedAts) ");
hql.append("AND prev.patient IN (:patients) ");
hql.append("AND prev.queue IN (:queuesComingFrom)");
if (!visits.isEmpty()) {
hql.append(" AND (prev.visit IS NULL OR prev.visit IN (:visits))");
}

javax.persistence.Query query = getCurrentSession().createQuery(hql.toString());
query.setParameter("startedAts", startedAts);
query.setParameter("patients", patients);
query.setParameter("queuesComingFrom", queuesComingFrom);
if (!visits.isEmpty()) {
query.setParameter("visits", visits);
}
List<QueueEntry> candidatesFromDb = query.getResultList();

Map<PrevKey, QueueEntry> index = new HashMap<>();
for (QueueEntry prev : candidatesFromDb) {
index.put(new PrevKey(prev.getPatient(), prev.getVisit(), prev.getQueue(), prev.getEndedAt()), prev);
}

Map<QueueEntry, String> result = new LinkedHashMap<>();
for (QueueEntry curr : candidates) {
QueueEntry prev = index
.get(new PrevKey(curr.getPatient(), curr.getVisit(), curr.getQueueComingFrom(), curr.getStartedAt()));
if (prev != null) {
result.put(curr, prev.getUuid());
}
}
return result;
}

@Override
public Long getCountOfQueueEntries(QueueEntrySearchCriteria searchCriteria) {
Criteria criteria = createCriteriaFromSearchCriteria(searchCriteria);
Expand Down Expand Up @@ -176,4 +256,40 @@ private Criteria createCriteriaFromSearchCriteria(QueueEntrySearchCriteria searc
}
return c;
}

private static final class PrevKey {

private final Patient patient;

private final Visit visit;

private final Queue queue;

private final Date endedAt;

PrevKey(Patient patient, Visit visit, Queue queue, Date endedAt) {
this.patient = patient;
this.visit = visit;
this.queue = queue;
this.endedAt = endedAt;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PrevKey)) {
return false;
}
PrevKey k = (PrevKey) o;
return java.util.Objects.equals(patient, k.patient) && java.util.Objects.equals(visit, k.visit)
&& java.util.Objects.equals(queue, k.queue) && java.util.Objects.equals(endedAt, k.endedAt);
}

@Override
public int hashCode() {
return java.util.Objects.hash(patient, visit, queue, endedAt);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import lombok.Setter;
Expand Down Expand Up @@ -215,6 +217,18 @@ public List<QueueEntry> getQueueEntries(QueueEntrySearchCriteria searchCriteria)
return dao.getQueueEntries(searchCriteria);
}

@Override
@Transactional(readOnly = true)
public List<QueueEntry> getQueueEntries(QueueEntrySearchCriteria searchCriteria, Integer startIndex, Integer limit) {
return dao.getQueueEntries(searchCriteria, startIndex, limit);
}

@Override
@Transactional(readOnly = true)
public Map<QueueEntry, String> getPreviousQueueEntryUuids(Collection<QueueEntry> entries) {
return dao.getPreviousQueueEntryUuids(entries);
}
Comment thread
UjjawalPrabhat marked this conversation as resolved.

@Override
@Transactional(readOnly = true)
public Long getCountOfQueueEntries(QueueEntrySearchCriteria searchCriteria) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.junit.Before;
Expand Down Expand Up @@ -486,6 +487,55 @@ public void getOverlappingQueueEntries_shouldReturnEntriesOverlappingWithGivenRa
assertThat(noOverlapResults, hasSize(0));
}

@Test
public void getQueueEntries_shouldRespectStartIndexAndLimit() {
// Full result set (active only) is 4 entries, sorted by sortWeight DESC then startedAt ASC:
// → ids [3 (sw=20), 2 (sw=10), 1 (sw=0, started earlier), 4 (sw=0, started later)]
List<QueueEntry> all = dao.getQueueEntries(criteria);
assertThat(all, hasSize(4));

List<QueueEntry> firstPage = dao.getQueueEntries(criteria, 0, 2);
assertThat(firstPage, hasSize(2));
assertThat(firstPage.get(0).getQueueEntryId(), is(all.get(0).getQueueEntryId()));
assertThat(firstPage.get(1).getQueueEntryId(), is(all.get(1).getQueueEntryId()));

List<QueueEntry> secondPage = dao.getQueueEntries(criteria, 2, 2);
assertThat(secondPage, hasSize(2));
assertThat(secondPage.get(0).getQueueEntryId(), is(all.get(2).getQueueEntryId()));
assertThat(secondPage.get(1).getQueueEntryId(), is(all.get(3).getQueueEntryId()));

// Past the end → empty
assertThat(dao.getQueueEntries(criteria, 10, 5), hasSize(0));

// Nulls disable bounds — equivalent to the unpaginated call
assertThat(dao.getQueueEntries(criteria, null, null), hasSize(4));
}

@Test
public void getPreviousQueueEntryUuids_shouldReturnEmptyForNullOrEmptyInput() {
assertThat(dao.getPreviousQueueEntryUuids(null).isEmpty(), is(true));
assertThat(dao.getPreviousQueueEntryUuids(Collections.emptyList()).isEmpty(), is(true));
}

@Test
public void getPreviousQueueEntryUuids_shouldReturnUuidForEntriesWithMatchingPredecessorOnly() {
// In the dataset, entry 2 was transitioned from queue 1 — entry 1 is its predecessor
// (same patient=100, same visit=101, prev.endedAt = entry2.startedAt).
QueueEntry entry1 = dao.get(1).orElseThrow(IllegalStateException::new);
QueueEntry entry2 = dao.get(2).orElseThrow(IllegalStateException::new);
QueueEntry entry3 = dao.get(3).orElseThrow(IllegalStateException::new);
QueueEntry entry4 = dao.get(4).orElseThrow(IllegalStateException::new);

Map<QueueEntry, String> result = dao.getPreviousQueueEntryUuids(Arrays.asList(entry1, entry2, entry3, entry4));

// Only entry 2 has a queueComingFrom that resolves to a real predecessor.
assertThat(result.size(), is(1));
assertThat(result.get(entry2), is(entry1.getUuid()));
assertThat(result.containsKey(entry1), is(false));
assertThat(result.containsKey(entry3), is(false));
assertThat(result.containsKey(entry4), is(false));
}

/**
* Utility method that tests criteria against both DAO methods to getQueueEntries and
* getCountOfQueueEntries
Expand Down
Loading