org.openmrs.module.patientaccesscontrol.web.dwr.DWRModulePatientService.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.patientaccesscontrol.web.dwr.DWRModulePatientService.java

Source

/**
 * 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.module.patientaccesscontrol.web.dwr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.GlobalProperty;
import org.openmrs.Obs;
import org.openmrs.Patient;
import org.openmrs.User;
import org.openmrs.api.APIAuthenticationException;
import org.openmrs.api.APIException;
import org.openmrs.api.ConceptService;
import org.openmrs.api.GlobalPropertyListener;
import org.openmrs.api.ObsService;
import org.openmrs.api.PatientService;
import org.openmrs.api.context.Context;
import org.openmrs.module.patientaccesscontrol.PatientProgramModel;
import org.openmrs.module.patientaccesscontrol.UserPatient;
import org.openmrs.module.patientaccesscontrol.api.PatientAccessControlService;
import org.openmrs.module.patientaccesscontrol.api.RoleProgramService;
import org.openmrs.module.patientaccesscontrol.api.UserPatientService;
import org.openmrs.patient.IdentifierValidator;
import org.openmrs.patient.UnallowedIdentifierException;
import org.openmrs.util.OpenmrsConstants;
import org.openmrs.web.dwr.ObsListItem;

/**
 * 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 DWRModulePatientService implements GlobalPropertyListener {

    private static final Log log = LogFactory.getLog(DWRModulePatientService.class);

    private static Integer maximumResults;

    /**
     * Search on the <code>searchValue</code>. If a number is in the search string, do an identifier search. Else, do a
     * name search
     * 
     * @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> listPatients(String searchValue, boolean includeVoided) {
        return listBatchOfPatients(searchValue, new ArrayList<Integer>(), includeVoided, null, null);
    }

    /**
     * Search on the <code>searchValue</code>. If a number is in the search string, do an identifier search. Else, do a
     * name search
     * 
     * @see RoleProgramService#getPatients(String, int, Integer)
     * @param searchValue
     *            string to be looked for
     * @param includeVoided
     *            true/false whether or not to included voided patients
     * @param start
     *            The starting index for the results to return
     * @param length
     *            The number of results of return
     * @return Collection<Object> of PatientListItem or String
     * @since 1.8
     */
    public Collection<Object> listBatchOfPatients(String searchValue, List<Integer> conceptIds,
            boolean includeVoided, Integer start, Integer length) {
        if (maximumResults == null) {
            maximumResults = getMaximumSearchResults();
        }
        if (length != null && length > maximumResults) {
            length = maximumResults;
        }

        // the list to return
        List<Object> patientList = new Vector<Object>();

        PatientAccessControlService ps = Context.getService(PatientAccessControlService.class);
        ConceptService cs = Context.getConceptService();
        ObsService os = Context.getObsService();

        Collection<PatientProgramModel> patients;

        try {
            patients = ps.getPatientPrograms(searchValue, start, length);
        } catch (APIAuthenticationException e) {
            patientList.add(
                    Context.getMessageSourceService().getMessage("Patient.search.error") + " - " + e.getMessage());
            return patientList;
        }

        patientList = new Vector<Object>(patients.size());
        for (PatientProgramModel p : patients) {
            Map<Integer, ObsListItem> latestObs = new LinkedHashMap<Integer, ObsListItem>();
            for (Integer conceptId : conceptIds) {
                List<Obs> obs = os.getLastNObservations(1, p.getPatient(), cs.getConcept(conceptId), false);
                if (obs.isEmpty()) {
                    latestObs.put(conceptId, new ObsListItem());
                } else {
                    latestObs.put(conceptId, new ObsListItem(obs.get(0), Context.getLocale()));
                }
            }
            patientList.add(new ModulePatientListItem(p.getPatient(), p.getProgram(), searchValue, latestObs));
        }
        // no results found and a number was in the search --
        // should check whether the check digit is correct.
        if (patients.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 = Context.getPatientService().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;
    }

    /**
     * Returns a map of results with the values as count of matches and a partial list of the matching patients
     * (depending on values of start and length parameters) while the keys are are 'count' and 'objectList'
     * respectively, if the length parameter is not specified, then all matches will be returned from the start index if
     * specified.
     * 
     * @param searchValue
     *            patient name or identifier
     * @param start
     *            the beginning index
     * @param length
     *            the number of matching patients to return
     * @param getMatchCount
     *            Specifies if the count of matches should be included in the returned map
     * @return a map of results
     * @throws APIException
     * @since 1.8
     * @should signal for a new search if the new search value has matches and is a first call
     * @should not signal for a new search if it is not the first ajax call
     * @should not signal for a new search if the new search value has no matches
     * @should match patient with identifiers that contain no digit
     */
    public Map<String, Object> listCountAndPatients(String searchValue, List<Integer> conceptIds, Integer start,
            Integer length, boolean getMatchCount) throws APIException {

        // Map to return
        Map<String, Object> resultsMap = new HashMap<String, Object>();
        Collection<Object> objectList = new Vector<Object>();
        try {
            PatientAccessControlService ps = Context.getService(PatientAccessControlService.class);
            int patientCount = 0;
            // if this is the first call
            if (getMatchCount) {
                patientCount += ps.getCountOfPatientPrograms(searchValue);

                // if there are no results found and a number was not in the
                // search and this is the first call, then do a decapitated search:
                // trim each word down to the first three characters and search again
                if (patientCount == 0 && start == 0 && !searchValue.matches(".*\\d+.*")) {
                    String[] names = searchValue.split(" ");
                    String newSearch = "";
                    for (String name : names) {
                        if (name.length() > 3) {
                            name = name.substring(0, 3);
                        }
                        newSearch += " " + name;
                    }

                    newSearch = newSearch.trim();
                    if (!newSearch.equals(searchValue)) {
                        newSearch = newSearch.trim();
                        int newPatientCount = ps.getCountOfPatients(newSearch);
                        if (newPatientCount > 0) {
                            // Send a signal to the core search widget to search again against newSearch
                            resultsMap.put("searchAgain", newSearch);
                            resultsMap.put("notification",
                                    Context.getMessageSourceService().getMessage("searchWidget.noResultsFoundFor",
                                            new Object[] { searchValue, newSearch }, Context.getLocale()));
                        }
                    }
                }

                // no results found and a number was in the search --
                // should check whether the check digit is correct.
                else if (patientCount == 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 = Context.getPatientService().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) {
                            resultsMap.put("notification", "<b>" + Context.getMessageSourceService()
                                    .getMessage("Patient.warning.inValidIdentifier") + "<b/>");
                        } else if (validCheckDigit) {
                            resultsMap.put("notification",
                                    "<b style=\"color:green;\">" + Context.getMessageSourceService()
                                            .getMessage("Patient.message.validIdentifier") + "<b/>");
                        }
                    }
                } else {
                    // ensure that count never exceeds this value because the API's service layer would never
                    // return more than it since it is limited in the DAO layer
                    if (maximumResults == null) {
                        maximumResults = getMaximumSearchResults();
                    }
                    if (length != null && length > maximumResults) {
                        length = maximumResults;
                    }

                    if (patientCount > maximumResults) {
                        patientCount = maximumResults;
                        if (log.isDebugEnabled()) {
                            log.debug("Limitng the size of matching patients to " + maximumResults);
                        }
                    }
                }

            }

            // if we have any matches or this isn't the first ajax call when the caller
            // requests for the count
            if (patientCount > 0 || !getMatchCount) {
                objectList = listBatchOfPatients(searchValue, conceptIds, false, start, length);
            }

            resultsMap.put("count", patientCount);
            resultsMap.put("objectList", objectList);
        } catch (Exception e) {
            log.error("Error while searching for patients", e);
            objectList.clear();
            objectList.add(
                    Context.getMessageSourceService().getMessage("Patient.search.error") + " - " + e.getMessage());
            resultsMap.put("count", 0);
            resultsMap.put("objectList", objectList);
        }
        return resultsMap;
    }

    @Override
    public boolean supportsPropertyName(String propertyName) {
        return propertyName.equals(OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS);
    }

    @Override
    public void globalPropertyChanged(GlobalProperty newValue) {
        try {
            maximumResults = Integer.valueOf(newValue.getPropertyValue());
        } catch (NumberFormatException e) {
            maximumResults = OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS_DEFAULT_VALUE;
        }
    }

    @Override
    public void globalPropertyDeleted(String propertyName) {
        maximumResults = OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS_DEFAULT_VALUE;
    }

    /**
     * Fetch the max results value from the global properties table
     * 
     * @return Integer value for the person search max results global property
     */
    private static Integer getMaximumSearchResults() {
        try {
            return Integer.valueOf(Context.getAdministrationService().getGlobalProperty(
                    OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS,
                    String.valueOf(OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS_DEFAULT_VALUE)));
        } catch (Exception e) {
            log.warn("Unable to convert the global property "
                    + OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS
                    + "to a valid integer. Returning the default "
                    + OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS_DEFAULT_VALUE);
        }

        return OpenmrsConstants.GLOBAL_PROPERTY_PERSON_SEARCH_MAX_RESULTS_DEFAULT_VALUE;
    }

    /**
     * Refer a patient to a user.
     * 
     * @param patientId
     * @param userId
     * @return
     */
    public String referPatientToUser(Integer patientId, Integer userId) {
        Patient patient = Context.getPatientService().getPatient(patientId);
        if (patient == null) {
            return "Unable to find valid patient with the supplied identification information - cannot refer patient";
        }
        User user = Context.getUserService().getUser(userId);
        if (user == null) {
            return "Unable to find valid user with the supplied identification information - cannot refer patient";
        }
        UserPatientService svc = Context.getService(UserPatientService.class);
        if (svc.getUserPatient(user, patient) == null) {
            UserPatient userPatient = new UserPatient();
            userPatient.setPatient(patient);
            userPatient.setUser(user);
            svc.saveUserPatient(userPatient);
        }

        return "Patient has been referred to user " + user.getPersonName().getFullName();
    }
}