Skip to content

Commit

Permalink
GSoC 2019: Patient search criteria added in openmrs core
Browse files Browse the repository at this point in the history
  • Loading branch information
Reyano132 committed Aug 6, 2019
1 parent 4a0a626 commit 58a4ed8
Show file tree
Hide file tree
Showing 15 changed files with 1,016 additions and 5 deletions.
2 changes: 2 additions & 0 deletions api/src/main/java/org/openmrs/Patient.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.hibernate.annotations.SortNatural;
import org.hibernate.search.annotations.ContainedIn;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;

/**
* Defines a Patient in the system. A patient is simply an extension of a person and all that that
Expand All @@ -41,6 +42,7 @@
@Entity
@Table(name = "patient")
@PrimaryKeyJoinColumn(name = "patient_id")
@Indexed
public class Patient extends Person {

public static final long serialVersionUID = 93123L;
Expand Down
10 changes: 9 additions & 1 deletion api/src/main/java/org/openmrs/Person.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.hibernate.annotations.SortNatural;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.ContainedIn;
import org.hibernate.search.annotations.DateBridge;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Resolution;
import org.openmrs.util.OpenmrsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -65,6 +70,7 @@
@Entity
@Table(name = "person")
@Inheritance(strategy = InheritanceType.JOINED)
@Indexed
public class Person extends BaseChangeableOpenmrsData {

public static final long serialVersionUID = 2L;
Expand Down Expand Up @@ -102,10 +108,12 @@ public class Person extends BaseChangeableOpenmrsData {
@ContainedIn
private Set<PersonAttribute> attributes = null;

@Field
@Field(name="gender",index=Index.YES, analyze=Analyze.YES)
@Column(length = 50)
private String gender;

@Field(name="birthdate",index=Index.YES, analyze=Analyze.YES)
@DateBridge(resolution = Resolution.MILLISECOND)
@Column(name = "birthdate", length = 10)
private Date birthdate;

Expand Down
44 changes: 44 additions & 0 deletions api/src/main/java/org/openmrs/api/PatientService.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,50 @@ public interface PatientService extends OpenmrsService {
public List<Patient> getPatients(String name, String identifier, List<PatientIdentifierType> identifierTypes,
boolean matchIdentifierExactly) throws APIException;

/**
* Return the list of patient which has required name or identifier or gender or birthdate or
* age.
*
* @param name (optional) this is a slight break from the norm, patients with a partial match on
* this name will be returned
* @param identifier (optional) only patients with a matching identifier are returned. This
* however applies only if <code>name</code> argument is null. Otherwise, its
* ignored.
* @param identifierTypes (optional) the PatientIdentifierTypes to restrict to
* @param matchIdentifierExactly (required) if true, then the given <code>identifier</code> must
* equal the id in the database. if false, then the identifier is 'searched' for by
* using a regular expression
* @param gender(optional) : if user want to search patient by gender or filter the search
* result by gender. value of gender parameter is either "M" or "F".
* @param to(optional) : user wants to search patients having age between some range, at that
* time this paramater represent the upper boundary of range.
* @param from(optional):user wants to search patients having age between some range, at that
* time this paramater represent the lower boundary of range.
* @param birthdate(optional) : User can search patient by birthdate.
* @return patients that matched the given criteria (and are not voided)
* @throws APIException
* @should fetch all patients that partially match given name
* @should fetch all patients that partially match given identifier if <code>name</code>
* argument is null
* @should fetch all patients that partially match given identifier when match identifier
* exactly equals false and if <code>name</code> argument is null
* @should fetch all patients that exactly match given identifier when match identifier exactly
* equals true and if <code>name</code> argument is null
* @should fetch all patients that match given identifier types
* @should not return duplicates
* @should not return voided patients
* @should return empty list when no match is found
* @should search familyName2 with name
* @should support simple regex
* @should support pattern using last digit as check digit
* @should return empty list if name and identifier is empty
* @should support all the search criteria
*/
@Authorized({ PrivilegeConstants.GET_PATIENTS })
public List<Patient> getPatients(String name, String identifier, List<PatientIdentifierType> identifierTypes,
boolean matchIdentifierExactly, String gender, Integer from, Integer to, Date birthdate) throws APIException;


/**
* Void patient record (functionally delete patient from system). Voids Person and retires
* Users.
Expand Down
96 changes: 96 additions & 0 deletions api/src/main/java/org/openmrs/api/db/PatientDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package org.openmrs.api.db;

import java.util.Date;
import java.util.List;

import org.openmrs.Allergies;
Expand Down Expand Up @@ -297,4 +298,99 @@ public List<PatientIdentifierType> getPatientIdentifierTypes(String name, String
*/
public Allergy saveAllergy(Allergy allergy);


