/**
* 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.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.Location;
import org.openmrs.Patient;
import org.openmrs.PatientIdentifier;
import org.openmrs.PatientIdentifierType;
import org.openmrs.PersonAddress;
import org.openmrs.api.APIAuthenticationException;
import org.openmrs.api.ConceptService;
import org.openmrs.api.DuplicateIdentifierException;
import org.openmrs.api.IdentifierNotUniqueException;
import org.openmrs.api.InsufficientIdentifiersException;
import org.openmrs.api.InvalidCheckDigitException;
import org.openmrs.api.InvalidIdentifierFormatException;
import org.openmrs.api.LocationService;
import org.openmrs.api.PatientIdentifierException;
import org.openmrs.api.PatientService;
import org.openmrs.api.context.Context;
import org.openmrs.patient.IdentifierValidator;
import org.openmrs.patient.UnallowedIdentifierException;
/**
* DWR patient methods. The methods in here are used in the webapp to get data from the database via
* javascript calls.
*
* @see PatientService
*/
public class DWRPatientService {
protected final Log log = LogFactory.getLog(getClass());
/**
* Search on the <code>searchValue</code>. If a number is in the search string, do an identifier
* search. Else, do a name search
*
* @see PatientService#getPatients(String)
* @param searchValue string to be looked for
* @param includeVoided true/false whether or not to included voided patients
* @return Collection<Object> of PatientListItem or String
* @should return only patient list items with nonnumeric search
* @should return string warning if invalid patient identifier
* @should not return string warning if searching with valid identifier
* @should include string in results if doing extra decapitated search
* @should not return duplicate patient list items if doing decapitated search
* @should not do decapitated search if numbers are in the search string
* @should get results for patients that have edited themselves
* @should logged in user should load their own patient object
*/
public Collection<Object> findPatients(String searchValue, boolean includeVoided) {
// the list to return
List<Object> patientList = new Vector<Object>();
PatientService ps = Context.getPatientService();
Collection<Patient> patients;
try {
patients = ps.getPatients(searchValue);
}
catch (APIAuthenticationException e) {
patientList.add("Error while attempting to find patients - " + e.getMessage());
return patientList;
}
patientList = new Vector<Object>(patients.size());
for (Patient p : patients)
patientList.add(new PatientListItem(p));
// if only 2 results found and a number was not in the
// search, then do a decapitated search: trim each word
// down to the first three characters and search again
if (patientList.size() < 3 && !searchValue.matches(".*\\d+.*")) {
String[] names = searchValue.split(" ");
String newSearch = "";
for (String name : names) {
if (name.length() > 3)
name = name.substring(0, 4);
newSearch += " " + name;
}
newSearch = newSearch.trim();
Collection<Patient> newPatients = ps.getPatients(newSearch);
newPatients.removeAll(patients); // remove the potential first two patients from these hits
if (newPatients.size() > 0) {
patientList.add("Minimal patients returned. Results for <b>" + newSearch + "</b>");
for (Patient p : newPatients) {
PatientListItem pi = new PatientListItem(p);
patientList.add(pi);
}
}
}
//no results found and a number was in the search --
//should check whether the check digit is correct.
else if (patientList.size() == 0 && searchValue.matches(".*\\d+.*")) {
//Looks through all the patient identifier validators to see if this type of identifier
//is supported for any of them. If it isn't, then no need to warn about a bad check
//digit. If it does match, then if any of the validators validates the check digit
//successfully, then the user is notified that the identifier has been entered correctly.
//Otherwise, the user is notified that the identifier was entered incorrectly.
Collection<IdentifierValidator> pivs = ps.getAllIdentifierValidators();
boolean shouldWarnUser = true;
boolean validCheckDigit = false;
boolean identifierMatchesValidationScheme = false;
for (IdentifierValidator piv : pivs) {
try {
if (piv.isValid(searchValue)) {
shouldWarnUser = false;
validCheckDigit = true;
}
identifierMatchesValidationScheme = true;
}
catch (UnallowedIdentifierException e) {}
}
if (identifierMatchesValidationScheme) {
if (shouldWarnUser)
patientList
.add("<p style=\"color:red; font-size:big;\"><b>WARNING: Identifier has been typed incorrectly! Please double check the identifier.</b></p>");
else if (validCheckDigit)
patientList
.add("<p style=\"color:green; font-size:big;\"><b>This identifier has been entered correctly, but still no patients have been found.</b></p>");
}
}
return patientList;
}
/**
* Convenience method for dwr/javascript to convert a patient id into a Patient object (or at
* least into data about the patient)
*
* @param patientId the {@link Patient#getPatientId()} to match on
* @return a truncated Patient object in the form of a PatientListItem
*/
public PatientListItem getPatient(Integer patientId) {
PatientService ps = Context.getPatientService();
Patient p = ps.getPatient(patientId);
PatientListItem pli = new PatientListItem(p);
if (p != null && p.getAddresses() != null && p.getAddresses().size() > 0) {
PersonAddress pa = (PersonAddress) p.getAddresses().toArray()[0];
pli.setAddress1(pa.getAddress1());
pli.setAddress2(pa.getAddress2());
}
return pli;
}
/**
* find all patients with duplicate attributes (searchOn)
*
* @param searchOn
* @return list of patientListItems
*/
public Vector<Object> findDuplicatePatients(String[] searchOn) {
Vector<Object> patientList = new Vector<Object>();
try {
List<String> options = new Vector<String>(searchOn.length);
for (String s : searchOn)
options.add(s);
List<Patient> patients = Context.getPatientService().getDuplicatePatientsByAttributes(options);
if (patients.size() > 200)
patients.subList(0, 200);
for (Patient p : patients)
patientList.add(new PatientListItem(p));
}
catch (Exception e) {
log.error(e);
patientList.add("Error while attempting to find duplicate patients - " + e.getMessage());
}
return patientList;
}
/**
* Auto generated method comment
*
* @param patientId
* @param identifierType
* @param identifier
* @param identifierLocationId
* @return
*/
public String addIdentifier(Integer patientId, String identifierType, String identifier, Integer identifierLocationId) {
String ret = "";
if (identifier == null || identifier.length() == 0)
return "PatientIdentifier.error.general";
PatientService ps = Context.getPatientService();
LocationService ls = Context.getLocationService();
Patient p = ps.getPatient(patientId);
PatientIdentifierType idType = ps.getPatientIdentifierTypeByName(identifierType);
//ps.updatePatientIdentifier(pi);
Location location = ls.getLocation(identifierLocationId);
log.debug("idType=" + identifierType + "->" + idType + " , location=" + identifierLocationId + "->" + location
+ " identifier=" + identifier);
PatientIdentifier id = new PatientIdentifier();
id.setIdentifierType(idType);
id.setIdentifier(identifier);
id.setLocation(location);
// in case we are editing, check to see if there is already an ID of this type and location
for (PatientIdentifier previousId : p.getActiveIdentifiers()) {
if (previousId.getIdentifierType().equals(idType) && previousId.getLocation().equals(location)) {
log.debug("Found equivalent ID: [" + idType + "][" + location + "][" + previousId.getIdentifier()
+ "], about to remove");
p.removeIdentifier(previousId);
} else {
if (!previousId.getIdentifierType().equals(idType))
log.debug("Previous ID id type does not match: [" + previousId.getIdentifierType().getName() + "]["
+ previousId.getIdentifier() + "]");
if (!previousId.getLocation().equals(location)) {
log.debug("Previous ID location is: " + previousId.getLocation());
log.debug("New location is: " + location);
}
}
}
p.addIdentifier(id);
try {
ps.savePatient(p);
}
catch (InvalidIdentifierFormatException iife) {
log.error(iife);
ret = "PatientIdentifier.error.formatInvalid";
}
catch (InvalidCheckDigitException icde) {
log.error(icde);
ret = "PatientIdentifier.error.checkDigit";
}
catch (IdentifierNotUniqueException inue) {
log.error(inue);
ret = "PatientIdentifier.error.notUnique";
}
catch (DuplicateIdentifierException die) {
log.error(die);
ret = "PatientIdentifier.error.duplicate";
}
catch (InsufficientIdentifiersException iie) {
log.error(iie);
ret = "PatientIdentifier.error.insufficientIdentifiers";
}
catch (PatientIdentifierException pie) {
log.error(pie);
ret = "PatientIdentifier.error.general";
}
return ret;
}
/**
* Auto generated method comment
*
* @param patientId
* @param reasonForExitId
* @param dateOfExit
* @param causeOfDeath
* @param otherReason
* @return
*/
public String exitPatientFromCare(Integer patientId, Integer exitReasonId, String exitDateStr,
Integer causeOfDeathConceptId, String otherReason) {
log.debug("Entering exitfromcare with [" + patientId + "] [" + exitReasonId + "] [" + exitDateStr + "]");
String ret = "";
PatientService ps = Context.getPatientService();
ConceptService cs = Context.getConceptService();
Patient patient = null;
try {
patient = ps.getPatient(patientId);
}
catch (Exception e) {
patient = null;
}
if (patient == null) {
ret = "Unable to find valid patient with the supplied identification information - cannot exit patient from care";
}
// Get the exit reason concept (if possible)
Concept exitReasonConcept = null;
try {
exitReasonConcept = cs.getConcept(exitReasonId);
}
catch (Exception e) {
exitReasonConcept = null;
}
// Exit reason error handling
if (exitReasonConcept == null) {
ret = "Unable to locate reason for exit in dictionary - cannot exit patient from care";
}
// Parse the exit date
Date exitDate = null;
if (exitDateStr != null) {
SimpleDateFormat sdf = Context.getDateFormat();
try {
exitDate = sdf.parse(exitDateStr);
}
catch (ParseException e) {
exitDate = null;
}
}
// Exit date error handling
if (exitDate == null) {
ret = "Invalid date supplied - cannot exit patient from care without a valid date.";
}
// If all data is provided as expected
if (patient != null && exitReasonConcept != null && exitDate != null) {
// need to check first if this is death or not
String patientDiedConceptId = Context.getAdministrationService().getGlobalProperty("concept.patientDied");
Concept patientDiedConcept = null;
if (patientDiedConceptId != null) {
patientDiedConcept = cs.getConcept(patientDiedConceptId);
}
// If there is a concept for death in the dictionary
if (patientDiedConcept != null) {
// If the exist reason == patient died
if (exitReasonConcept.equals(patientDiedConcept)) {
Concept causeOfDeathConcept = null;
try {
causeOfDeathConcept = cs.getConcept(causeOfDeathConceptId);
}
catch (Exception e) {
causeOfDeathConcept = null;
}
// Cause of death concept exists
if (causeOfDeathConcept != null) {
try {
ps.processDeath(patient, exitDate, causeOfDeathConcept, otherReason);
}
catch (Exception e) {
log.debug("Caught error", e);
ret = "Internal error while trying to process patient death - unable to proceed.";
}
}
// cause of death concept does not exist
else {
ret = "Unable to locate cause of death in dictionary - cannot proceed";
}
}
// Otherwise, we process this as an exit
else {
try {
ps.exitFromCare(patient, exitDate, exitReasonConcept);
}
catch (Exception e) {
log.debug("Caught error", e);
ret = "Internal error while trying to exit patient from care - unable to exit patient from care at this time.";
}
}
}
// If the system does not recognize death as a concept, then we exit from care
else {
try {
ps.exitFromCare(patient, exitDate, exitReasonConcept);
}
catch (Exception e) {
log.debug("Caught error", e);
ret = "Internal error while trying to exit patient from care - unable to exit patient from care at this time.";
}
}
log.debug("Exited from care, it seems");
}
return ret;
}
/**
* Auto generated method comment
*
* @param patientId
* @param locationId
* @return
*/
public String changeHealthCenter(Integer patientId, Integer locationId) {
log.warn("Deprecated method in 'DWRPatientService.changeHealthCenter'");
String ret = "";
/*
if ( patientId != null && locationId != null ) {
Patient patient = Context.getPatientService().getPatient(patientId);
Location location = Context.getEncounterService().getLocation(locationId);
if ( patient != null && location != null ) {
patient.setHealthCenter(location);
Context.getPatientService().updatePatient(patient);
}
}
*/
return ret;
}
}