Skip to content

Commit

Permalink
O3-2868: Provide service method and REST endpoint to facilitate trans…
Browse files Browse the repository at this point in the history
…itioning from one queue entry to another (#54)
  • Loading branch information
mseaton authored Feb 16, 2024
1 parent 6e8bbe9 commit c8a48f0
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.openmrs.module.queue.api.search.QueueEntrySearchCriteria;
import org.openmrs.module.queue.model.Queue;
import org.openmrs.module.queue.model.QueueEntry;
import org.openmrs.module.queue.model.QueueEntryTransition;

public interface QueueEntryService {

Expand Down Expand Up @@ -48,6 +49,15 @@ public interface QueueEntryService {
*/
QueueEntry saveQueueEntry(@NotNull QueueEntry queueEntry);

/**
* Transitions a queue entry by ending one queue entry and creating a new queue entry that starts at
* that time
*
* @param queueEntryTransition the queueEntryTransition
* @return the new QueueEntry that is created
*/
QueueEntry transitionQueueEntry(@NotNull QueueEntryTransition queueEntryTransition);

/**
* Voids a queue entry
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.openmrs.module.queue.exception.DuplicateQueueEntryException;
import org.openmrs.module.queue.model.Queue;
import org.openmrs.module.queue.model.QueueEntry;
import org.openmrs.module.queue.model.QueueEntryTransition;
import org.openmrs.module.queue.utils.QueueUtils;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -100,6 +101,20 @@ public QueueEntry saveQueueEntry(QueueEntry queueEntry) {
return dao.createOrUpdate(queueEntry);
}

/**
* @see QueueEntryService#transitionQueueEntry(QueueEntryTransition)
*/
@Override
public QueueEntry transitionQueueEntry(QueueEntryTransition queueEntryTransition) {
// End the initial queue entry
QueueEntry queueEntryToStop = queueEntryTransition.getQueueEntryToTransition();
queueEntryToStop.setEndedAt(queueEntryTransition.getTransitionDate());
getProxiedQueueEntryService().saveQueueEntry(queueEntryToStop);
// Create a new queue entry
QueueEntry queueEntryToStart = queueEntryTransition.constructNewQueueEntry();
return getProxiedQueueEntryService().saveQueueEntry(queueEntryToStart);
}

/**
* @see QueueEntryService#voidQueueEntry(QueueEntry, String)
*/
Expand Down Expand Up @@ -169,6 +184,15 @@ public void closeActiveQueueEntries() {
queueEntries.forEach(this::endQueueEntry);
}

/**
* @return the instance of the QueueEntryService from the context. This is needed for
* self-referential access to ensure proxied instance is returned with relevant AOP
* operations. Only for internal use.
*/
protected QueueEntryService getProxiedQueueEntryService() {
return Context.getService(QueueEntryService.class);
}

private void endQueueEntry(@NotNull QueueEntry queueEntry) {
queueEntry.setEndedAt(new Date());
dao.createOrUpdate(queueEntry);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.queue.model;

import java.io.Serializable;
import java.util.Date;

import lombok.Data;
import org.openmrs.Concept;

/**
* Bean definition that encapsulates the supported criteria for saving a direct transition from one
* QueueEntry to another QueueEntry.
*/
@Data
public class QueueEntryTransition implements Serializable {

private static final long serialVersionUID = 1L;

private QueueEntry queueEntryToTransition;

private Date transitionDate;

private Queue newQueue;

private Concept newStatus;

private Concept newPriority;

/**
* @return a new queue entry representing what one intends to transition into
*/
public QueueEntry constructNewQueueEntry() {
QueueEntry queueEntry = new QueueEntry();
queueEntry.setQueue(newQueue == null ? queueEntryToTransition.getQueue() : newQueue);
queueEntry.setPatient(queueEntryToTransition.getPatient());
queueEntry.setVisit(queueEntryToTransition.getVisit());
queueEntry.setPriority(newPriority == null ? queueEntryToTransition.getPriority() : newPriority);
queueEntry.setPriorityComment(queueEntryToTransition.getPriorityComment());
queueEntry.setStatus(newStatus == null ? queueEntryToTransition.getStatus() : newStatus);
queueEntry.setSortWeight(queueEntryToTransition.getSortWeight());
queueEntry.setLocationWaitingFor(queueEntryToTransition.getLocationWaitingFor());
queueEntry.setProviderWaitingFor(queueEntryToTransition.getProviderWaitingFor());
queueEntry.setQueueComingFrom(queueEntryToTransition.getQueue());
queueEntry.setStartedAt(transitionDate);
return queueEntry;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
Expand All @@ -19,6 +20,7 @@
import java.util.Date;
import java.util.Optional;

import org.apache.commons.lang.time.DateUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -30,6 +32,7 @@
import org.openmrs.Concept;
import org.openmrs.Location;
import org.openmrs.Patient;
import org.openmrs.Provider;
import org.openmrs.User;
import org.openmrs.Visit;
import org.openmrs.VisitAttributeType;
Expand All @@ -42,6 +45,7 @@
import org.openmrs.module.queue.exception.DuplicateQueueEntryException;
import org.openmrs.module.queue.model.Queue;
import org.openmrs.module.queue.model.QueueEntry;
import org.openmrs.module.queue.model.QueueEntryTransition;

@RunWith(MockitoJUnitRunner.class)
public class QueueEntryServiceTest {
Expand All @@ -64,7 +68,13 @@ public class QueueEntryServiceTest {
@Before
public void setupMocks() {
MockitoAnnotations.openMocks(this);
queueEntryService = new QueueEntryServiceImpl();
queueEntryService = new QueueEntryServiceImpl() {

@Override
protected QueueEntryService getProxiedQueueEntryService() {
return this;
}
};
queueEntryService.setDao(dao);
queueEntryService.setVisitService(visitService);
}
Expand Down Expand Up @@ -195,6 +205,83 @@ public void shouldGetQueuesEntriesByCriteria() {
assertThat(daoCriteria, equalTo(criteria));
}

@Test
public void shouldTransitionQueueEntry() {
Patient patient1 = new Patient();
Visit visit1 = new Visit();
visit1.setPatient(patient1);
Queue queue0 = new Queue();
Queue queue1 = new Queue();
Queue queue2 = new Queue();
Concept concept1 = new Concept();
Concept concept2 = new Concept();
String string1 = "starting";
double double1 = 5.0;
Location location1 = new Location();
Provider provider1 = new Provider();
Date date1 = DateUtils.addHours(new Date(), -12);
Date date2 = DateUtils.addHours(date1, 6);
Date date3 = DateUtils.addHours(date1, 3);

QueueEntry queueEntry1 = new QueueEntry();
queueEntry1.setQueue(queue1);
queueEntry1.setPatient(patient1);
queueEntry1.setVisit(visit1);
queueEntry1.setPriority(concept1);
queueEntry1.setPriorityComment(string1);
queueEntry1.setStatus(concept1);
queueEntry1.setSortWeight(double1);
queueEntry1.setLocationWaitingFor(location1);
queueEntry1.setProviderWaitingFor(provider1);
queueEntry1.setQueueComingFrom(queue0);
queueEntry1.setStartedAt(date1);
assertNull(queueEntry1.getEndedAt());

// Mock the DAO to return the object being saved
when(dao.createOrUpdate(any())).thenAnswer(invocation -> invocation.getArguments()[0]);

// First transition test that no changes are required and all values will be pulled from existing queue entry
QueueEntryTransition transition1 = new QueueEntryTransition();
transition1.setQueueEntryToTransition(queueEntry1);
transition1.setTransitionDate(date2);
QueueEntry queueEntry2 = queueEntryService.transitionQueueEntry(transition1);
assertThat(queueEntry1.getEndedAt(), equalTo(date2));
assertThat(queueEntry2.getQueue(), equalTo(queue1));
assertThat(queueEntry2.getPatient(), equalTo(patient1));
assertThat(queueEntry2.getVisit(), equalTo(visit1));
assertThat(queueEntry2.getPriority(), equalTo(concept1));
assertThat(queueEntry2.getPriorityComment(), equalTo(string1));
assertThat(queueEntry2.getStatus(), equalTo(concept1));
assertThat(queueEntry2.getSortWeight(), equalTo(double1));
assertThat(queueEntry2.getLocationWaitingFor(), equalTo(location1));
assertThat(queueEntry2.getProviderWaitingFor(), equalTo(provider1));
assertThat(queueEntry2.getQueueComingFrom(), equalTo(queue1));
assertThat(queueEntry2.getStartedAt(), equalTo(date2));
assertNull(queueEntry2.getEndedAt());

// Next transition test that appropriate fields can be changed
QueueEntryTransition transition2 = new QueueEntryTransition();
transition2.setQueueEntryToTransition(queueEntry2);
transition2.setTransitionDate(date3);
transition2.setNewQueue(queue2);
transition2.setNewPriority(concept2);
transition2.setNewStatus(concept2);
QueueEntry queueEntry3 = queueEntryService.transitionQueueEntry(transition2);
assertThat(queueEntry2.getEndedAt(), equalTo(date3));
assertThat(queueEntry3.getQueue(), equalTo(queue2));
assertThat(queueEntry3.getPatient(), equalTo(patient1));
assertThat(queueEntry3.getVisit(), equalTo(visit1));
assertThat(queueEntry3.getPriority(), equalTo(concept2));
assertThat(queueEntry3.getPriorityComment(), equalTo(string1));
assertThat(queueEntry3.getStatus(), equalTo(concept2));
assertThat(queueEntry3.getSortWeight(), equalTo(double1));
assertThat(queueEntry3.getLocationWaitingFor(), equalTo(location1));
assertThat(queueEntry3.getProviderWaitingFor(), equalTo(provider1));
assertThat(queueEntry3.getQueueComingFrom(), equalTo(queue1));
assertThat(queueEntry3.getStartedAt(), equalTo(date3));
assertNull(queueEntry3.getEndedAt());
}

@Test
public void shouldGenerateVisitQueueNumber() {
Visit visit = new Visit();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.queue.web;

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

import org.openmrs.Concept;
import org.openmrs.api.APIException;
import org.openmrs.module.queue.api.QueueServicesWrapper;
import org.openmrs.module.queue.model.Queue;
import org.openmrs.module.queue.model.QueueEntry;
import org.openmrs.module.queue.model.QueueEntryTransition;
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.RestConstants;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.openmrs.module.webservices.rest.web.v1_0.controller.BaseRestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* The main controller that exposes additional end points for order entry
*/
@Controller
@RequestMapping(value = "/rest/" + RestConstants.VERSION_1 + "/queue-entry-transition")
public class QueueEntryTransitionRestController extends BaseRestController {

public static final String QUEUE_ENTRY_TO_TRANSITION = "queueEntryToTransition";

public static final String TRANSITION_DATE = "transitionDate";

public static final String NEW_QUEUE = "newQueue";

public static final String NEW_STATUS = "newStatus";

public static final String NEW_PRIORITY = "newPriority";

private final QueueServicesWrapper services;

@Autowired
public QueueEntryTransitionRestController(QueueServicesWrapper services) {
this.services = services;
}

@RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
@ResponseBody
public Object transitionQueueEntry(@RequestBody Map<String, String> body) {
QueueEntryTransition transition = new QueueEntryTransition();

// Queue Entry to Transition
String queueEntryUuid = body.get(QUEUE_ENTRY_TO_TRANSITION);
QueueEntry queueEntry = services.getQueueEntryService().getQueueEntryByUuid(queueEntryUuid)
.orElseThrow(() -> new APIException(QUEUE_ENTRY_TO_TRANSITION + " is a required parameter"));
transition.setQueueEntryToTransition(queueEntry);

// Transition Date
Date transitionDate = new Date();
if (body.containsKey(TRANSITION_DATE)) {
transitionDate = (Date) ConversionUtil.convert(body.get(TRANSITION_DATE), Date.class);
}
if (transitionDate == null) {
throw new APIException("Invalid transition date specified: " + body.get(TRANSITION_DATE));
}
transition.setTransitionDate(transitionDate);

// Queue
if (body.containsKey(NEW_QUEUE)) {
Optional<Queue> queueOptional = services.getQueueService().getQueueByUuid(body.get(NEW_QUEUE));
if (!queueOptional.isPresent()) {
throw new APIException("Invalid queue specified: " + body.get(NEW_QUEUE));
}
transition.setNewQueue(queueOptional.get());
}

// Status
if (body.containsKey(NEW_STATUS)) {
Concept concept = services.getConcept(body.get(NEW_STATUS));
if (concept == null) {
throw new APIException("Invalid status specified: " + body.get(NEW_STATUS));
}
transition.setNewStatus(concept);
}

// Priority
if (body.containsKey(NEW_PRIORITY)) {
Concept concept = services.getConcept(body.get(NEW_PRIORITY));
if (concept == null) {
throw new APIException("Invalid priority specified: " + body.get(NEW_PRIORITY));
}
transition.setNewPriority(concept);
}

// Execute transition
QueueEntry newQueueEntry = services.getQueueEntryService().transitionQueueEntry(transition);
return ConversionUtil.convertToRepresentation(newQueueEntry, Representation.REF);
}
}

0 comments on commit c8a48f0

Please sign in to comment.