use of org.openmrs.ConceptAnswer in project openmrs-core by openmrs.
the class ConceptAnswersEditor method setAsText.
/**
* loops over the textbox assigned to this property. The textbox is assumed to be a string of
* conceptIds^drugIds separated by spaces.
*
* @param text list of conceptIds (not conceptAnswerIds)
* @should set the sort weights with the least possible changes
*/
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (StringUtils.hasText(text)) {
ConceptService cs = Context.getConceptService();
String[] conceptIds = text.split(" ");
List<String> requestConceptIds = new ArrayList<>();
// set up parameter answer Set for easier add/delete functions and removal of duplicates
for (String id : conceptIds) {
id = id.trim();
if (!("".equals(id)) && !requestConceptIds.contains(id)) {
// remove whitespace, blank lines, and duplicates
requestConceptIds.add(id);
}
}
Collection<ConceptAnswer> deletedConceptAnswers = new HashSet<>();
// loop over original concept answers to find any deleted answers
for (ConceptAnswer origConceptAnswer : originalConceptAnswers) {
boolean answerDeleted = true;
for (String conceptId : requestConceptIds) {
Integer id = getConceptId(conceptId);
Integer drugId = getDrugId(conceptId);
Drug answerDrug = origConceptAnswer.getAnswerDrug();
if (id.equals(origConceptAnswer.getAnswerConcept().getConceptId())) {
if (drugId == null && answerDrug == null) {
answerDeleted = false;
} else if ((drugId != null && answerDrug != null) && drugId.equals(origConceptAnswer.getAnswerDrug().getDrugId())) {
answerDeleted = false;
}
}
}
if (answerDeleted) {
deletedConceptAnswers.add(origConceptAnswer);
}
}
// loop over those deleted answers to delete them
for (ConceptAnswer conceptAnswer : deletedConceptAnswers) {
originalConceptAnswers.remove(conceptAnswer);
}
// loop over concept ids in the request to add any that are new
for (String conceptId : requestConceptIds) {
Integer id = getConceptId(conceptId);
Integer drugId = getDrugId(conceptId);
boolean newAnswerConcept = true;
for (ConceptAnswer origConceptAnswer : originalConceptAnswers) {
Drug answerDrug = origConceptAnswer.getAnswerDrug();
if (id.equals(origConceptAnswer.getAnswerConcept().getConceptId())) {
if (drugId == null && answerDrug == null) {
newAnswerConcept = false;
} else if ((drugId != null && answerDrug != null) && drugId.equals(answerDrug.getDrugId())) {
newAnswerConcept = false;
}
}
}
// if the current request answer is new, add it to the originals
if (newAnswerConcept) {
Concept answer = cs.getConcept(id);
Drug drug = null;
if (drugId != null) {
drug = cs.getDrug(drugId);
}
ConceptAnswer ac = new ConceptAnswer(answer, drug);
originalConceptAnswers.add(ac);
}
}
// loop over to set the order
// as the list comes into 'requestConceptIds' in the order the user wants
// there are 2 conditions that will require the sort_weights to be reassigned
// 1) any ConceptAnswer.sortWeight == NULL (meaning it is just added)
// 2) the list is not in ASCENDING order (example sort order of the list is 1, 2, 10, 9)
// -startIdx (start index) is where in this list we will start to reassign the sort_weights
Double lastWeightSeen = null;
// the idx to start at, if we have a NULL sort weight (new concept answer) or sort weights are not ascending
int startIdx = -1;
for (int i = 0; i < requestConceptIds.size() - 1; i++) {
Integer id1 = getConceptId(requestConceptIds.get(i));
ConceptAnswer ca1 = getConceptAnswerFromOriginal(id1);
if (ca1.getSortWeight() == null) {
if (lastWeightSeen == null) {
// start at 1, we're at the beginning
lastWeightSeen = 1d;
} else {
// we start at +1
lastWeightSeen += 1;
}
startIdx = i;
break;
}
Integer id2 = getConceptId(requestConceptIds.get(i + 1));
ConceptAnswer ca2 = getConceptAnswerFromOriginal(id2);
int c = ca1.compareTo(ca2);
if (c > 0) {
startIdx = i;
lastWeightSeen = ca1.getSortWeight();
break;
}
lastWeightSeen = ca1.getSortWeight();
}
if (startIdx != -1) {
// then we need to re-weight
for (int i = startIdx; i < requestConceptIds.size(); i++) {
Integer id = getConceptId(requestConceptIds.get(i));
ConceptAnswer ca = getConceptAnswerFromOriginal(id);
ca.setSortWeight(lastWeightSeen++);
}
}
log.debug("originalConceptAnswers.getConceptId(): ");
for (ConceptAnswer a : originalConceptAnswers) {
log.debug("id: " + a.getAnswerConcept().getConceptId());
}
log.debug("requestConceptIds: ");
for (String i : requestConceptIds) {
log.debug("id: " + i);
}
} else {
originalConceptAnswers.clear();
}
setValue(originalConceptAnswers);
}
use of org.openmrs.ConceptAnswer in project openmrs-core by openmrs.
the class ConceptAnswersEditorTest method setAsText_shouldSetTheSortWeightsWithTheLeastPossibleChanges.
/**
* @see ConceptAnswersEditor#setAsText(String)
*/
@Test
public void setAsText_shouldSetTheSortWeightsWithTheLeastPossibleChanges() {
ConceptService service = Context.getConceptService();
Concept c = service.getConcept(21);
ConceptAnswersEditor editor = new ConceptAnswersEditor(c.getAnswers(true));
editor.setAsText("22 7 8");
// conceptId=7
ConceptAnswer ca1 = service.getConceptAnswer(1);
// conceptId=8
ConceptAnswer ca2 = service.getConceptAnswer(2);
// conceptId=22
ConceptAnswer ca3 = service.getConceptAnswer(3);
Concept cafter = service.getConcept(21);
Assert.assertEquals(3, cafter.getAnswers(true).size());
Assert.assertTrue(ca3.getSortWeight() < ca1.getSortWeight());
Assert.assertTrue(ca1.getSortWeight() < ca2.getSortWeight());
}
use of org.openmrs.ConceptAnswer in project openmrs-core by openmrs.
the class ConceptValidator method validate.
/**
* Checks that a given concept object is valid.
*
* @see org.springframework.validation.Validator#validate(java.lang.Object,
* org.springframework.validation.Errors)
* @should pass if the concept has at least one fully specified name added to it
* @should fail if there is a duplicate unretired concept name in the locale
* @should fail if there is a duplicate unretired preferred name in the same locale
* @should fail if there is a duplicate unretired fully specified name in the same locale
* @should fail if any names in the same locale for this concept are similar
* @should pass if the concept with a duplicate name is retired
* @should pass if the concept being validated is retired and has a duplicate name
* @should fail if any name is an empty string
* @should fail if the object parameter is null
* @should pass if the concept is being updated with no name change
* @should fail if any name is a null value
* @should not allow multiple preferred names in a given locale
* @should not allow multiple fully specified conceptNames in a given locale
* @should not allow multiple short names in a given locale
* @should not allow an index term to be a locale preferred name
* @should fail if there is no name explicitly marked as fully specified
* @should pass if the duplicate ConceptName is neither preferred nor fully Specified
* @should pass if the concept has a synonym that is also a short name
* @should fail if a term is mapped multiple times to the same concept
* @should not fail if a term has two new mappings on it
* @should fail if there is a duplicate unretired concept name in the same locale different than
* the system locale
* @should pass for a new concept with a map created with deprecated concept map methods
* @should pass for an edited concept with a map created with deprecated concept map methods
* @should pass validation if field lengths are correct
* @should fail validation if field lengths are not correct
* @should pass if fully specified name is the same as short name
* @should pass if different concepts have the same short name
* @should fail if the concept datatype is null
* @should fail if the concept class is null
*/
@Override
public void validate(Object obj, Errors errors) throws APIException, DuplicateConceptNameException {
if (obj == null || !(obj instanceof Concept)) {
throw new IllegalArgumentException("The parameter obj should not be null and must be of type" + Concept.class);
}
Concept conceptToValidate = (Concept) obj;
// no name to validate, but why is this the case?
if (conceptToValidate.getNames().isEmpty()) {
errors.reject("Concept.name.atLeastOneRequired");
return;
}
ValidationUtils.rejectIfEmpty(errors, "datatype", "Concept.datatype.empty");
ValidationUtils.rejectIfEmpty(errors, "conceptClass", "Concept.conceptClass.empty");
boolean hasFullySpecifiedName = false;
for (Locale conceptNameLocale : conceptToValidate.getAllConceptNameLocales()) {
boolean fullySpecifiedNameForLocaleFound = false;
boolean preferredNameForLocaleFound = false;
boolean shortNameForLocaleFound = false;
Set<String> validNamesFoundInLocale = new HashSet<>();
Collection<ConceptName> namesInLocale = conceptToValidate.getNames(conceptNameLocale);
for (ConceptName nameInLocale : namesInLocale) {
if (StringUtils.isBlank(nameInLocale.getName())) {
log.debug("Name in locale '" + conceptNameLocale.toString() + "' cannot be an empty string or white space");
errors.reject("Concept.name.empty");
}
if (nameInLocale.getLocalePreferred() != null) {
if (nameInLocale.getLocalePreferred() && !preferredNameForLocaleFound) {
if (nameInLocale.isIndexTerm()) {
log.warn("Preferred name in locale '" + conceptNameLocale.toString() + "' shouldn't be an index term");
errors.reject("Concept.error.preferredName.is.indexTerm");
} else if (nameInLocale.isShort()) {
log.warn("Preferred name in locale '" + conceptNameLocale.toString() + "' shouldn't be a short name");
errors.reject("Concept.error.preferredName.is.shortName");
} else if (nameInLocale.getVoided()) {
log.warn("Preferred name in locale '" + conceptNameLocale.toString() + "' shouldn't be a voided name");
errors.reject("Concept.error.preferredName.is.voided");
}
preferredNameForLocaleFound = true;
} else // should have one preferred name per locale
if (nameInLocale.getLocalePreferred() && preferredNameForLocaleFound) {
log.warn("Found multiple preferred names in locale '" + conceptNameLocale.toString() + "'");
errors.reject("Concept.error.multipleLocalePreferredNames");
}
}
if (nameInLocale.isFullySpecifiedName()) {
if (!hasFullySpecifiedName) {
hasFullySpecifiedName = true;
}
if (!fullySpecifiedNameForLocaleFound) {
fullySpecifiedNameForLocaleFound = true;
} else {
log.warn("Found multiple fully specified names in locale '" + conceptNameLocale.toString() + "'");
errors.reject("Concept.error.multipleFullySpecifiedNames");
}
if (nameInLocale.getVoided()) {
log.warn("Fully Specified name in locale '" + conceptNameLocale.toString() + "' shouldn't be a voided name");
errors.reject("Concept.error.fullySpecifiedName.is.voided");
}
}
if (nameInLocale.isShort()) {
if (!shortNameForLocaleFound) {
shortNameForLocaleFound = true;
} else // should have one short name per locale
{
log.warn("Found multiple short names in locale '" + conceptNameLocale.toString() + "'");
errors.reject("Concept.error.multipleShortNames");
}
}
// find duplicate names for a non-retired concept
if (Context.getConceptService().isConceptNameDuplicate(nameInLocale)) {
throw new DuplicateConceptNameException("'" + nameInLocale.getName() + "' is a duplicate name in locale '" + conceptNameLocale.toString() + "'");
}
//
if (errors.hasErrors()) {
log.debug("Concept name '" + nameInLocale.getName() + "' for locale '" + conceptNameLocale + "' is invalid");
// for different conceptNames
return;
}
// except for short names
if (!nameInLocale.isShort() && !validNamesFoundInLocale.add(nameInLocale.getName().toLowerCase())) {
throw new DuplicateConceptNameException("'" + nameInLocale.getName() + "' is a duplicate name in locale '" + conceptNameLocale.toString() + "' for the same concept");
}
if (log.isDebugEnabled()) {
log.debug("Valid name found: " + nameInLocale.getName());
}
}
}
// Ensure that each concept has at least a fully specified name
if (!hasFullySpecifiedName) {
log.debug("Concept has no fully specified name");
errors.reject("Concept.error.no.FullySpecifiedName");
}
if (CollectionUtils.isNotEmpty(conceptToValidate.getConceptMappings())) {
// validate all the concept maps
int index = 0;
Set<Integer> mappedTermIds = null;
for (ConceptMap map : conceptToValidate.getConceptMappings()) {
if (map.getConceptReferenceTerm().getConceptReferenceTermId() == null) {
// if this term is getting created on the fly e.g. from old legacy code, validate it
try {
errors.pushNestedPath("conceptMappings[" + index + "].conceptReferenceTerm");
ValidationUtils.invokeValidator(new ConceptReferenceTermValidator(), map.getConceptReferenceTerm(), errors);
} finally {
errors.popNestedPath();
}
}
// don't proceed to the next maps since the current one already has errors
if (errors.hasErrors()) {
return;
}
if (mappedTermIds == null) {
mappedTermIds = new HashSet<>();
}
// if we already have a mapping to this term, reject it this map
if (map.getConceptReferenceTerm().getId() != null && !mappedTermIds.add(map.getConceptReferenceTerm().getId())) {
errors.rejectValue("conceptMappings[" + index + "]", "ConceptReferenceTerm.term.alreadyMapped", "Cannot map a reference term multiple times to the same concept");
}
index++;
}
}
if (CollectionUtils.isNotEmpty(conceptToValidate.getAnswers())) {
for (ConceptAnswer conceptAnswer : conceptToValidate.getAnswers()) {
if (conceptAnswer.getAnswerConcept().equals(conceptToValidate)) {
errors.reject("Concept.contains.itself.as.answer");
}
}
}
ValidateUtil.validateFieldLengths(errors, obj.getClass(), "version", "retireReason");
super.validateAttributes(conceptToValidate, errors, Context.getConceptService().getAllConceptAttributeTypes());
}
use of org.openmrs.ConceptAnswer in project openmrs-core by openmrs.
the class ConceptValidatorTest method validate_shouldFailIfCodedConceptContainsItselfAsAnAnswer.
@Test
public void validate_shouldFailIfCodedConceptContainsItselfAsAnAnswer() {
Concept concept = conceptService.getConcept(30);
ConceptAnswer conceptAnswer = new ConceptAnswer(concept);
concept.addAnswer(conceptAnswer);
Errors errors = new BindException(concept, "concept");
validator.validate(concept, errors);
assertThat(errors, hasGlobalErrors("Concept.contains.itself.as.answer"));
}
use of org.openmrs.ConceptAnswer in project openmrs-core by openmrs.
the class ConceptServiceTest method saveConcept_shouldSetAuditInfoIfAnItemIsRemovedFromAnyOfItsChildCollections.
/**
* @see ConceptService#saveConcept(Concept)
*/
@Test
public void saveConcept_shouldSetAuditInfoIfAnItemIsRemovedFromAnyOfItsChildCollections() {
Concept concept = conceptService.getConcept(3);
Assert.assertNull(concept.getDateChanged());
Assert.assertNull(concept.getChangedBy());
Concept concept1 = conceptService.getConcept(5);
ConceptAnswer conceptanswer = new ConceptAnswer(concept1);
concept.addAnswer(conceptanswer);
conceptService.saveConcept(concept);
Assert.assertNotNull(concept.getDateChanged());
Date date = concept.getDateChanged();
Assert.assertTrue(concept.removeAnswer(conceptanswer));
conceptService.saveConcept(concept);
Assert.assertNotNull(concept.getDateChanged());
Date date1 = concept.getDateChanged();
Assert.assertFalse(date.equals(date1));
Assert.assertNotNull(concept.getChangedBy());
}
Aggregations