Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LUI-197 UI for saving concept reference ranges #196

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
97bd354
Initial commit
dicksonmulli Aug 27, 2024
45c5846
Concept references header on the UI
dicksonmulli Aug 28, 2024
8004fd8
Showing reference range title and table headers
dicksonmulli Aug 28, 2024
c6ee428
Concept reference form - restructured
dicksonmulli Aug 29, 2024
0b72cf4
Showing saved data
dicksonmulli Aug 29, 2024
3d6c088
Updating concept page with reference ranges
dicksonmulli Aug 30, 2024
1af1c3d
Successfully saving new data
dicksonmulli Sep 1, 2024
9bcb15c
Hiding reference ranges when concept type is not numeric
dicksonmulli Sep 2, 2024
24963af
Updating version for testing purposes
dicksonmulli Sep 2, 2024
c888df0
Removing an existing row on the UI
dicksonmulli Sep 4, 2024
2695383
Adding tests
dicksonmulli Sep 5, 2024
71860fd
Hiding and showing referenceRange row on conceptForm
dicksonmulli Sep 5, 2024
5fee0d8
Creating a reflection object of reference range
dicksonmulli Sep 8, 2024
cd19cda
Using reflection to map reference ranges
dicksonmulli Sep 10, 2024
46e9cdf
Updating reflection in tests
dicksonmulli Sep 11, 2024
a43a391
Code refactor
dicksonmulli Sep 11, 2024
a1c1f4d
Update reference ranges
dicksonmulli Sep 14, 2024
04dfa3f
Reverting the core version
dicksonmulli Sep 14, 2024
270311a
Sorting reference ranges
dicksonmulli Sep 17, 2024
d3dd833
Fixing null pointer
dicksonmulli Sep 17, 2024
6cd4978
Code refactor
dicksonmulli Sep 18, 2024
36634bb
Ability to edit concept reference ranges
dicksonmulli Sep 22, 2024
2194cfb
Updating reference range validations
dicksonmulli Sep 22, 2024
71fea3e
Reconciliation of admin and dictionary views
dicksonmulli Sep 22, 2024
29ffdac
Fixing an error when 2 rows are added and the first one removed befor…
dicksonmulli Sep 27, 2024
a66e598
Conditional display of concept reference ranges based on openMRS-core…
dicksonmulli Oct 8, 2024
dd54bf1
Refactoring
dicksonmulli Oct 11, 2024
988d917
code refactor
dicksonmulli Oct 17, 2024
a98b735
code refactor
dicksonmulli Oct 17, 2024
2be1fed
removed unnecessary method
dicksonmulli Oct 18, 2024
234fb75
Hidding concept set row when checkbox is not clicked
dicksonmulli Oct 23, 2024
695d589
Showing 'Is set' when checkbox is checked
dicksonmulli Oct 27, 2024
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 @@ -9,18 +9,23 @@
*/
package org.openmrs.web.controller;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -69,7 +74,9 @@
import org.openmrs.validator.ValidateUtil;
import org.openmrs.web.WebConstants;
import org.openmrs.web.attribute.WebAttributeUtil;
import org.openmrs.web.controller.concept.ConceptReferenceRange;
import org.openmrs.web.controller.concept.ConceptReferenceTermWebValidator;
import org.openmrs.web.controller.mappper.ConceptFormMapper;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.context.support.MessageSourceAccessor;
Expand Down Expand Up @@ -271,6 +278,8 @@ protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse

validateConceptUsesPersistedObjects(concept, errors);

new ConceptFormValidator().validateConceptReferenceRange(concept, errors);