//Patient Search Criteria:
/**
* @param query : name or identifier of patients
* @param gender : gender of patients
* @param length : maximum number of patients should return
* @return : list of patients who follow the query regex and having required gender
* @throws DAOException
*/
public List<Patient> getPatientsByNameOrIdAndGender(String query, String gender, Integer start, Integer length, Boolean includeVoided)
throws DAOException;

/**
* @return : list of patient having required gender
* @throws DAOException
*/
public List<Patient> getPatientsByGender(String gender, Integer start, Integer length, Boolean includeVoided)
throws DAOException;

/**
* @param from: lower boundary of range of age
* @param to : upper boundary of range of age
* @return list of patients, who's age is in between the required range
* @throws DAOException
*/
public List<Patient> getPatientsByRangeOfAge(Date from, Date to, Integer start, Integer length, Boolean includeVoided)
throws DAOException;

/**
* @param birthdate : birthdate of patient
* @return list of patient/s , who's birthdate is similar to required birthdate
* @throws DAOException
*/
public List<Patient> getPatientsByBirthdate(Date birthdate, Integer start, Integer length, Boolean includeVoided)
throws DAOException;

/**
* @param query : name or identifier of patient/s.
* @param gender : gender of patient
* @param from : lower boundary of range of age
* @param to : upper boundary of range of age
* @return list of patients who follow the query regex and having required gender and having age
* in required range
* @throws DAOException
*/
public List<Patient> getPatientsByNameOrIdAndGenderAndRangeOfAge(String query, String gender, Date from, Date to, Integer start, Integer length,
Boolean includeVoided) throws DAOException;

/**
* @param query : name or identifier of patient/s
* @param gender : gender of patient
* @param birthdate : birthdate of patient
* @return list of patients who follow the query regex and having required gender and having
* required birthdate
* @throws DAOException
*/
public List<Patient> getPatientsByNameOrIdAndGenderAndBirthdate(String query, String gender, Date birthdate, Integer start, Integer length,
Boolean includeVoided) throws DAOException;

/**
* @param from: lower boundary of range of age
* @param to : upper boundary of range of age
* @return list of patients who follow the query regex and having age in required range
* @throws DAOException
*/
public List<Patient> getPatientsByNameOrIdAndRangeOfAge(String query, Date from, Date to, Integer start, Integer length, Boolean includeVoided)
throws DAOException;

/**
* @param birthdate: birthdate of patient
* @return list of patients who follow the query regex and having birthdate as required.
* @throws DAOException
*/
public List<Patient> getPatientsByNameOrIdAndBirthdate(String query, Date birthdate, Integer start, Integer length, Boolean includeVoided)
throws DAOException;

/**
* @param gender : gender of patients
* @param birthdate: birthdate of patient
* @return list of patients which have required birthdate and gender
* @throws DAOException
*/
public List<Patient> getPatientsByGenderAndBirthdate(String gender, Date birthdate, Integer start, Integer length,
Boolean includeVoided) throws DAOException;

/**
* @param gender : gender of patients
* @param from: lower boundary of range of age
* @param to : upper boundary of range of age
* @return list of patients which have required gender and age in required range.
* @throws DAOException
*/
public List<Patient> getPatientsByGenderAndAge(String gender, Date from, Date to, Integer start, Integer length,
Boolean includeVoided) throws DAOException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -813,6 +814,110 @@ public List<Patient> findPatients(String query, boolean includeVoided, Integer s

return patients;
}

//Patient search criteria functions:

@Override
public List<Patient> getPatientsByGender(String gender, Integer start, Integer length, Boolean includeVoided)
throws DAOException {

PatientLuceneQuery patientLuceneQuery = new PatientLuceneQuery(sessionFactory);
LuceneQuery<Person> genderQuery = patientLuceneQuery.getPatinetWithGender(gender, includeVoided);
return getPatientsWithLuceneQuery(genderQuery, start, length);
}

@Override
public List<Patient> getPatientsByRangeOfAge(Date from, Date to, Integer start, Integer length, Boolean includeVoided)
throws DAOException {

PatientLuceneQuery personLuceneQuery = new PatientLuceneQuery(sessionFactory);
LuceneQuery<Person> ageQuery = personLuceneQuery.getPatinetWithAgeRange(from, to, includeVoided);
return getPatientsWithLuceneQuery(ageQuery, start, length);
}

