Skip to content

Commit

Permalink
LUI-197 UI for saving concept reference ranges (#196)
Browse files Browse the repository at this point in the history
* Initial commit

* Concept references header on the UI

* Showing reference range title and table headers

* Concept reference form - restructured

* Showing saved data

* Updating concept page with reference ranges

* Successfully saving new data

* Hiding reference ranges when concept type is not numeric

* Updating version for testing purposes

* Removing an existing row on the UI

* Adding tests

* Hiding and showing referenceRange row on conceptForm

* Creating a reflection object of reference range

* Using reflection to map reference ranges

* Updating reflection in tests

* Code refactor

* Update reference ranges

* Reverting the core version

* Sorting reference ranges

* Fixing null pointer

* Code refactor

* Ability to edit concept reference ranges

* Updating reference range validations

* Reconciliation of admin and dictionary views

* Fixing an error when 2 rows are added and the first one removed before saving

* Conditional display of concept reference ranges based on openMRS-core version

* Refactoring

* code refactor

* code refactor

* removed unnecessary method

* Hidding concept set row when checkbox is not clicked

* Showing 'Is set' when checkbox is checked
  • Loading branch information
dicksonmulli authored Nov 26, 2024
1 parent e382018 commit 89cdd96
Show file tree
Hide file tree
Showing 13 changed files with 1,075 additions and 3 deletions.
20 changes: 20 additions & 0 deletions api/src/main/java/org/openmrs/module/legacyui/GeneralUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import org.openmrs.Concept;
import org.openmrs.api.context.Context;
import org.openmrs.util.OpenmrsConstants;

public class GeneralUtils {

Expand Down Expand Up @@ -68,4 +69,23 @@ public static Concept getConcept(String id) {

return cpt;
}

/**
* Checks if current version of openmrs is greater or equal to 2.7.0 The aim is to try loading
* ConceptReferenceRange class, which is in version 2.7.0. If the ConceptReferenceRange class is
* loaded, then the current version is greater than or equal to 2.7.0
*
* @return true if current version is greater or equal to 2.7.0 and false otherwise
* @since 1.17.0
*/
public static boolean isTwoPointSevenAndAbove() {
try {
Class.forName("org.openmrs.ConceptReferenceRange");

return true;
}
catch (ClassNotFoundException exception) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@
*/
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.Collections;
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 javax.servlet.ServletException;
Expand Down Expand Up @@ -54,6 +58,7 @@
import org.openmrs.api.ConceptsLockedException;
import org.openmrs.api.DuplicateConceptNameException;
import org.openmrs.api.context.Context;
import org.openmrs.module.legacyui.GeneralUtils;
import org.openmrs.module.web.extension.ConceptUsageExtension;
import org.openmrs.module.web.extension.provider.Link;
import org.openmrs.propertyeditor.ConceptAnswersEditor;
Expand All @@ -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 @@ -406,6 +415,9 @@ protected Map<String, Object> referenceData(HttpServletRequest request) throws E
map.put("locale", Context.getLocale()); // should be same string format as conceptNamesByLocale map keys

map.put("attributeTypes", cs.getAllConceptAttributeTypes());

map.put("canUseConceptReferenceRanges", GeneralUtils.isTwoPointSevenAndAbove());

return map;
}

Expand Down Expand Up @@ -459,6 +471,8 @@ public class ConceptFormBackingObject {

public Collection<ConceptAttribute> activeAttributes;

public List<ConceptReferenceRange> referenceRanges;

/**
* Default constructor must take in a Concept object to create itself
*
Expand Down Expand Up @@ -517,6 +531,10 @@ 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)),
FactoryUtils.instantiateFactory(ConceptReferenceRange.class));
} else if (concept instanceof ConceptComplex) {
ConceptComplex complex = (ConceptComplex) concept;
this.handlerKey = complex.getHandler();
Expand Down Expand Up @@ -662,6 +680,8 @@ public Concept getConceptFromFormData() {
cn.setDisplayPrecision(displayPrecision);
cn.setUnits(units);

setConceptReferenceRanges(cn);

concept = cn;

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

/**
* This method sets reference ranges to 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) {
removeReferenceRange(cn, referenceRange);
} else {
updateReferenceRange(cn, referenceRange);
}
} else {
if (referenceRange.getHiAbsolute() != null && referenceRange.getLowAbsolute() != null) {
addReferenceRange(cn, referenceRange);
}
}
}
}

/**
* This method removes a reference range from conceptNumeric
*
* @param cn conceptNumeric
* @param referenceRange referenceRange
* @since 1.17.0
*/
private void removeReferenceRange(ConceptNumeric cn, ConceptReferenceRange referenceRange) {
try {
Object platformReferenceRange = new ConceptFormMapper().mapToConceptReferenceRange(referenceRange, cn);
setMethodValue(cn, "removeReferenceRange", platformReferenceRange);
}
catch (Exception exception) {
// Note that openMRS-core version 2.7.0 or higher is required for this functionality to work.
}
}

/**
* This method adds a new reference range to conceptNumeric
*
* @param cn conceptNumeric
* @param referenceRange referenceRange
* @since 1.17.0
*/
private void addReferenceRange(ConceptNumeric cn, ConceptReferenceRange referenceRange) {
referenceRange.setConceptNumeric(cn);
try {
Object platformReferenceRange = new ConceptFormMapper().mapToConceptReferenceRange(referenceRange, cn);
setMethodValue(cn, "addReferenceRange", platformReferenceRange);
}
catch (Exception exception) {
// Note that openMRS-core version 2.7.0 or higher is required for this functionality to work.
}
}

/**
* This method updates concept reference range if a field value has changed.
*
* @param cn ConceptNumeric
* @param referenceRange ConceptReferenceRange
* @since 1.17.0
*/
public void updateReferenceRange(ConceptNumeric cn, ConceptReferenceRange referenceRange) {
try {
Set<?> existingReferenceRanges = getExistingReferenceRanges(cn);

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

if (Objects.equals(idValue, referenceRange.getId()) && hasReferenceRangeChanged(existingRange, referenceRange)) {
updateReferenceRangeFields(existingRange, referenceRange);
break;
}
}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException |
ClassNotFoundException exception) {
// Note that openMRS-core version 2.7.0 or higher is required for this functionality to work.
} catch (Exception exception) {
// Note that openMRS-core version 2.7.0 or higher is required for this functionality to work.
}
}

/**
* This method gets the existing reference ranges
*
* @param cn ConceptNumeric
* @return a set of reference ranges
* @since 1.17.0
*/
private Set<?> getExistingReferenceRanges(ConceptNumeric cn) {
try {
Method getReferenceRangesMethod = cn.getClass().getMethod("getReferenceRanges");
return (Set<?>) getReferenceRangesMethod.invoke(cn);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
// Note that openMRS-core version 2.7.0 or higher is required for this functionality to work.
return Collections.emptySet();
}
}

private boolean hasReferenceRangeChanged(Object existingRange, ConceptReferenceRange referenceRange)
throws Exception {
return !Objects.equals(referenceRange.getCriteria(), getMethodValue(existingRange, "getCriteria"))
|| !Objects.equals(referenceRange.getHiAbsolute(), getMethodValue(existingRange, "getHiAbsolute"))
|| !Objects.equals(referenceRange.getHiCritical(), getMethodValue(existingRange, "getHiCritical"))
|| !Objects.equals(referenceRange.getHiNormal(), getMethodValue(existingRange, "getHiNormal"))
|| !Objects.equals(referenceRange.getLowAbsolute(), getMethodValue(existingRange, "getLowAbsolute"))
|| !Objects.equals(referenceRange.getLowCritical(), getMethodValue(existingRange, "getLowCritical"))
|| !Objects.equals(referenceRange.getLowNormal(), getMethodValue(existingRange, "getLowNormal"));
}

/**
* This method updates reference range fields
*
* @param existingRange existing reference range
* @param referenceRange the updated reference range
* @throws Exception exception
* @since 1.17.0
*/
private void updateReferenceRangeFields(Object existingRange, ConceptReferenceRange referenceRange) throws Exception {
updateField(existingRange, "setHiAbsolute", referenceRange.getHiAbsolute());
updateField(existingRange, "setHiCritical", referenceRange.getHiCritical());
updateField(existingRange, "setHiNormal", referenceRange.getHiNormal());
updateField(existingRange, "setLowAbsolute", referenceRange.getLowAbsolute());
updateField(existingRange, "setLowCritical", referenceRange.getLowCritical());
updateField(existingRange, "setLowNormal", referenceRange.getLowNormal());
updateField(existingRange, "setCriteria", referenceRange.getCriteria());
}

private void updateField(Object obj, String methodName, Object value) throws Exception {
if (value != null) {
setMethodValue(obj, methodName, value);
}
}

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
*
Expand Down Expand Up @@ -938,6 +1112,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
Loading

0 comments on commit 89cdd96

Please sign in to comment.