Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: openmrs/openmrs-core
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 66d994e325a3
Choose a base ref
...
head repository: openmrs/openmrs-core
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fc49c704082f
Choose a head ref
  • 2 commits
  • 6 files changed
  • 2 contributors

Commits on Jul 22, 2013

  1. Copy the full SHA
    b21b6c7 View commit details
  2. Merge pull request #366 from rowanseymour/master

    TRUNK-3589: Add LocationService method to search for a location by a location attribute
    dkayiwa committed Jul 22, 2013
    Copy the full SHA
    fc49c70 View commit details
25 changes: 25 additions & 0 deletions api/src/main/java/org/openmrs/api/LocationService.java
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
package org.openmrs.api;

import java.util.List;
import java.util.Map;

import org.openmrs.Address;
import org.openmrs.Location;
@@ -155,17 +156,41 @@ public interface LocationService extends OpenmrsService {
* returned if there are no locations. Search is case insensitive. matching this
* <code>nameFragment</code>. If start and length are not specified, then all matches are
* returned
*
* @deprecated replaced by {@link LocationService#getLocations(String, org.openmrs.Location, java.util.Map, boolean, Integer, Integer)}
*
* @param nameFragment is the string used to search for locations
* @param includeRetired Specifies if retired locations should be returned
* @param start the beginning index
* @param length the number of matching locations to return
* @since 1.8
*/
@Deprecated
@Authorized( { PrivilegeConstants.GET_LOCATIONS })
public List<Location> getLocations(String nameFragment, boolean includeRetired, Integer start, Integer length)
throws APIException;

/**
* Gets the locations matching the specified arguments. A null list will never be returned. An empty list will be
* returned if there are no locations. Search is case insensitive. matching this <code>nameFragment</code>. If start
* and length are not specified, then all matches are returned.
*
* @param nameFragment is the string used to search for locations
* @param parent only return children of this parent
* @param attributeValues the attribute values
* @param includeRetired specifies if retired locations should also be returned
* @param start the beginning index
* @param length the number of matching locations to return
* @return the list of locations
* @should return empty list when no location has matching attribute values
* @should get locations having all matching attribute values
* @since 1.10
*/
@Authorized( { PrivilegeConstants.GET_LOCATIONS })
public List<Location> getLocations(String nameFragment, Location parent,
Map<LocationAttributeType, Object> attributeValues, boolean includeRetired, Integer start, Integer length)
throws APIException;

/**
* Returns locations that contain the given tag.
*
18 changes: 13 additions & 5 deletions api/src/main/java/org/openmrs/api/db/LocationDAO.java
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
package org.openmrs.api.db;

import java.util.List;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.openmrs.Location;
@@ -68,12 +69,19 @@ public interface LocationDAO {
public List<Location> getAllLocations(boolean includeRetired);

/**
* Returns a specified number of locations starting with a given string from the specified index
*
* @see LocationService#getLocations(String, boolean, Integer, Integer)
* Gets the locations matching the specified arguments
*
* @param nameFragment is the string used to search for locations
* @param parent only return children of this parent
* @param serializedAttributeValues the serialized attribute values
* @param includeRetired specifies if retired locations should also be returned
* @param start the beginning index
* @param length the number of matching locations to return
* @return the list of locations
*/
public List<Location> getLocations(String nameFragment, boolean includeRetired, Integer start, Integer length)
throws DAOException;
public List<Location> getLocations(String nameFragment, Location parent,
Map<LocationAttributeType, String> serializedAttributeValues, boolean includeRetired, Integer start,
Integer length) throws DAOException;

/**
* Completely remove the location from the database.
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
package org.openmrs.api.db.hibernate;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
@@ -202,19 +203,29 @@ public Long getCountOfLocations(String nameFragment, Boolean includeRetired) {
}

/**
* @see LocationDAO#getLocations(String, Integer, Integer)
* @see LocationDAO#getLocations(String, org.openmrs.Location, java.util.Map, boolean, Integer, Integer)
*/
@SuppressWarnings("unchecked")
@Override
public List<Location> getLocations(String nameFragment, boolean includeRetired, Integer start, Integer length)
throws DAOException {
public List<Location> getLocations(String nameFragment, Location parent,
Map<LocationAttributeType, String> serializedAttributeValues, boolean includeRetired, Integer start,
Integer length) {

Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Location.class);
if (!includeRetired)
criteria.add(Restrictions.eq("retired", false));

if (StringUtils.isNotBlank(nameFragment))
if (StringUtils.isNotBlank(nameFragment)) {
criteria.add(Restrictions.ilike("name", nameFragment, MatchMode.START));
}

if (parent != null) {
criteria.add(Restrictions.eq("parentLocation", parent));
}

if (serializedAttributeValues != null) {
HibernateUtil.addAttributeCriteria(criteria, serializedAttributeValues);
}

if (!includeRetired)
criteria.add(Restrictions.eq("retired", false));

criteria.addOrder(Order.asc("name"));
if (start != null)
33 changes: 33 additions & 0 deletions api/src/main/java/org/openmrs/api/db/hibernate/HibernateUtil.java
Original file line number Diff line number Diff line change
@@ -15,14 +15,23 @@

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.openmrs.Location;
import org.openmrs.attribute.AttributeType;

/**
* This class holds common methods and utilities that are used across the hibernate related classes
@@ -108,4 +117,28 @@ public static String escapeSqlWildcards(String oldString, Connection connection)
return oldString;
}

/**
* Adds attribute value criteria to the given criteria query
* @param criteria the criteria
* @param serializedAttributeValues the serialized attribute values
* @param <AT> the attribute type
*/
public static <AT extends AttributeType> void addAttributeCriteria(Criteria criteria,
Map<AT, String> serializedAttributeValues) {
Conjunction conjunction = Restrictions.conjunction();
int a = 0;

for (Map.Entry<AT, String> entry : serializedAttributeValues.entrySet()) {
String alias = "attributes" + (a++);
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Location.class).setProjection(Projections.id());
detachedCriteria.createAlias("attributes", alias);
detachedCriteria.add(Restrictions.eq(alias + ".attributeType", entry.getKey()));
detachedCriteria.add(Restrictions.eq(alias + ".valueReference", entry.getValue()));
detachedCriteria.add(Restrictions.eq(alias + ".voided", false));

conjunction.add(Property.forName("id").in(detachedCriteria));
}

criteria.add(conjunction);
}
}
19 changes: 17 additions & 2 deletions api/src/main/java/org/openmrs/api/impl/LocationServiceImpl.java
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.openmrs.Address;
import org.openmrs.Location;
@@ -175,7 +176,7 @@ public List<Location> getAllLocations(boolean includeRetired) throws APIExceptio
*/
@Transactional(readOnly = true)
public List<Location> getLocations(String nameFragment) throws APIException {
return getLocations(nameFragment, false, null, null);
return getLocations(nameFragment, null, null, false, null, null);
}

/**
@@ -345,10 +346,24 @@ public Integer getCountOfLocations(String nameFragment, Boolean includeRetired)
* @see LocationService#getLocations(String, boolean, Integer, Integer)
*/
@Override
@Deprecated
@Transactional(readOnly = true)
public List<Location> getLocations(String nameFragment, boolean includeRetired, Integer start, Integer length)
throws APIException {
return dao.getLocations(nameFragment, includeRetired, start, length);
return dao.getLocations(nameFragment, null, null, includeRetired, start, length);
}

/**
* @see LocationService#getLocations(String, org.openmrs.Location, java.util.Map, boolean, Integer, Integer)
*/
@Override
public List<Location> getLocations(String nameFragment, Location parent,
Map<LocationAttributeType, Object> attributeValues, boolean includeRetired, Integer start, Integer length) {

Map<LocationAttributeType, String> serializedAttributeValues = CustomDatatypeUtil
.getValueReferences(attributeValues);

return dao.getLocations(nameFragment, parent, serializedAttributeValues, includeRetired, start, length);
}

/**
75 changes: 75 additions & 0 deletions api/src/test/java/org/openmrs/api/LocationServiceTest.java
Original file line number Diff line number Diff line change
@@ -22,13 +22,16 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openmrs.GlobalProperty;
import org.openmrs.Location;
import org.openmrs.LocationAttribute;
import org.openmrs.LocationAttributeType;
import org.openmrs.LocationTag;
import org.openmrs.api.context.Context;
@@ -302,6 +305,78 @@ public void getLocations_shouldReturnEmptyListWhenNoLocationMatchTheNameFragment
Assert.assertEquals(0, Context.getLocationService().getLocations("Mansion").size());
}

/**
* @see LocationService#getLocations(String, org.openmrs.Location, java.util.Map, boolean, Integer, Integer)
*/
@Test
@Verifies(value = "should return empty list when no location has matching attribute values", method = "getLocations(String,Location,Map,boolean,Integer,Integer)")
public void getLocations_shouldNotFindAnyLocationsIfNoneHaveGivenAttributeValues() {
// Save new phone number attribute type
LocationAttributeType phoneAttrType = new LocationAttributeType();
phoneAttrType.setName("Facility Phone");
phoneAttrType.setMinOccurs(0);
phoneAttrType.setMaxOccurs(1);
phoneAttrType.setDatatypeClassname("org.openmrs.customdatatype.datatype.FreeTextDatatype");
Context.getLocationService().saveLocationAttributeType(phoneAttrType);

Map<LocationAttributeType, Object> attrValues = new HashMap<LocationAttributeType, Object>();
attrValues.put(phoneAttrType, "xxxxxx");
Assert.assertEquals(0, Context.getLocationService().getLocations(null, null, attrValues, true, null, null).size());
}

/**
* @see LocationService#getLocations(String, org.openmrs.Location, java.util.Map, boolean, Integer, Integer)
*/
@Test
@Verifies(value = "should get locations having all matching attribute values", method = "getLocations(String,Location,Map,boolean,Integer,Integer)")
public void getLocations_shouldGetLocationsHavingAllMatchingAttributeValues() {
// Save new phone number attribute type
LocationAttributeType phoneAttrType = new LocationAttributeType();
phoneAttrType.setName("Facility Phone");
phoneAttrType.setMinOccurs(0);
phoneAttrType.setMaxOccurs(1);
phoneAttrType.setDatatypeClassname("org.openmrs.customdatatype.datatype.FreeTextDatatype");
Context.getLocationService().saveLocationAttributeType(phoneAttrType);

// Save new email address attribute type
LocationAttributeType emailAttrType = new LocationAttributeType();
emailAttrType.setName("Facility Email");
emailAttrType.setMinOccurs(0);
emailAttrType.setMaxOccurs(1);
emailAttrType.setDatatypeClassname("org.openmrs.customdatatype.datatype.FreeTextDatatype");
Context.getLocationService().saveLocationAttributeType(emailAttrType);

// Assign phone number 0123456789 and email address admin@facility.com to location #1
Location location1 = Context.getLocationService().getLocation(1);
LocationAttribute la1a = new LocationAttribute();
la1a.setAttributeType(phoneAttrType);
la1a.setValue("0123456789");
location1.addAttribute(la1a);
LocationAttribute la1b = new LocationAttribute();
la1b.setAttributeType(emailAttrType);
la1b.setValue("admin@facility.com");
location1.addAttribute(la1b);
Context.getLocationService().saveLocation(location1);

// Assign same phone number 0123456789 to location #2
Location location2 = Context.getLocationService().getLocation(2);
LocationAttribute la2 = new LocationAttribute();
la2.setAttributeType(phoneAttrType);
la2.setValue("0123456789");
location2.addAttribute(la2);
Context.getLocationService().saveLocation(location2);

// Search for location #1 by phone number AND email address
Map<LocationAttributeType, Object> attrValues = new HashMap<LocationAttributeType, Object>();
attrValues.put(phoneAttrType, "0123456789");
attrValues.put(emailAttrType, "admin@facility.com");

// Check that only location #1 is returned
List<Location> locations = Context.getLocationService().getLocations(null, null, attrValues, false, null, null);
Assert.assertEquals(1, locations.size());
Assert.assertEquals(location1, locations.get(0));
}

/**
* Get locations that have a specified tag among its child tags.
*