if (!errors.hasErrors()) {
if (action.equals(msa.getMessage("Concept.cancel"))) {
return new ModelAndView(new RedirectView("index.htm"));
Expand Down Expand Up @@ -459,6 +468,8 @@ public class ConceptFormBackingObject {

public Collection<ConceptAttribute> activeAttributes;

public List<org.openmrs.web.controller.concept.ConceptReferenceRange> referenceRanges;

/**
* Default constructor must take in a Concept object to create itself
*
Expand Down Expand Up @@ -517,6 +528,14 @@ public ConceptFormBackingObject(Concept concept) {
this.allowDecimal = cn.getAllowDecimal();
this.displayPrecision = cn.getDisplayPrecision();
this.units = cn.getUnits();

this.referenceRanges = ListUtils.lazyList(
new ArrayList<>(new ConceptFormMapper().mapToWebReferenceRanges(cn)
.stream()
.sorted(Comparator.comparing(ConceptReferenceRange::getId))
.collect(Collectors.toList())
),
FactoryUtils.instantiateFactory(ConceptReferenceRange.class));
} else if (concept instanceof ConceptComplex) {
ConceptComplex complex = (ConceptComplex) concept;
this.handlerKey = complex.getHandler();
Expand Down Expand Up @@ -662,6 +681,8 @@ public Concept getConceptFromFormData() {
cn.setDisplayPrecision(displayPrecision);
cn.setUnits(units);

setConceptReferenceRanges(cn);

concept = cn;

} else if (concept.getDatatype().getName().equals("Complex")) {
Expand All @@ -678,10 +699,107 @@ public Concept getConceptFromFormData() {
return concept;
}

/**
* This method sets reference ranges to concept numeric. If an existing reference range was
* removed, then we remove it from concept numeric.
*
* @param cn ConceptNumeric
* @since 1.17.0
*/
private void setConceptReferenceRanges(ConceptNumeric cn) {
if (this.referenceRanges == null) {
return;
}
for (ConceptReferenceRange referenceRange : this.referenceRanges) {
if (referenceRange == null) {
continue;
}

if (referenceRange.getId() != null) {
if (referenceRange.getId() <= 0) {
try {
Object platformReferenceRange = new ConceptFormMapper().mapToConceptReferenceRange(
referenceRange, cn);
setMethodValue(cn, "removeReferenceRange", platformReferenceRange);
}
catch (Exception exception) {
logger.error("Failed to remove reference range: Exception: " + exception.getMessage(), exception);
}
} else {
updateReferenceRange(cn, referenceRange);
}
} else {
referenceRange.setConceptNumeric(cn);
try {
Object platformReferenceRange = new ConceptFormMapper().mapToConceptReferenceRange(referenceRange,
cn);
setMethodValue(cn, "addReferenceRange", platformReferenceRange);
}
catch (Exception exception) {
logger.error("Failed to add reference range: Exception: " + exception.getMessage(), exception);
}
}
}
}

/**
* This method updates concept reference range if a field value has changed.
*
* @since 1.17.0
*/
public void updateReferenceRange(ConceptNumeric cn, ConceptReferenceRange referenceRange) {
try {
Method getReferenceRangesMethod = cn.getClass().getMethod("getReferenceRanges");
Set<?> referenceRanges = (Set<?>) getReferenceRangesMethod.invoke(cn);

for (Object rr : referenceRanges) {
Method getIdMethod = rr.getClass().getMethod("getId");
Object idValue = getIdMethod.invoke(rr);

if (Objects.equals(idValue, referenceRange.getId())) {

if (!referenceRange.getCriteria().equals(getMethodValue(rr, "getCriteria")) ||
!Objects.equals(referenceRange.getHiAbsolute(), getMethodValue(rr, "getHiAbsolute")) ||
!Objects.equals(referenceRange.getHiCritical(), getMethodValue(rr, "getHiCritical")) ||
!Objects.equals(referenceRange.getHiNormal(), getMethodValue(rr, "getHiNormal")) ||
!Objects.equals(referenceRange.getLowAbsolute(), getMethodValue(rr, "getLowAbsolute")) ||
!Objects.equals(referenceRange.getLowCritical(), getMethodValue(rr, "getLowCritical")) ||
!Objects.equals(referenceRange.getLowNormal(), getMethodValue(rr, "getLowNormal"))) {

setMethodValue(rr, "setHiAbsolute", referenceRange.getHiAbsolute());
setMethodValue(rr, "setHiCritical", referenceRange.getHiCritical());
setMethodValue(rr, "setHiNormal", referenceRange.getHiNormal());
setMethodValue(rr, "setLowAbsolute", referenceRange.getLowAbsolute());
setMethodValue(rr, "setLowCritical", referenceRange.getLowCritical());
setMethodValue(rr, "setLowNormal", referenceRange.getLowNormal());
setMethodValue(rr, "setCriteria", referenceRange.getCriteria());
}
break;
}
}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException |
ClassNotFoundException exception) {
logger.error("Failed to update reference range: Exception: " + exception.getMessage(), exception);
} catch (Exception exception) {
logger.error("Failed to add reference range: Exception: " + exception.getMessage(), exception);
}
}

private Object getMethodValue(Object obj, String methodName) throws Exception {
Method method = obj.getClass().getMethod(methodName);
return method.invoke(obj);
}

private void setMethodValue(Object obj, String methodName, Object value) throws Exception {
Method method = obj.getClass().getMethod(methodName, value.getClass());
method.invoke(obj, value);
}

/**
* Builds a white-space separated list of concept ids belonging to a concept set
*
* @return white-space separated list
* @since 1.17.0
*/
public String getSetElements() {
StringBuilder result = new StringBuilder();
Expand Down Expand Up @@ -938,6 +1056,36 @@ public List<Form> getFormsInUse() {
return Context.getFormService().getFormsContainingConcept(concept);
}

/**
* Get reference ranges
*
* @return the referenceRanges
* @since 1.17.0
*/
public List<ConceptReferenceRange> getReferenceRanges() {
return referenceRanges;
}

/**
* Sets reference ranges
*
* @param referenceRanges the referenceRanges to set
* @since 1.17.0
*/
public void setReferenceRanges(List<ConceptReferenceRange> referenceRanges) {
this.referenceRanges = referenceRanges;
}

/**
* Adds a new reference range to the list of reference ranges
*
* @param referenceRange the referenceRange to add
* @since 1.17.0
*/
public void addReferenceRange(ConceptReferenceRange referenceRange) {
getReferenceRanges().add(referenceRange);
}

/**
* Get the list of extensions/metadata and the specific instances of them that use this
* concept.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@
package org.openmrs.web.controller;

import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptMap;
import org.openmrs.ConceptName;
import org.openmrs.ConceptNumeric;
import org.openmrs.api.context.Context;
import org.openmrs.web.controller.ConceptFormController.ConceptFormBackingObject;
import org.openmrs.web.controller.concept.ConceptReferenceRange;
import org.openmrs.web.controller.mappper.ConceptFormMapper;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

Expand Down Expand Up @@ -113,4 +119,78 @@ else if (backingObject.getNamesByLocale().get(locale).getConceptNameId() != null
}
}

/**
* Validates reference range fields
*
* @param concept concept
* @param errors errors
* @since 1.17.0
*/
public void validateConceptReferenceRange(Concept concept, BindException errors) {
if (concept.isNumeric()) {
ConceptNumeric conceptNumeric = (ConceptNumeric) concept;

List<ConceptReferenceRange> referenceRanges = new ConceptFormMapper().mapToWebReferenceRanges(conceptNumeric);

if (referenceRanges == null || referenceRanges.isEmpty()) {
return;
}

int index = 0;
for (ConceptReferenceRange referenceRange : referenceRanges) {

if (referenceRange.getId() == null) {
if (referenceRange.getHiAbsolute() == null) {
setReferenceRangeErrors(errors, index, "hiAbsolute",
"Concept.referenceRanges.error.high.absolute.value.required",
"Concept.referenceRanges.error.absolute.value.required");
} else {
if (referenceRange.getHiAbsolute() > conceptNumeric.getHiAbsolute()) {
dicksonmulli marked this conversation as resolved.
Show resolved Hide resolved
setReferenceRangeErrors(errors, index, "hiAbsolute",
"Concept.referenceRanges.error.highAbsolute.value.outOfRange",
"Concept.referenceRanges.error.absolute.value.invalid");
} else if (referenceRange.getHiAbsolute() < conceptNumeric.getLowAbsolute()) {
setReferenceRangeErrors(errors, index, "hiAbsolute",
"Concept.referenceRanges.error.absolute.value.invalid",
"Concept.referenceRanges.error.absolute.value.invalid");
}
}
if (referenceRange.getLowAbsolute() == null) {
setReferenceRangeErrors(errors, index, "lowAbsolute",
"Concept.referenceRanges.error.low.absolute.value.required",
"Concept.referenceRanges.error.absolute.value.required");
} else {
if (referenceRange.getLowAbsolute() < conceptNumeric.getLowAbsolute()) {
setReferenceRangeErrors(errors, index, "lowAbsolute",
"Concept.referenceRanges.error.lowAbsolute.value.outOfRange",
"Concept.referenceRanges.error.absolute.value.invalid");
} else if (referenceRange.getLowAbsolute() > conceptNumeric.getHiAbsolute()) {
setReferenceRangeErrors(errors, index, "lowAbsolute",
"Concept.referenceRanges.error.absolute.value.invalid",
"Concept.referenceRanges.error.absolute.value.invalid");
}
}

index++;
}
}
}
}

/**
* Set Reference Range Errors
*
* @param errors BindException
* @param index index of referenceRange row
* @param field field of the reference range
* @param errorCode error code
* @param defaultMessage default message
* @since 1.17.0
*/
private static void setReferenceRangeErrors(BindException errors, long index, String field, String errorCode,
String defaultMessage) {
errors.pushNestedPath("referenceRanges[" + index + "]");
errors.rejectValue(field, errorCode, defaultMessage);
errors.popNestedPath();
}
}
Loading