/**
* 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.controller.person;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Attributable;
import org.openmrs.Concept;
import org.openmrs.Obs;
import org.openmrs.Person;
import org.openmrs.PersonAddress;
import org.openmrs.PersonAttribute;
import org.openmrs.PersonAttributeType;
import org.openmrs.PersonName;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.propertyeditor.ConceptEditor;
import org.openmrs.util.OpenmrsConstants.PERSON_TYPE;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.validation.BindException;
import org.springframework.validation.ValidationUtils;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
/**
* This class controls the generic person properties (address, name, attributes). The Patient and
* User form controllers extend this class.
*
* @see org.openmrs.web.controller.patient.PatientFormController
*/
public class PersonFormController extends SimpleFormController {
/** Logger for this class and subclasses */
protected static final Log log = LogFactory.getLog(PersonFormController.class);
/**
* Allows for other Objects to be used as values in input tags. Normally, only strings and lists
* are expected
*
* @see org.springframework.web.servlet.mvc.BaseCommandController#initBinder(javax.servlet.http.HttpServletRequest,
* org.springframework.web.bind.ServletRequestDataBinder)
*/
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
binder.registerCustomEditor(java.lang.Integer.class, new CustomNumberEditor(java.lang.Integer.class, true));
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(Context.getDateFormat(), true));
binder.registerCustomEditor(org.openmrs.Concept.class, new ConceptEditor());
}
/**
* @see org.springframework.web.servlet.mvc.AbstractFormController#processFormSubmission(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse, java.lang.Object,
* org.springframework.validation.BindException)
*/
protected ModelAndView processFormSubmission(HttpServletRequest request, HttpServletResponse response, Object obj,
BindException errors) throws Exception {
Person person = (Person) obj;
if (!Context.isAuthenticated()) {
errors.reject("auth.invalid");
} else {
// Make sure they assign a name
if (person.getPersonName().getGivenName().trim().equals(""))
errors.rejectValue("names[0].givenName", "Person.name.required");
if (person.getPersonName().getFamilyName().trim().equals(""))
errors.rejectValue("names[0].familyName", "Person.name.required");
// Make sure they choose a gender
if (person.getGender() == null || person.getGender().equals(""))
errors.rejectValue("gender", "Person.gender.required");
// look for person attributes in the request and save to person
for (PersonAttributeType type : Context.getPersonService().getPersonAttributeTypes(PERSON_TYPE.PERSON, null)) {
String paramName = type.getPersonAttributeTypeId().toString();
String value = request.getParameter(paramName);
// if there is an error displaying the attribute, the value will be null
if (value != null) {
PersonAttribute attribute = new PersonAttribute(type, value);
try {
Object hydratedObject = attribute.getHydratedObject();
if (hydratedObject == null || "".equals(hydratedObject.toString())) {
// if null is returned, the value should be blanked out
attribute.setValue("");
} else if (hydratedObject instanceof Attributable) {
attribute.setValue(((Attributable) hydratedObject).serialize());
} else if (!hydratedObject.getClass().getName().equals(type.getFormat()))
// if the classes doesn't match the format, the hydration failed somehow
// TODO change the PersonAttribute.getHydratedObject() to not swallow all errors?
throw new APIException();
}
catch (APIException e) {
errors.rejectValue("attributes", "Invalid value for " + type.getName() + ": '" + value + "'");
log
.warn("Got an invalid value: " + value + " while setting personAttributeType id #"
+ paramName, e);
// setting the value to empty so that the user can reset the value to something else
attribute.setValue("");
}
person.addAttribute(attribute);
}
}
// check patients birthdate against future dates and really old dates
if (person.getBirthdate() != null) {
if (person.getBirthdate().after(new Date()))
errors.rejectValue("birthdate", "error.date.future");
else {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.YEAR, -120); // patient cannot be older than 120 years old
if (person.getBirthdate().before(c.getTime())) {
errors.rejectValue("birthdate", "error.date.nonsensical");
}
}
}
// Patient Info
//ValidationUtils.rejectIfEmptyOrWhitespace(errors, "birthdate", "error.null");
if (person.isPersonVoided())
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "voidReason", "error.null");
if (person.isDead() && (person.getCauseOfDeath() == null))
errors.rejectValue("causeOfDeath", "Patient.dead.causeOfDeathNull");
}
if (log.isDebugEnabled())
log.debug("Person Attributes: \n" + person.printAttributes());
return super.processFormSubmission(request, response, person, errors);
}
/**
* Setup the person object. Should be called by the
* PersonFormController.formBackingObject(request)
*
* @param person
* @return
*/
protected Person setupFormBackingObject(Person person) {
// set a default name and address for the person. This allows us to use person.names[0] binding in the jsp
if (person.getNames().size() < 1)
person.addName(new PersonName());
if (person.getAddresses().size() < 1)
person.addAddress(new PersonAddress());
// initialize the user/person sets
// hibernate seems to have an issue with empty lists/sets if they aren't initialized
person.getAttributes().size();
return person;
}
/**
* Setup the reference map object. Should be called by the
* PersonFormController.referenceData(...)
*
* @param person
* @return
*/
@SuppressWarnings("unchecked")
protected Map setupReferenceData(Map map, Person person) throws Exception {
String causeOfDeathOther = "";
if (Context.isAuthenticated()) {
String propCause = Context.getAdministrationService().getGlobalProperty("concept.causeOfDeath");
Concept conceptCause = Context.getConceptService().getConcept(propCause);
if (conceptCause != null) {
// TODO add back in for persons
List<Obs> obssDeath = Context.getObsService().getObservationsByPersonAndConcept(person, conceptCause);
if (obssDeath.size() == 1) {
Obs obsDeath = obssDeath.iterator().next();
causeOfDeathOther = obsDeath.getValueText();
if (causeOfDeathOther == null) {
log.debug("cod is null, so setting to empty string");
causeOfDeathOther = "";
} else {
log.debug("cod is valid: " + causeOfDeathOther);
}
} else {
log.debug("obssDeath is wrong size: " + obssDeath.size());
}
} else {
log.warn("No concept death cause found");
}
}
map.put("causeOfDeathOther", causeOfDeathOther);
return map;
}
/**
* Add the given name, gender, and birthdate/age to the given Person
*
* @param <P> Should be a Patient or User object
* @param person
* @param name
* @param gender
* @param date birthdate
* @param age
*/
public static <P extends Person> void getMiniPerson(P person, String name, String gender, String date, String age) {
person.addName(Context.getPersonService().parsePersonName(name));
person.setGender(gender);
Date birthdate = null;
boolean birthdateEstimated = false;
if (date != null && !date.equals("")) {
try {
// only a year was passed as parameter
if (date.length() < 5) {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, Integer.valueOf(date));
c.set(Calendar.MONTH, 0);
c.set(Calendar.DATE, 1);
birthdate = c.getTime();
birthdateEstimated = true;
}
// a full birthdate was passed as a parameter
else {
birthdate = Context.getDateFormat().parse(date);
birthdateEstimated = false;
}
}
catch (ParseException e) {
log.debug("Error getting date from birthdate", e);
}
} else if (age != null && !age.equals("")) {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
Integer d = c.get(Calendar.YEAR);
d = d - Integer.parseInt(age);
try {
birthdate = DateFormat.getDateInstance(DateFormat.SHORT).parse("01/01/" + d);
birthdateEstimated = true;
}
catch (ParseException e) {
log.debug("Error getting date from age", e);
}
}
if (birthdate != null)
person.setBirthdate(birthdate);
person.setBirthdateEstimated(birthdateEstimated);
}
}