org.openmrs.module.patientflags.evaluator.SQLFlagEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.patientflags.evaluator.SQLFlagEvaluator.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.patientflags.evaluator;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Cohort;
import org.openmrs.Patient;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.module.patientflags.Flag;
import org.openmrs.module.patientflags.FlagValidationResult;

/**
 * A FlagEvaluator that takes SQL statement as it's criteria. The SQL statement must contain the
 * string "*.patient_id" so that the evaluator knows how to modify the SQL statement to operate on a
 * single patient when required.
 */
public class SQLFlagEvaluator implements FlagEvaluator {

    private Log log = LogFactory.getLog(this.getClass());

    /**
     * @see org.openmrs.module.patientflags.evaluator.FlagEvaluator#eval(Flag, Patient)
     */
    public Boolean eval(Flag flag, Patient patient) {

        if (patient.isVoided())
            throw new APIException("Unable to evaluate SQL flag " + flag.getName() + " against voided patient");

        String criteria = flag.getCriteria();

        // pull out the "*.patient_id" clause
        // is this robust enough?
        Matcher matcher = Pattern.compile("(\\w+\\.patient_id)").matcher(criteria);
        matcher.find(); // just check for the first occurrence of the pattern... is this enough?
        String patientIdColumn = matcher.group();

        // since we are going to append a where/and to the end of this sql statement, we need to trim off trailing ";" and any trailing whitespace
        matcher = Pattern.compile(";?\\s*$").matcher(criteria);
        criteria = matcher.replaceFirst(""); // replace first, because there should only be one occurrence

        // create the criteria for a single patient by appending a "where" or "and" clause
        String toEval = criteria + (criteria.matches("(?i)(?s).*where.*") ? " and " : " where ") + patientIdColumn
                + " = " + patient.getPatientId();

        try {
            Context.addProxyPrivilege("SQL Level Access");
            List<List<Object>> resultSet = Context.getAdministrationService().executeSQL(toEval, true);
            // if the list is empty, return false, otherwise, return true
            return !resultSet.isEmpty();
        } catch (Exception e) {
            throw new APIException("Unable to evaluate SQL Flag " + flag.getName() + ", " + e.getLocalizedMessage(),
                    e);
        } finally {
            Context.removeProxyPrivilege("SQL Level Access");
        }
    }

    /**
     * @see org.openmrs.module.patientflags.evaluator.FlagEvaluator#eval(Flag, Cohort)
     */
    public Cohort evalCohort(Flag flag, Cohort cohort) {

        List<List<Object>> resultSet;

        try {
            Context.addProxyPrivilege("SQL Level Access");
            resultSet = Context.getAdministrationService().executeSQL(flag.getCriteria(), true);
        } catch (Exception e) {
            throw new APIException("Unable to evaluate SQL Flag " + flag.getName() + ", " + e.getLocalizedMessage(),
                    e);
        } finally {
            Context.removeProxyPrivilege("SQL Level Access");
        }

        Cohort resultCohort = new Cohort();

        for (List<Object> row : resultSet) {
            Integer patient_id = (Integer) row.get(0);

            // only add patients that haven't been voided
            if (!Context.getPatientService().getPatient(patient_id).isVoided())
                resultCohort.addMember((Integer) row.get(0));
        }

        if (cohort != null) {
            resultCohort = Cohort.intersect(resultCohort, cohort);
        }

        return resultCohort;
    }

    /**
     * @see org.openmrs.module.patientflags.evaluator.FlagEvaluator#validate(Flag)
     */
    public FlagValidationResult validate(Flag flag) {

        String criteria = flag.getCriteria();

        // if the *.patient_id pattern cannot be found, reject the criteria
        Matcher matcher = Pattern.compile("(\\w+\\.patient_id)").matcher(criteria);
        if (!matcher.find())
            return new FlagValidationResult(false, "patientflags.errors.noPatientIdCriteria");

        // try to execute the query, if it throws an exception, fail
        try {
            // note that unlike the two eval methods, we don't proxy SQL Level Access privilege here
            // because we don't want users without SQL Level Access to be able to create SQL Flags
            Context.getAdministrationService().executeSQL(criteria, true);
        } catch (Exception e) {
            return new FlagValidationResult(false, e.getLocalizedMessage());
        }

        // if we've gotten this far, mark the criteria as valid
        return new FlagValidationResult(true);
    }

    @Override
    public String evalMessage(Flag flag, int patientId) {
        String literal = "\\$\\{\\d{1,2}\\}";
        String message = flag.getMessage();

        if (!message.matches(".*(" + literal + ")+.*")) {
            return message;
        }

        log.info("Replacing values in " + message);

        Patient p = Context.getPatientService().getPatient(patientId);
        if (p.isVoided())
            throw new APIException("VOIDED PATIENT");

        String criteria = flag.getCriteria();

        // pull out the "*.patient_id" clause
        // is this robust enough?
        Matcher matcher = Pattern.compile("(\\w+\\.patient_id)").matcher(criteria);
        matcher.find(); // just check for the first occurrence of the pattern... is this enough?
        String patientIdColumn = matcher.group();

        // since we are going to append a where/and to the end of this sql statement, we need to trim off trailing ";" and any trailing whitespace
        matcher = Pattern.compile(";?\\s*$").matcher(criteria);
        criteria = matcher.replaceFirst(""); // replace first, because there should only be one occurrence

        // create the criteria for a single patient by appending a "where" or "and" clause
        String toEval = criteria + (criteria.matches("(?i)(?s).*where.*") ? " and " : " where ") + patientIdColumn
                + " = " + p.getPatientId();

        try {
            Context.addProxyPrivilege("SQL Level Access");
            List<List<Object>> resultSet = Context.getAdministrationService().executeSQL(toEval, true);
            // list would for sure contain one only one patient
            if (!resultSet.isEmpty()) {// empty resultset means no one matched the criteria
                Matcher m = Pattern.compile(literal).matcher(message);
                while (!m.hitEnd() && m.find()) {// replace each instance until end
                    String replaceString = m.group();
                    try {
                        //get index between the brackets ${indexNumber}
                        int index = Integer.parseInt(replaceString.replace("${", "").replace("}", ""));
                        if (index < resultSet.get(0).size()) {// do nothing if index is invalid
                            message = message.replace(replaceString, resultSet.get(0).get(index).toString());
                            log.info("Replaced " + replaceString + " ON " + index);
                        }
                    } catch (Exception e) {
                        //do nothing. text would remain unreplaced
                    }
                }
            } else {
                log.info("result set empty");
            }
        } catch (Exception e) {
            throw new APIException(
                    "Unable to evaluate SQL Flag Message" + flag.getName() + ", " + e.getLocalizedMessage(), e);
        } finally {
            Context.removeProxyPrivilege("SQL Level Access");
        }

        return message;
    }
}