/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.webservices.rest.web.v1_0.resource.openmrs1_8;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.openmrs.Person;
import org.openmrs.PersonAddress;
import org.openmrs.PersonAttribute;
import org.openmrs.PersonName;
import org.openmrs.api.context.Context;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.RequestContext;
import org.openmrs.module.webservices.rest.web.RestConstants;
import org.openmrs.module.webservices.rest.web.RestUtil;
import org.openmrs.module.webservices.rest.web.annotation.PropertyGetter;
import org.openmrs.module.webservices.rest.web.annotation.PropertySetter;
import org.openmrs.module.webservices.rest.web.annotation.Resource;
import org.openmrs.module.webservices.rest.web.representation.DefaultRepresentation;
import org.openmrs.module.webservices.rest.web.representation.FullRepresentation;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.openmrs.module.webservices.rest.web.resource.impl.DataDelegatingCrudResource;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
import org.openmrs.module.webservices.rest.web.resource.impl.NeedsPaging;
import org.openmrs.module.webservices.rest.web.response.ResourceDoesNotSupportOperationException;
import org.openmrs.module.webservices.rest.web.response.ResponseException;
import org.openmrs.util.OpenmrsUtil;
/**
* {@link Resource} for Person, supporting standard CRUD operations
*/
@Resource(name = RestConstants.VERSION_1 + "/person", order = 2, supportedClass = Person.class, supportedOpenmrsVersions = {
"1.8.*", "1.9.*", "1.10.*", "1.11.*", "1.12.*", "2.0.*", "2.1.*" })
//order must be greater than that for PatientResource(order=0) RESTWS-273
public class PersonResource1_8 extends DataDelegatingCrudResource<Person> {
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#getRepresentationDescription(org.openmrs.module.webservices.rest.web.representation.Representation)
*/
@Override
public DelegatingResourceDescription getRepresentationDescription(Representation rep) {
if (rep instanceof DefaultRepresentation) {
DelegatingResourceDescription description = new DelegatingResourceDescription();
description.addProperty("uuid");
description.addProperty("display");
description.addProperty("gender");
description.addProperty("age");
description.addProperty("birthdate");
description.addProperty("birthdateEstimated");
description.addProperty("dead");
description.addProperty("deathDate");
description.addProperty("causeOfDeath", Representation.REF);
description.addProperty("preferredName", "personName", Representation.REF);
description.addProperty("preferredAddress", "personAddress", Representation.REF);
description.addProperty("attributes", "activeAttributes", Representation.REF);
description.addProperty("voided");
description.addSelfLink();
description.addLink("full", ".?v=" + RestConstants.REPRESENTATION_FULL);
return description;
} else if (rep instanceof FullRepresentation) {
DelegatingResourceDescription description = new DelegatingResourceDescription();
description.addProperty("uuid");
description.addProperty("display");
description.addProperty("gender");
description.addProperty("age");
description.addProperty("birthdate");
description.addProperty("birthdateEstimated");
description.addProperty("dead");
description.addProperty("deathDate");
description.addProperty("causeOfDeath");
description.addProperty("preferredName", "personName", Representation.DEFAULT);
description.addProperty("preferredAddress", "personAddress", Representation.DEFAULT);
description.addProperty("names");
description.addProperty("addresses");
description.addProperty("attributes", "activeAttributes", Representation.DEFAULT);
description.addProperty("voided");
description.addProperty("auditInfo");
description.addSelfLink();
return description;
}
return null;
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.BaseDelegatingResource#getCreatableProperties()
*/
@Override
public DelegatingResourceDescription getCreatableProperties() {
DelegatingResourceDescription description = new DelegatingResourceDescription();
description.addRequiredProperty("names");
description.addRequiredProperty("gender");
description.addProperty("age");
description.addProperty("birthdate");
description.addProperty("birthdateEstimated");
description.addProperty("dead");
description.addProperty("deathDate");
description.addProperty("causeOfDeath");
description.addProperty("addresses");
description.addProperty("attributes");
return description;
}
/**
* @throws org.openmrs.module.webservices.rest.web.response.ResourceDoesNotSupportOperationException
* @see org.openmrs.module.webservices.rest.web.resource.impl.BaseDelegatingResource#getUpdatableProperties()
*/
@Override
public DelegatingResourceDescription getUpdatableProperties() throws ResourceDoesNotSupportOperationException {
DelegatingResourceDescription description = new DelegatingResourceDescription();
description.addProperty("age");
description.addProperty("gender");
description.addProperty("birthdate");
description.addProperty("birthdateEstimated");
description.addProperty("preferredName");
description.addProperty("preferredAddress");
description.addProperty("addresses");
description.addProperty("attributes");
description.addRequiredProperty("names");
description.addRequiredProperty("causeOfDeath");
description.addRequiredProperty("dead");
description.addProperty("deathDate");
return description;
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.BaseDelegatingResource#getPropertiesToExposeAsSubResources()
*/
@Override
public List<String> getPropertiesToExposeAsSubResources() {
return Arrays.asList("names", "addresses", "attributes");
}
/**
* Returns non-voided names for a person
*
* @param instance
* @return
*/
@PropertyGetter("names")
public static Set<PersonName> getNames(Person instance) {
return RestUtil.removeVoidedData(instance.getNames());
}
/**
* Sets names and marks the first one as preferred if none is marked. It also makes sure that
* only one name is marked as preferred and changes the rest to non-preferred.
* <p/>
* It takes the list so that the order is preserved.
*
* @param instance
* @param names
*/
@PropertySetter("names")
public static void setNames(Person instance, List<PersonName> names) {
for (PersonName existingName : instance.getNames()) {
existingName.setPreferred(false);
}
setFirstNameAsPreferred(names);
for (PersonName name : names) {
PersonName existingName = getMatchingName(name, instance.getNames());
if (existingName != null) {
copyNameFields(existingName, name);
} else {
instance.addName(name);
}
}
}
/**
* Returns non-voided attributes of a person
*
* @param instance
*/
@PropertyGetter("attributes")
public static List<PersonAttribute> getAttributes(Person instance) {
return instance.getActiveAttributes();
}
/**
* Sets attributes on the given person.
*
* @param instance
* @param attrs
*/
@PropertySetter("attributes")
public static void setAttributes(Person instance, List<PersonAttribute> attrs) {
for (PersonAttribute attr : attrs) {
PersonAttribute existingAttribute = instance.getAttribute(Context.getPersonService()
.getPersonAttributeTypeByUuid(attr.getAttributeType().getUuid()));
if (existingAttribute != null) {
if (attr.getValue() == null) {
instance.removeAttribute(existingAttribute);
} else {
existingAttribute.setValue(attr.getValue());
}
} else {
instance.addAttribute(attr);
}
}
}
/**
* Returns non-voided addresses for a person
*
* @param instance
* @return
*/
@PropertyGetter("addresses")
public static Set<PersonAddress> getAddresses(Person instance) {
return RestUtil.removeVoidedData(instance.getAddresses());
}
/**
* Sets addresses and marks the first one as preferred if none is marked. It also makes sure
* that only one address is marked as preferred and changes the rest to non-preferred.
* <p/>
* It takes the list so that the order is preserved.
*
* @param instance
* @param addresses
*/
@PropertySetter("addresses")
public static void setAddresses(Person instance, List<PersonAddress> addresses) {
for (PersonAddress existingAddress : instance.getAddresses()) {
existingAddress.setPreferred(false);
}
setFirstAddressAsPreferred(addresses);
for (PersonAddress address : addresses) {
PersonAddress existingAddress = getMatchingAddress(address, instance.getAddresses());
if (existingAddress != null) {
copyAddressFields(existingAddress, address);
} else {
instance.addAddress(address);
}
}
}
/**
* Sets the preferred name for a person. If no name exists new name is set as preferred.
*
* @param instance
* @param name
* @throws ResourceDoesNotSupportOperationException
*/
@PropertySetter("preferredName")
public static void setPreferredName(Person instance, PersonName name) throws ResourceDoesNotSupportOperationException {
if (name.getId() == null) {
throw new ResourceDoesNotSupportOperationException("Only an existing name can be marked as preferred!");
}
// switching which name is preferred
for (PersonName existing : instance.getNames()) {
if (existing.isPreferred() && !existing.equals(name))
existing.setPreferred(false);
}
name.setPreferred(true);
instance.addName(name);
}
@PropertyGetter("preferredName")
public static PersonName getPreferredName(Person instance) {
return instance.getPersonName();
}
@PropertySetter("age")
public static void setAge(Person person, Integer age) throws ResourceDoesNotSupportOperationException {
if (person.getBirthdate() == null && age != null) {
person.setBirthdateFromAge(age, new Date());
person.setBirthdateEstimated(true);
}
}
/**
* Sets the preferred address for a person. If no address exists new address is set as
* preferred.
*
* @param instance
* @param address
* @throws ResourceDoesNotSupportOperationException
*/
@PropertySetter("preferredAddress")
public static void setPreferredAddress(Person instance, PersonAddress address)
throws ResourceDoesNotSupportOperationException {
if (address.getPersonAddressId() == null) {
throw new ResourceDoesNotSupportOperationException("Only an existing address can be marked as preferred!");
}
//un mark the current preferred address as preferred if any
for (PersonAddress existing : instance.getAddresses()) {
if (existing.isPreferred() && !OpenmrsUtil.nullSafeEquals(existing, address))
existing.setPreferred(false);
}
address.setPreferred(true);
}
@PropertyGetter("preferredAddress")
public static PersonAddress getPreferredAddress(Person instance) {
return instance.getPersonAddress();
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#getByUniqueId(java.lang.String)
*/
@Override
public Person getByUniqueId(String uuid) {
return Context.getPersonService().getPersonByUuid(uuid);
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#newDelegate()
*/
@Override
public Person newDelegate() {
return new Person();
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#save(java.lang.Object)
*/
@Override
public Person save(Person person) {
return Context.getPersonService().savePerson(person);
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#doSearch(org.openmrs.module.webservices.rest.web.RequestContext)
*/
@Override
protected NeedsPaging<Person> doSearch(RequestContext context) {
return new NeedsPaging<Person>(Context.getPersonService().getPeople(context.getParameter("q"), null), context);
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#delete(java.lang.Object,
* java.lang.String, org.openmrs.module.webservices.rest.web.RequestContext)
*/
@Override
protected void delete(Person person, String reason, RequestContext context) throws ResponseException {
if (person.isVoided()) {
// DELETE is idempotent, so we return success here
return;
}
Context.getPersonService().voidPerson(person, reason);
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.impl.DelegatingCrudResource#purge(java.lang.Object,
* org.openmrs.module.webservices.rest.web.RequestContext)
*/
@Override
public void purge(Person person, RequestContext context) throws ResponseException {
if (person == null) {
// DELETE is idempotent, so we return success here
return;
}
Context.getPersonService().purgePerson(person);
}
/**
* @param person
* @return fullname (for concise display purposes)
*/
@PropertyGetter("display")
public String getDisplayString(Person person) {
// TODO copy what is done in PatientResource to use configured name layout
if (person.getPersonName() == null)
return "";
return person.getPersonName().getFullName();
}
private static void copyNameFields(PersonName existingName, PersonName personName) {
existingName.setPreferred(personName.getPreferred());
existingName.setPrefix(personName.getPrefix());
existingName.setGivenName(personName.getGivenName());
existingName.setMiddleName(personName.getMiddleName());
existingName.setFamilyNamePrefix(personName.getFamilyNamePrefix());
existingName.setFamilyName(personName.getFamilyName());
existingName.setFamilyName2(personName.getFamilyName2());
existingName.setFamilyNameSuffix(personName.getFamilyNameSuffix());
existingName.setDegree(personName.getDegree());
}
private static void setFirstNameAsPreferred(List<PersonName> personNames) {
boolean hasPreferred = false;
for (PersonName name : personNames) {
if (name.isPreferred()) {
if (!hasPreferred) {
hasPreferred = true;
} else {
name.setPreferred(false);
}
}
}
if (!hasPreferred) {
personNames.iterator().next().setPreferred(true);
}
}
private static PersonName getMatchingName(PersonName personName, Set<PersonName> personNames) {
for (PersonName existingName : personNames) {
String uuid = personName.getUuid();
if (uuid != null && uuid.equals(existingName.getUuid())) {
return existingName;
}
}
return null;
}
private static PersonAddress getMatchingAddress(PersonAddress personAddress, Set<PersonAddress> personAddresses) {
for (PersonAddress existingAddress : personAddresses) {
if (personAddress.getUuid().equals(existingAddress.getUuid())) {
return existingAddress;
}
}
return null;
}
private static void copyAddressFields(PersonAddress existingAddress, PersonAddress address) {
existingAddress.setPreferred(address.getPreferred());
existingAddress.setAddress1(address.getAddress1());
existingAddress.setAddress2(address.getAddress2());
existingAddress.setAddress3(address.getAddress3());
existingAddress.setAddress4(address.getAddress4());
existingAddress.setAddress5(address.getAddress5());
existingAddress.setAddress6(address.getAddress6());
existingAddress.setCityVillage(address.getCityVillage());
existingAddress.setCountry(address.getCountry());
existingAddress.setStateProvince(address.getStateProvince());
existingAddress.setCountyDistrict(address.getCountyDistrict());
existingAddress.setPostalCode(address.getPostalCode());
existingAddress.setLatitude(address.getLatitude());
existingAddress.setLongitude(address.getLongitude());
existingAddress.setDateCreated(address.getDateCreated());
}
private static void setFirstAddressAsPreferred(List<PersonAddress> addresses) {
boolean hasPreferred = false;
for (PersonAddress address : addresses) {
if (address.isPreferred()) {
if (!hasPreferred) {
hasPreferred = true;
} else {
address.setPreferred(false);
}
}
}
if (!hasPreferred) {
addresses.iterator().next().setPreferred(true);
}
}
/**
* Overrides the base getAuditInfo(delegate) since the dateCreated for person should get
* personDateCreated attribute
*
* @param person the delegate person
* @return audit information
* @throws Exception
*/
@PropertyGetter("auditInfo")
public SimpleObject getAuditInfo(Person person) throws Exception {
SimpleObject ret = super.getAuditInfo(person);
ret.put("dateCreated", ConversionUtil.convertToRepresentation(person.getPersonDateCreated(), Representation.DEFAULT));
return ret;
}
}