/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.web.dwr;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptAnswer;
import org.openmrs.ConceptClass;
import org.openmrs.ConceptDatatype;
import org.openmrs.ConceptDescription;
import org.openmrs.ConceptName;
import org.openmrs.ConceptNumeric;
import org.openmrs.ConceptProposal;
import org.openmrs.ConceptSet;
import org.openmrs.ConceptWord;
import org.openmrs.Drug;
import org.openmrs.Field;
import org.openmrs.User;
import org.openmrs.api.APIException;
import org.openmrs.api.ConceptService;
import org.openmrs.api.FormService;
import org.openmrs.api.context.Context;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.util.OpenmrsUtil;
/**
* This class exposes some of the methods in org.openmrs.api.ConceptService via the dwr package
*/
public class DWRConceptService {
protected static final Log log = LogFactory.getLog(DWRConceptService.class);
/**
* Auto generated method comment
*
* @param phrase
* @param includeRetired
* @param includeClassNames
* @param excludeClassNames
* @param includeDatatypeNames
* @param excludeDatatypeNames
* @param includeDrugConcepts
* @return
*/
public List<Object> findConcepts(String phrase, boolean includeRetired, List<String> includeClassNames,
List<String> excludeClassNames, List<String> includeDatatypeNames,
List<String> excludeDatatypeNames, boolean includeDrugConcepts) {
// List to return
// Object type gives ability to return error strings
Vector<Object> objectList = new Vector<Object>();
// TODO add localization for messages
User currentUser = Context.getAuthenticatedUser();
Locale defaultLocale = Context.getLocale();
// get the list of locales to search on from the user's
// defined proficient locales (if applicable)
List<Locale> localesToSearchOn = null;
if (currentUser != null)
localesToSearchOn = currentUser.getProficientLocales();
if (localesToSearchOn == null)
// we're working with an anonymous user right now or
// with a user that has not defined any proficient locales
localesToSearchOn = new Vector<Locale>();
// add the user's locale
if (localesToSearchOn.size() == 0) {
localesToSearchOn.add(defaultLocale);
// if country is specified, also add the generic language locale
if (defaultLocale.getCountry() != "") {
localesToSearchOn.add(new Locale(defaultLocale.getLanguage()));
}
}
// debugging output
if (log.isDebugEnabled()) {
StringBuffer searchLocalesString = new StringBuffer();
for (Locale loc : localesToSearchOn) {
searchLocalesString.append(loc.toString() + " ");
}
log.debug("searching locales: " + searchLocalesString);
}
if (includeClassNames == null)
includeClassNames = new Vector<String>();
if (excludeClassNames == null)
excludeClassNames = new Vector<String>();
if (includeDatatypeNames == null)
includeDatatypeNames = new Vector<String>();
if (excludeDatatypeNames == null)
excludeDatatypeNames = new Vector<String>();
try {
ConceptService cs = Context.getConceptService();
List<ConceptWord> words = new Vector<ConceptWord>();
if (phrase.matches("\\d+")) {
// user searched on a number. Insert concept with
// corresponding conceptId
Concept c = cs.getConcept(Integer.valueOf(phrase));
if (c != null) {
ConceptName cn = c.getName(defaultLocale);
ConceptWord word = new ConceptWord(phrase, c, cn, defaultLocale, "Exact match on concept id: #" + phrase);
words.add(word);
}
}
if (phrase == null || phrase.equals("")) {
// TODO get all concepts for testing purposes?
} else {
// turn classnames into class objects
List<ConceptClass> includeClasses = new Vector<ConceptClass>();
for (String name : includeClassNames)
if (!"".equals(name))
includeClasses.add(cs.getConceptClassByName(name));
// turn classnames into class objects
List<ConceptClass> excludeClasses = new Vector<ConceptClass>();
for (String name : excludeClassNames)
if (!"".equals(name))
excludeClasses.add(cs.getConceptClassByName(name));
// turn classnames into class objects
List<ConceptDatatype> includeDatatypes = new Vector<ConceptDatatype>();
for (String name : includeDatatypeNames)
if (!"".equals(name))
includeDatatypes.add(cs.getConceptDatatypeByName(name));
// turn classnames into class objects
List<ConceptDatatype> excludeDatatypes = new Vector<ConceptDatatype>();
for (String name : excludeDatatypeNames)
if (!"".equals(name))
excludeDatatypes.add(cs.getConceptDatatypeByName(name));
// perform the search
words.addAll(cs.findConcepts(phrase, localesToSearchOn, includeRetired, includeClasses, excludeClasses,
includeDatatypes, excludeDatatypes));
}
if (words.size() == 0) {
objectList.add("No matches found for <b>" + phrase + "</b> in locale: "
+ OpenmrsUtil.join(localesToSearchOn, ", "));
} else {
int maxCount = 500;
int curCount = 0;
// turn words into concept list items
// if user wants drug concepts included, append those
for (ConceptWord word : words) {
if (++curCount > maxCount)
break;
objectList.add(new ConceptListItem(word));
// add drugs for concept if desired
if (includeDrugConcepts) {
Integer classId = word.getConcept().getConceptClass().getConceptClassId();
if (classId.equals(OpenmrsConstants.CONCEPT_CLASS_DRUG))
for (Drug d : cs.getDrugsByConcept(word.getConcept()))
objectList.add(new ConceptDrugListItem(d, defaultLocale)); // ABKTODO: using the default locale here may be improper
}
}
}
}
catch (Exception e) {
log.error("Error while finding concepts + " + e.getMessage(), e);
objectList.add("Error while attempting to find concepts - " + e.getMessage());
}
if (objectList.size() == 0)
objectList.add("No matches found for <b>" + phrase + "</b> in locale: " + defaultLocale);
return objectList;
}
/**
* Get a {@link ConceptListItem} by its internal database id.
*
* @param conceptId the id to look for
* @return a {@link ConceptListItem} or null if conceptId is not found
*/
public ConceptListItem getConcept(Integer conceptId) {
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
Concept c = cs.getConcept(conceptId);
if (c == null)
return null;
ConceptName cn = c.getName(locale);
return new ConceptListItem(c, cn, locale);
}
public List<ConceptListItem> findProposedConcepts(String text) {
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
List<Concept> concepts = cs.getProposedConcepts(text);
List<ConceptListItem> cli = new Vector<ConceptListItem>();
for (Concept c : concepts) {
ConceptName cn = c.getName(locale);
cli.add(new ConceptListItem(c, cn, locale));
}
return cli;
}
/**
* Find a list of {@link ConceptListItem} or {@link ConceptDrugListItem}s that are answers to
* the given question. The given question is determined by the given <code>conceptId</code>
*
* @param text the text to search for within the answers
* @param conceptId the conceptId of the question concept
* @param includeVoided (this argument is ignored now. searching for voided answers is not logical)
* @param includeDrugConcepts if true, drug concepts are searched too
* @return list of {@link ConceptListItem} or {@link ConceptDrugListItem} answers that match the
* query
* @throws Exception if given conceptId is not found
*/
public List<Object> findConceptAnswers(String text, Integer conceptId, boolean includeVoided, boolean includeDrugConcepts)
throws Exception {
if (includeVoided == true) throw new APIException("You should not include voideds in the search.");
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
Concept concept = cs.getConcept(conceptId);
if (concept == null)
throw new Exception("Unable to find a concept with id: " + conceptId);
List<ConceptWord> words = cs.getConceptAnswers(text, locale, concept);
List<Drug> drugAnswers = new Vector<Drug>();
for (ConceptAnswer conceptAnswer : concept.getAnswers()) {
if (conceptAnswer.getAnswerDrug() != null)
drugAnswers.add(conceptAnswer.getAnswerDrug());
}
List<Object> items = new Vector<Object>();
for (ConceptWord word : words) {
items.add(new ConceptListItem(word));
// add drugs for concept if desired
if (includeDrugConcepts) {
Integer classId = word.getConcept().getConceptClass().getConceptClassId();
if (classId.equals(OpenmrsConstants.CONCEPT_CLASS_DRUG))
for (Drug d : cs.getDrugsByConcept(word.getConcept())) {
if (drugAnswers.contains(d))
items.add(new ConceptDrugListItem(d, locale));
}
}
}
return items;
}
public List<Object> getConceptSet(Integer conceptId) {
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
FormService fs = Context.getFormService();
Concept concept = cs.getConcept(conceptId);
List<Object> returnList = new Vector<Object>();
if (concept.isSet()) {
for (ConceptSet set : concept.getConceptSets()) {
Field field = null;
ConceptName cn = set.getConcept().getName(locale);
ConceptDescription description = set.getConcept().getDescription(locale);
for (Field f : fs.getFieldsByConcept(set.getConcept())) {
if (f.getName().equals(cn.getName()) && f.getDescription().equals(description.getDescription())
&& f.isSelectMultiple().equals(false))
field = f;
}
if (field == null)
returnList.add(new ConceptListItem(set.getConcept(), cn, locale));
else
returnList.add(new FieldListItem(field, locale));
}
}
return returnList;
}
public List<ConceptListItem> getQuestionsForAnswer(Integer conceptId) {
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
Concept concept = cs.getConcept(conceptId);
List<Concept> concepts = cs.getConceptsByAnswer(concept);
List<ConceptListItem> items = new Vector<ConceptListItem>();
for (Concept c : concepts) {
ConceptName cn = c.getName(locale);
items.add(new ConceptListItem(c, cn, locale));
}
return items;
}
public ConceptDrugListItem getDrug(Integer drugId) {
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
Drug d = cs.getDrug(drugId);
return d == null ? null : new ConceptDrugListItem(d, locale);
}
public List<Object> getDrugs(Integer conceptId, boolean showConcept) {
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
Concept concept = cs.getConcept(conceptId);
List<Object> items = new Vector<Object>();
// Add this concept as the first option in the list
// If there are no drugs to choose from, this will be automatically
// selected
// by the openmrsSearch.fillTable(objs) function
if (showConcept == true) {
ConceptDrugListItem thisConcept = new ConceptDrugListItem(null, conceptId, concept.getName(locale, false)
.getName());
items.add(thisConcept);
}
// find drugs for this concept
List<Drug> drugs = null;
// if there are drugs to choose from, add some instructions
if (drugs.size() > 0 && showConcept == true)
items.add("Or choose a form of " + concept.getName(locale, false).getName());
// miniaturize our drug objects
for (Drug drug : drugs) {
items.add(new ConceptDrugListItem(drug, locale));
}
return items;
}
public List<Object> findDrugs(String phrase, boolean includeRetired) throws APIException{
if (includeRetired == true) throw new APIException("You should not include voideds in the search.");
Locale locale = Context.getLocale();
ConceptService cs = Context.getConceptService();
List<Object> items = new Vector<Object>();
// find drugs for this concept
List<Drug> drugs = cs.getDrugs(phrase);
// miniaturize our drug objects
for (Drug drug : drugs) {
items.add(new ConceptDrugListItem(drug, locale));
}
return items;
}
public boolean isValidNumericValue(Float value, Integer conceptId) {
ConceptNumeric conceptNumeric = Context.getConceptService().getConceptNumeric(conceptId);
return OpenmrsUtil.isValidNumericValue(value, conceptNumeric);
}
public String getConceptNumericUnits(Integer conceptId) {
ConceptNumeric conceptNumeric = Context.getConceptService().getConceptNumeric(conceptId);
return conceptNumeric.getUnits();
}
public List<ConceptListItem> getAnswersForQuestion(Integer conceptId) {
Vector<ConceptListItem> ret = new Vector<ConceptListItem>();
Concept c = Context.getConceptService().getConcept(conceptId);
Collection<ConceptAnswer> answers = c.getAnswers();
// TODO: deal with concept answers (e.g. drug) whose answer concept is null. (Not sure if this actually ever happens)
Locale locale = Context.getLocale();
for (ConceptAnswer ca : answers)
if (ca.getAnswerConcept() != null) {
ConceptName cn = ca.getAnswerConcept().getName(locale);
ret.add(new ConceptListItem(ca.getAnswerConcept(), cn, locale));
}
return ret;
}
}