Skip to content

Commit

Permalink
O3-2446 - Each Queue should support a configurable set of priorities …
Browse files Browse the repository at this point in the history
…and statuses that differs from the default
  • Loading branch information
mseaton committed Jan 3, 2024
1 parent 2705eed commit 5e6dd05
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import org.openmrs.Patient;
import org.openmrs.Provider;
import org.openmrs.Visit;
import org.openmrs.api.AdministrationService;
import org.openmrs.api.ConceptService;
import org.openmrs.api.LocationService;
import org.openmrs.api.PatientService;
import org.openmrs.api.ProviderService;
import org.openmrs.api.VisitService;
import org.openmrs.module.queue.QueueModuleConstants;
import org.openmrs.module.queue.model.Queue;
import org.openmrs.module.queue.model.QueueRoom;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -42,6 +44,8 @@ public class QueueServicesWrapper {

private final RoomProviderMapService roomProviderMapService;

private final AdministrationService administrationService;

private final ConceptService conceptService;

private final LocationService locationService;
Expand All @@ -57,12 +61,14 @@ public QueueServicesWrapper(@Qualifier("queue.QueueService") QueueService queueS
@Qualifier("queue.QueueEntryService") QueueEntryService queueEntryService,
@Qualifier("queue.QueueRoomService") QueueRoomService queueRoomService,
@Qualifier("queue.RoomProviderMapService") RoomProviderMapService roomProviderMapService,
ConceptService conceptService, LocationService locationService, PatientService patientService,
VisitService visitService, ProviderService providerService) {
@Qualifier("adminService") AdministrationService administrationService, ConceptService conceptService,
LocationService locationService, PatientService patientService, VisitService visitService,
ProviderService providerService) {
this.queueService = queueService;
this.queueEntryService = queueEntryService;
this.queueRoomService = queueRoomService;
this.roomProviderMapService = roomProviderMapService;
this.administrationService = administrationService;
this.conceptService = conceptService;
this.locationService = locationService;
this.patientService = patientService;
Expand Down Expand Up @@ -256,4 +262,54 @@ public QueueRoom getQueueRoom(String queueRoomRef) {
}
throw new IllegalArgumentException("Unable to find queueRoom: " + queueRoomRef);
}

/**
* @return the allowed service concepts
*/
public List<Concept> getAllowedServices() {
Concept conceptSet = getConcept(getGlobalProperty(QueueModuleConstants.QUEUE_SERVICE));
if (conceptSet == null) {
throw new IllegalStateException("Missing global property: " + QueueModuleConstants.QUEUE_SERVICE);
}
return conceptSet.getSetMembers();
}

/**
* @param queue
* @return the allowed status concepts for the given queue, either configured directly or from GP
* default
*/
public List<Concept> getAllowedStatuses(Queue queue) {
Concept conceptSet = queue.getStatusConceptSet();
if (conceptSet == null) {
conceptSet = getConcept(getGlobalProperty(QueueModuleConstants.QUEUE_STATUS));
}
if (conceptSet == null) {
throw new IllegalStateException("Missing global property: " + QueueModuleConstants.QUEUE_STATUS);
}
return conceptSet.getSetMembers();
}

/**
* @param queue
* @return the allowed priority concepts for the given queue, either configured directly or from GP
* default
*/
public List<Concept> getAllowedPriorities(Queue queue) {
Concept conceptSet = queue.getPriorityConceptSet();
if (conceptSet == null) {
conceptSet = getConcept(getGlobalProperty(QueueModuleConstants.QUEUE_PRIORITY));
}
if (conceptSet == null) {
throw new IllegalStateException("Missing global property: " + QueueModuleConstants.QUEUE_PRIORITY);
}
return conceptSet.getSetMembers();
}

/**
* @return the value of the global property with the given name
*/
public String getGlobalProperty(String property) {
return administrationService.getGlobalProperty(property);
}
}
8 changes: 8 additions & 0 deletions api/src/main/java/org/openmrs/module/queue/model/Queue.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ public class Queue extends BaseChangeableOpenmrsMetadata {
@JoinColumn(name = "service", referencedColumnName = "concept_id", nullable = false)
private Concept service;

@ManyToOne
@JoinColumn(name = "priority_concept_set", referencedColumnName = "concept_id")
private Concept priorityConceptSet;

@ManyToOne
@JoinColumn(name = "status_concept_set", referencedColumnName = "concept_id")
private Concept statusConceptSet;

@OneToMany(mappedBy = "queue", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<QueueEntry> queueEntries;

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import static org.springframework.validation.ValidationUtils.rejectIfEmptyOrWhitespace;

import org.openmrs.annotation.Handler;
import org.openmrs.api.context.Context;
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.utils.QueueValidationUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

Expand All @@ -30,19 +32,37 @@ public void validate(Object target, Errors errors) {
if (!(target instanceof QueueEntry)) {
throw new IllegalArgumentException("the parameter target must be of type " + QueueEntry.class);
}
rejectIfEmptyOrWhitespace(errors, "queue", "queueEntry.queue.null", "The property queue should not be null");
rejectIfEmptyOrWhitespace(errors, "patient", "queueEntry.patient.null", "The property patient should not be null");
rejectIfEmptyOrWhitespace(errors, "startedAt", "queueEntry.startedAt.null",
"The property startedAt should not be null");

QueueEntry queueEntry = (QueueEntry) target;
Queue queue = queueEntry.getQueue();

if (queueEntry.getEndedAt() != null) {
//queueEntry.endedAt >= queueEntry.startedAt
if (queueEntry.getStartedAt().after(queueEntry.getEndedAt())) {
errors.rejectValue("endedAt", "queueEntry.endedAt.invalid",
"Queue entry endedAt should after the startedAt date");
"Queue entry endedAt should be on or after the startedAt date");
}
}

QueueValidationUtils.validateQueueEntry((QueueEntry) target, errors);
QueueServicesWrapper queueServices = Context.getRegisteredComponents(QueueServicesWrapper.class).get(0);
if (queueEntry.getStatus() == null) {
errors.rejectValue("status", "queueEntry.status.null", "The property status should not be null");
} else {
if (!queueServices.getAllowedStatuses(queue).contains(queueEntry.getStatus())) {
errors.rejectValue("status", "queueEntry.status.invalid",
"The property status should be a member of configured queue status conceptSet.");
}
}
if (queueEntry.getPriority() == null) {
errors.rejectValue("priority", "queueEntry.priority.null", "The property priority should not be null");
} else {
if (!queueServices.getAllowedPriorities(queue).contains(queueEntry.getPriority())) {
errors.rejectValue("priority", "queueEntry.priority.invalid",
"The property priority should be a member of configured queue priority conceptSet.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@
*/
package org.openmrs.module.queue.validators;

import javax.validation.constraints.NotNull;

import lombok.extern.slf4j.Slf4j;
import org.openmrs.Location;
import org.openmrs.annotation.Handler;
import org.openmrs.api.context.Context;
import org.openmrs.module.queue.api.QueueServicesWrapper;
import org.openmrs.module.queue.model.Queue;
import org.openmrs.module.queue.utils.QueueValidationUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
Expand All @@ -38,38 +35,22 @@ public void validate(Object target, Errors errors) {
log.debug("{}.validate", this.getClass().getName());
//instanceof checks for null
if (!(target instanceof Queue)) {
throw new IllegalArgumentException("The parameter target should not be null & must be of type" + Queue.class);
throw new IllegalArgumentException("Invalid Queue class: " + target.getClass().getName());
}
Queue queue = (Queue) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "queue.name.null", "Queue name can't be null");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "location", "queue.location.null", "Location can't be null");

if (!isValidLocation(queue.getLocation())) {
errors.rejectValue("location", "queue.location.null", "Location is null or doesn't exists");
}
// TODO: Check if the location is tagged as a Queue Location?

QueueServicesWrapper queueServices = Context.getRegisteredComponents(QueueServicesWrapper.class).get(0);
if (queue.getService() == null) {
errors.rejectValue("service", "QueueEntry.service.null", "The property service should not be null");
} else {
if (!QueueValidationUtils.isValidService(queue.getService())) {
if (!queueServices.getAllowedServices().contains(queue.getService())) {
errors.rejectValue("service", "Queue.service.invalid",
"The property service should be a member of configured queue service conceptSet.");
}
}
}

/**
* For now checks for existence
*
* @param location location to check if it exists
* @return true or false if the location exists
*/
public boolean isValidLocation(@NotNull Location location) {
if (location == null) {
return false;
}
Location isExistentLocation = Context.getLocationService().getLocationByUuid(location.getUuid());
return isExistentLocation != null;
}

}
28 changes: 28 additions & 0 deletions api/src/main/resources/liquibase.xml
Original file line number Diff line number Diff line change
Expand Up @@ -418,4 +418,32 @@
<dropTable tableName="visit_queue_entries"/>
</changeSet>

<changeSet id="add_priority_concept_set_to_queue_20240102" author="mseaton">
<preConditions onError="WARN" onFail="MARK_RAN">
<tableExists tableName="queue"/>
<not><columnExists tableName="queue" columnName="priority_concept_set"/></not>
</preConditions>
<comment>
Add column priority_concept_set to queue table
</comment>
<addColumn tableName="queue">
<column name="priority_concept_set" type="int"/>
</addColumn>
<addForeignKeyConstraint baseColumnNames="priority_concept_set" baseTableName="queue" constraintName="queue_priority_concept_set_fk" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="concept_id" referencedTableName="concept"/>
</changeSet>

<changeSet id="add_status_concept_set_to_queue_20240102" author="mseaton">
<preConditions onError="WARN" onFail="MARK_RAN">
<tableExists tableName="queue"/>
<not><columnExists tableName="queue" columnName="status_concept_set"/></not>
</preConditions>
<comment>
Add column status_concept_set to queue table
</comment>
<addColumn tableName="queue">
<column name="status_concept_set" type="int"/>
</addColumn>
<addForeignKeyConstraint baseColumnNames="status_concept_set" baseTableName="queue" constraintName="queue_status_concept_set_fk" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="concept_id" referencedTableName="concept"/>
</changeSet>

</databaseChangeLog>
Loading

0 comments on commit 5e6dd05

Please sign in to comment.