@Override
public List<Patient> getPatientsByBirthdate(Date birthdate, Integer start, Integer length, Boolean includeVoided)
throws DAOException {

PatientLuceneQuery personLuceneQuery = new PatientLuceneQuery(sessionFactory);
LuceneQuery<Person> birthdateQuery = personLuceneQuery.getPatinetWithBirthdate(birthdate, includeVoided);
return getPatientsWithLuceneQuery(birthdateQuery, start, length);
}

@Override
public List<Patient> getPatientsByNameOrIdAndGender(String query, String gender, Integer start, Integer length, Boolean includeVoided)
throws DAOException {
return findPatients(query, includeVoided, start, length).stream().filter(patient -> patient.getGender().equals(gender)).collect(Collectors.toList());
}

@Override
public List<Patient> getPatientsByNameOrIdAndRangeOfAge(String query, Date from, Date to, Integer start, Integer length,
Boolean includeVoided) throws DAOException {

PatientLuceneQuery patientLuceneQuery=new PatientLuceneQuery(sessionFactory);
LuceneQuery<Person> ageQuery=patientLuceneQuery.getPatinetWithAgeRange(from, to, includeVoided);
List<Patient> temp=getPatientsWithLuceneQuery(ageQuery,start,length);
return findPatients(query, includeVoided, start, length).stream().filter(patient -> temp.contains(patient)).collect(Collectors.toList());
}

@Override
public List<Patient> getPatientsByNameOrIdAndBirthdate(String query,Date birthdate, Integer start, Integer length,
Boolean includeVoided) throws DAOException {

PatientLuceneQuery patientLuceneQuery=new PatientLuceneQuery(sessionFactory);
LuceneQuery<Person> birthdateQuery=patientLuceneQuery.getPatinetWithBirthdate(birthdate, includeVoided);
List<Patient> temp=getPatientsWithLuceneQuery(birthdateQuery,start,length);
return findPatients(query, includeVoided, start, length).stream().filter(patient -> temp.contains(patient)).collect(Collectors.toList());
}

@Override
public List<Patient> getPatientsByNameOrIdAndGenderAndRangeOfAge(String query, String gender, Date from, Date to, Integer start, Integer length,
Boolean includeVoided) throws DAOException {
return getPatientsByNameOrIdAndRangeOfAge(query, from, to, start, length, includeVoided).stream().filter(patient -> patient.getGender().equals(gender)).collect(Collectors.toList());
}

@Override
public List<Patient> getPatientsByNameOrIdAndGenderAndBirthdate(String query, String gender, Date birthdate, Integer start, Integer length,
Boolean includeVoided) throws DAOException {
return getPatientsByNameOrIdAndBirthdate(query, birthdate, start, length, includeVoided).stream().filter(patient -> patient.getGender().equals(gender)).collect(Collectors.toList());
}

@Override
public List<Patient> getPatientsByGenderAndBirthdate(String gender, Date birthdate, Integer start, Integer length,
Boolean includeVoided) throws DAOException {
return getPatientsByBirthdate(birthdate, start, length, includeVoided).stream().filter(patient -> patient.getGender().equals(gender)).collect(Collectors.toList());
}

@Override
public List<Patient> getPatientsByGenderAndAge(String gender, Date from, Date to, Integer start, Integer length,
Boolean includeVoided) throws DAOException {
return getPatientsByRangeOfAge(from, to, start, length, includeVoided).stream().filter(patient -> patient.getGender().equals(gender)).collect(Collectors.toList());
}

private List<Patient> getPatientsWithLuceneQuery(LuceneQuery<Person> query, Integer start, Integer length){
Integer tmpStart = start;
if (tmpStart == null) {
tmpStart = 0;
}
Integer maxLength = HibernatePersonDAO.getMaximumSearchResults();
Integer tmpLength = length;
if (tmpLength == null || tmpLength > maxLength) {
tmpLength = maxLength;
}

List<Patient> patients = new LinkedList<>();

long querySize = query.resultSize();
if (querySize > tmpStart) {
ListPart<Object[]> tempPatients = query.listPartProjection(tmpStart, tmpLength, "personId");
tempPatients.getList().forEach(patient -> patients.add(getPatient((Integer) patient[0])));
}

return patients;


}

private LuceneQuery<PatientIdentifier> getPatientIdentifierLuceneQuery(String query, List<PatientIdentifierType> identifierTypes, boolean matchExactly) {
LuceneQuery<PatientIdentifier> patientIdentifierLuceneQuery = getPatientIdentifierLuceneQuery(query, matchExactly);
for(PatientIdentifierType identifierType : identifierTypes) {
Expand Down
Loading

0 comments on commit 58a4ed8

Please sign in to comment.