femr.business.services.system.ResearchService.java Source code

Java tutorial

Introduction

Here is the source code for femr.business.services.system.ResearchService.java

Source

/*
 fEMR - fast Electronic Medical Records
 Copyright (C) 2014  Team fEMR
    
 fEMR is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
    
 fEMR is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
    
 You should have received a copy of the GNU General Public License
 along with fEMR.  If not, see <http://www.gnu.org/licenses/>. If
 you have any questions, contact <info@teamfemr.org>.
*/
package femr.business.services.system;

import com.avaje.ebean.Query;
import com.google.inject.Inject;
import femr.business.services.core.IResearchService;
import femr.business.helpers.DomainMapper;
import femr.business.helpers.QueryProvider;
import femr.common.dtos.ServiceResponse;
import femr.common.models.*;
import femr.ui.models.research.json.ResearchGraphDataItem;
import femr.data.daos.IRepository;
import femr.data.models.core.*;
import femr.data.models.mysql.Medication;
import femr.data.models.mysql.PatientEncounter;
import femr.data.models.mysql.PatientEncounterVital;
import femr.data.models.mysql.PatientPrescription;
import femr.util.calculations.dateUtils;
import femr.util.stringhelpers.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class ResearchService implements IResearchService {

    private final IRepository<IPatientEncounter> patientEncounterRepository;
    private final IRepository<IPatientEncounterVital> patientEncounterVitalRepository;
    protected final IRepository<IVital> vitalRepository;
    private final IRepository<IPatientPrescription> prescriptionRepository;
    private final IRepository<IMedication> medicationRepository;
    private final DomainMapper domainMapper;

    /**
     * Initializes the research service and injects the dependence
     */
    @Inject
    public ResearchService(IRepository<IPatientEncounter> patientEncounterRepository,
            IRepository<IPatientEncounterVital> patientEncounterVitaRepository, IRepository<IVital> vitalRepository,
            IRepository<IPatientPrescription> prescriptionRepository, IRepository<IMedication> medicationRepository,
            DomainMapper domainMapper) {

        this.patientEncounterRepository = patientEncounterRepository;
        this.patientEncounterVitalRepository = patientEncounterVitaRepository;
        this.vitalRepository = vitalRepository;
        this.prescriptionRepository = prescriptionRepository;
        this.medicationRepository = medicationRepository;
        this.domainMapper = domainMapper;
    }

    @Override
    public ServiceResponse<ResearchGraphDataItem> getGraphData(ResearchFilterItem filters) {

        ServiceResponse<ResearchGraphDataItem> response = new ServiceResponse<>();

        String primaryDatasetName = filters.getPrimaryDataset();
        //TODO: gender throws error here due to patients with no sex
        ResearchResult primaryItems = getDatasetItems(primaryDatasetName, filters);

        ResearchResult secondaryItems = new ResearchResult();
        String secondaryDatasetName = filters.getSecondaryDataset();
        if (!secondaryDatasetName.isEmpty()) {

            secondaryItems = getDatasetItems(secondaryDatasetName, filters);
        }

        try {
            ResearchGraphDataItem graphDataItem = createResearchGraphItem(primaryItems, secondaryItems, filters);
            response.setResponseObject(graphDataItem);
        } catch (Exception ex) {
            response.addError("exception", ex.getMessage());
        }

        return response;
    }

    @Override
    public ServiceResponse<Map<Integer, String>> getAllMedications() {

        ServiceResponse<Map<Integer, String>> response = new ServiceResponse<>();

        try {
            List<? extends IMedication> medications = medicationRepository.findAll(Medication.class);

            Map<Integer, String> medicationItems = new HashMap<>();

            for (IMedication medication : medications) {

                medicationItems.put(medication.getId(), medication.getName());
            }
            response.setResponseObject(medicationItems);

        } catch (Exception ex) {
            response.addError("exception", ex.getMessage());
        }

        return response;
    }

    public static ResearchGraphDataItem createResearchGraphItem(ResearchResult primaryResult,
            ResearchResult secondaryResult, ResearchFilterItem filters) {

        ResearchGraphDataItem graphModel = new ResearchGraphDataItem();
        List<ResearchItem> graphData = new ArrayList<>();
        Map<String, ResearchItem> groupedData = new HashMap<>();

        String yAxisTitle = "Number of Patients";
        String xAxisTitle = WordUtils.capitalize(StringUtils.splitCamelCase(primaryResult.getDataType()));
        String unitOfMeasurement = primaryResult.getUnitOfMeasurement();

        List<Float> sortedPrimary = new ArrayList<>();
        Map<Integer, Float> primaryDataset = primaryResult.getDataset();
        Map<Integer, Float> secondaryDataset = secondaryResult.getDataset();
        int sampleSize = primaryDataset.size();
        float total = 0;
        float rangeHigh = 0;
        float rangeLow = 10000;
        float median = 0;

        // Medications will not group correctly, just bundle and return
        if (filters.getPrimaryDataset().equals("prescribedMeds")
                || filters.getPrimaryDataset().equals("dispensedMeds")) {

            for (Integer key : primaryDataset.keySet()) {

                // Make sure all secondary keys are set for all items
                Float value = primaryDataset.get(key);

                ResearchItem currItem = new ResearchItem();
                currItem.setPrimaryName(Float.toString((float) key));
                currItem.setPrimaryValue(value);

                graphData.add(currItem);
            }

            graphModel.setAverage(0.0f);
            graphModel.setMedian(median);
            graphModel.setRangeLow(rangeLow);
            graphModel.setRangeHigh(rangeHigh);
            graphModel.setGraphData(graphData);
            graphModel.setPrimaryValuemap(primaryResult.getValueMap());
            graphModel.setSecondaryValuemap(secondaryResult.getValueMap());
            graphModel.setyAxisTitle(yAxisTitle);
            graphModel.setxAxisTitle(xAxisTitle);
            graphModel.setUnitOfMeasurement(unitOfMeasurement);
            return graphModel;
        }

        // total the individual patients based on their value
        Map<Float, ResearchItem> primaryGraphTotals = new HashMap<>();

        // used to ensure all secondary keys are present in all items, just in case they have no data
        Map<String, Integer> secondaryKeyset = new HashMap<>();

        for (Integer key : primaryDataset.keySet()) {

            Float value = primaryDataset.get(key);
            sortedPrimary.add(value);

            // Find current primary Value and add to builder map
            ResearchItem currItem;
            if (primaryGraphTotals.containsKey(value)) {

                currItem = primaryGraphTotals.get(value);
                float currItemTotal = currItem.getPrimaryValue() + 1;
                currItem.setPrimaryValue(currItemTotal);
            } else {

                currItem = new ResearchItem();
                currItem.setPrimaryValue(1);
            }
            currItem.setPrimaryName(value.toString());

            // check for and add secondary item to total
            if (secondaryDataset.containsKey(key)) {

                // Get secondary item form current ResearchItem
                Map<String, Float> secondaryItems = currItem.getSecondaryData();
                Float secondaryValue = secondaryDataset.get(key);
                Float secondaryTotal = 1.0f;
                String secondaryKey = secondaryValue.toString();

                // Keep track of unique keys for secondary items
                // want to make sure all possible keys are present in graphData
                secondaryKeyset.put(secondaryKey, 0);

                if (secondaryItems.containsKey(secondaryKey)) {

                    secondaryTotal = secondaryItems.get(secondaryKey);
                    secondaryTotal++;
                    secondaryItems.put(secondaryKey, secondaryTotal);
                } else {

                    secondaryItems.put(secondaryKey, 1.0f);
                }

                // add secondary totals to currItem
                currItem.setSecondaryData(secondaryItems);
            }

            primaryGraphTotals.put(value, currItem);

            // Calculate Stats while building data
            // check range
            if (value > rangeHigh) {
                rangeHigh = value;
            }
            if (value < rangeLow) {
                rangeLow = value;
            }

            // sum total for average
            total += value;

        }

        // If no grouping and over 30 items, force groups of 10
        if (primaryGraphTotals.keySet().size() > 30 && !filters.isGroupPrimary()
                && (filters.getGraphType().equals("pie") || filters.getGraphType().equals("stacked-bar")
                        || filters.getGraphType().equals("grouped-bar"))) {

            filters.setGroupPrimary(true);
            filters.setGroupFactor(10);
        }

        // Build group key values -- don't bother if there will only be 1 group
        // make happen automatically too? -- primaryGraphTotals.keySet().size() > 20
        if (filters.isGroupPrimary() && primaryGraphTotals.keySet().size() > filters.getGroupFactor()) {

            int groupFactor = filters.getGroupFactor();
            int firstLowKey = (int) (rangeLow / groupFactor) * groupFactor;
            int lastLowKey = (int) (rangeHigh / groupFactor) * groupFactor;

            //int totalGroups = (int)((lastLowKey-firstLowKey) / groupFactor);
            List<String> groupIndexes = new ArrayList<String>();
            for (int low = firstLowKey; low <= lastLowKey; low += groupFactor) {

                int lowTemp = low;
                if (low < filters.getRangeStart()) {

                    lowTemp = filters.getRangeStart().intValue();
                }

                int high = low + (groupFactor - 1);
                if (high > filters.getRangeEnd()) {

                    high = filters.getRangeEnd().intValue();
                }

                String groupIndex = String.format("%d - %d", lowTemp, high);
                groupIndexes.add(groupIndex);

                ResearchItem blankItem = new ResearchItem(groupIndex);
                groupedData.put(groupIndex, blankItem);
            }

            for (Float key : primaryGraphTotals.keySet()) {

                ResearchItem currItem = primaryGraphTotals.get(key);

                Float newVal = Float.parseFloat(currItem.getPrimaryName());
                Float newCount = currItem.getPrimaryValue();

                int targetGroup = (int) (newVal / groupFactor) - (firstLowKey / groupFactor);
                String groupIndex = groupIndexes.get(targetGroup);

                ResearchItem finalItem;
                if (groupedData.containsKey(groupIndex)) {

                    // add current single item to existing group Item
                    finalItem = groupedData.get(groupIndex);

                    Float currCount = finalItem.getPrimaryValue();
                    currCount += newCount;
                    finalItem.setPrimaryValue(currCount);

                    Map<String, Float> currSecondaryData = currItem.getSecondaryData();
                    for (String sKey : currSecondaryData.keySet()) {

                        Float sCount = currSecondaryData.get(sKey);

                        Map<String, Float> finalSecondaryData = finalItem.getSecondaryData();
                        if (finalSecondaryData.containsKey(sKey)) {

                            Float finalCount = finalSecondaryData.get(sKey);
                            finalCount += sCount;
                            finalSecondaryData.put(sKey, finalCount);
                        } else {

                            finalSecondaryData.put(sKey, sCount);
                        }

                        finalItem.setSecondaryData(finalSecondaryData);
                    }

                } else {

                    // haven't encountered item in range before, just set to current single item
                    finalItem = new ResearchItem();
                    finalItem.setPrimaryName(groupIndex);
                    finalItem.setPrimaryValue(newCount);
                    finalItem.setSecondaryData(currItem.getSecondaryData());

                }
                // add item to grouped data
                groupedData.put(groupIndex, finalItem);
            }

            SortedSet<String> keys = new TreeSet<>(new GroupedCompare());
            keys.addAll(groupedData.keySet());
            for (String key : keys) {

                // Make sure all secondary keys are set for all items
                ResearchItem currItem = groupedData.get(key);
                Map<String, Float> secondaryData = currItem.getSecondaryData();
                for (String secondaryKey : secondaryKeyset.keySet()) {

                    if (!secondaryData.containsKey(secondaryKey)) {

                        secondaryData.put(secondaryKey, 0.0f);
                    }
                }

                graphData.add(currItem);
            }

        } else {

            SortedSet<Float> keys = new TreeSet<>(primaryGraphTotals.keySet());
            for (Float key : keys) {

                // Make sure all secondary keys are set for all items
                ResearchItem currItem = primaryGraphTotals.get(key);
                Map<String, Float> secondaryData = currItem.getSecondaryData();
                for (String secondaryKey : secondaryKeyset.keySet()) {

                    if (!secondaryData.containsKey(secondaryKey)) {

                        secondaryData.put(secondaryKey, 0.0f);
                    }
                }

                graphData.add(currItem);
            }
        }

        // Sort primary Data
        Collections.sort(sortedPrimary);

        // Get Median Value from sorted list
        float average = total / sampleSize;

        if (sampleSize > 1) {
            if (sampleSize % 2 == 0) {

                int i = (sampleSize / 2) - 1;
                int j = i + 1;

                // get vals i and j
                float val1 = sortedPrimary.get(i);
                float val2 = sortedPrimary.get(j);

                //Integer key1 = primaryKeyList.get(i);
                //Integer key2 = primaryKeyList.get(j);

                //float val1 = primaryDataset.get(key1);
                //float val2 = primaryDataset.get(key2);

                median = (val1 + val2) / 2;
            } else {

                int i = (int) Math.floor(sampleSize / 2);

                //Integer key = primaryKeyList.get(i);
                //median = primaryDataset.get(key);

                median = sortedPrimary.get(i);

            }
        } else {

            median = sortedPrimary.get(0);
        }

        // build graph model item

        graphModel.setAverage(average);
        graphModel.setMedian(median);
        graphModel.setRangeLow(rangeLow);
        graphModel.setRangeHigh(rangeHigh);
        graphModel.setGraphData(graphData);
        graphModel.setPrimaryValuemap(primaryResult.getValueMap());
        graphModel.setSecondaryValuemap(secondaryResult.getValueMap());
        graphModel.setyAxisTitle(yAxisTitle);
        graphModel.setxAxisTitle(xAxisTitle);
        graphModel.setUnitOfMeasurement(unitOfMeasurement);

        return graphModel;

    }

    private ResearchResult getDatasetItems(String datasetName, ResearchFilterItem filters) {

        switch (datasetName) {

        // Single Value Vital Items
        case "weight":
        case "temperature":
        case "heartRate":
        case "respiratoryRate":
        case "oxygenSaturation":
        case "glucose":
        case "bloodPressureSystolic":
        case "bloodPressureDiastolic":
            return getPatientVitals(datasetName, filters);

        // Special Case Vital Item
        case "height":
            return getPatientHeights(filters);

        // Patient Specific Items
        case "age":
        case "gender":
        case "pregnancyStatus":
        case "pregnancyTime":
            return getPatientAttribute(datasetName, filters);

        // Medication Items
        case "prescribedMeds":
            return getPrescribedMedications(filters);

        case "dispensedMeds":
            return getDispensedMedications(filters);

        default:

            return new ResearchResult();
        }

    }

    /**
     * {@inheritDoc}
     */
    public ResearchResult getPatientVitals(String vitalName, ResearchFilterItem filters) {

        String startDateString = filters.getStartDate();
        String endDateString = filters.getEndDate();
        Integer medicationID = filters.getMedicationId();

        ResearchResult resultObj = new ResearchResult();
        Map<Integer, Float> resultItems = new HashMap<>();

        Date startDateObj;
        Date endDateObj;
        SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {

            // Set Start Date to start of day
            String startParseDate = startDateString + " 00:00:00";
            startDateObj = sqlFormat.parse(startParseDate);

            // Set End Date to end of day
            String parseEndDate = endDateString + " 23:59:59";
            endDateObj = sqlFormat.parse(parseEndDate);

        } catch (ParseException e) {

            startDateObj = new Date();
            endDateObj = new Date();
        }

        Query<PatientEncounterVital> q = QueryProvider.getPatientEncounterVitalQuery();

        if (medicationID > 0) {

            Query<PatientPrescription> pQ = QueryProvider.getPatientPrescriptionQuery();

            pQ.fetch("patientEncounter").where().eq("medication.id", medicationID);
            List<? extends IPatientPrescription> patientPrescriptions = prescriptionRepository.find(pQ);

            List<Integer> scriptEncounterIds = new ArrayList<>();
            int i = 0;
            for (IPatientPrescription script : patientPrescriptions) {

                scriptEncounterIds.add(script.getPatientEncounter().getId());
            }

            q.where().in("patientEncounterId", scriptEncounterIds);
        }

        q.where().gt("dateTaken", sqlFormat.format(startDateObj)).lt("dateTaken", sqlFormat.format(endDateObj))
                .eq("vital.name", vitalName).orderBy("vital_value").findList();

        List<? extends IPatientEncounterVital> patientEncounterVitals = patientEncounterVitalRepository.find(q);

        /*
                select t0.id c0, t0.user_id c1, t0.patient_encounter_id c2, t0.vital_value c3, t0.date_taken c4,
                t1.id c5, t1.name c6, t1.data_type c7, t1.unit_of_measurement c8, t1.isDeleted c9
                from patient_encounter_vitals t0
                left outer join vitals as t1 on t1.id = t0.vital_id
                left join patient_prescriptions as t2 on t2.encounter_id = t0.patient_encounter_id
                where t0.date_taken > '2014-10-24 00:00:00'
                and t0.date_taken < '2014-11-05 11:59:59'
                and t1.name = 'temperature'
                and t2.medication_id = 1
                order by vital_value
            
                select t0.id c0, t0.user_id c1, t0.patient_encounter_id c2, t0.vital_value c3, t0.date_taken c4
                from patient_encounter_vitals t0
                left outer join vitals t1 on t1.id = t0.vital_id
                where t0.date_taken > ?
                and t0.date_taken < ?
                and t1.name = ?
                order by vital_value
        */

        String unitOfMeasurement = "";
        for (IPatientEncounterVital eVital : patientEncounterVitals) {

            if (unitOfMeasurement.isEmpty()) {
                unitOfMeasurement = eVital.getVital().getUnitOfMeasurement();
            }

            Float vitalValue = eVital.getVitalValue();
            if (vitalValue >= filters.getRangeStart() && vitalValue <= filters.getRangeEnd()) {

                resultItems.put(eVital.getPatientEncounterId(), vitalValue);
            }

        }

        resultObj.setDataType(vitalName);
        resultObj.setUnitOfMeasurement(unitOfMeasurement);
        resultObj.setDataset(resultItems);

        return resultObj;
    }

    /**
     * {@inheritDoc}
     */
    public ResearchResult getPatientAttribute(String attributeName, ResearchFilterItem filters) {

        String startDateString = filters.getStartDate();
        String endDateString = filters.getEndDate();
        Integer medicationID = filters.getMedicationId();

        ResearchResult resultObj = new ResearchResult();
        Map<Integer, Float> resultItems = new HashMap<>();
        Map<Float, String> resultMap = new HashMap<>();

        Date startDateObj;
        Date endDateObj;
        SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {

            // Set Start Date to start of day
            String startParseDate = startDateString + " 00:00:00";
            startDateObj = sqlFormat.parse(startParseDate);

            // Set End Date to end of day
            String parseEndDate = endDateString + " 23:59:59";
            endDateObj = sqlFormat.parse(parseEndDate);

        } catch (ParseException e) {

            startDateObj = new Date();
            endDateObj = new Date();
        }

        // Get patients that had an encounter between startDate and endDate
        // using dateOfTriageVisit for now -- might want to also check dateOfMedicalVisit & dateOfPharmacyVisit
        Query<PatientEncounter> q = QueryProvider.getPatientEncounterQuery();

        if (medicationID > 0) {

            Query<PatientPrescription> pQ = QueryProvider.getPatientPrescriptionQuery();

            pQ.fetch("patientEncounter").where().eq("medication.id", medicationID);
            List<? extends IPatientPrescription> patientPrescriptions = prescriptionRepository.find(pQ);

            List<Integer> scriptEncounterIds = new ArrayList<>();
            int i = 0;
            for (IPatientPrescription script : patientPrescriptions) {

                scriptEncounterIds.add(script.getPatientEncounter().getId());
            }

            q.where().in("id", scriptEncounterIds);
        }

        q.fetch("patient").where().gt("dateOfTriageVisit", sqlFormat.format(startDateObj))
                .lt("dateOfTriageVisit", sqlFormat.format(endDateObj)).orderBy("id").findList();

        List<? extends IPatientEncounter> encounters = patientEncounterRepository.find(q);

        String unitOfMeasurement = "";
        switch (attributeName) {

        case "age":

            unitOfMeasurement = "years";
            for (IPatientEncounter encounter : encounters) {
                IPatient patient = encounter.getPatient();

                Float age = (float) Math.floor(dateUtils.getAgeFloat(patient.getAge()));

                if (age >= filters.getRangeStart() && age <= filters.getRangeEnd()) {

                    resultItems.put(encounter.getId(), age);
                }
            }
            break;

        case "gender":

            resultMap.put(0.0f, "Male");
            resultMap.put(1.0f, "Female");
            resultMap.put(2.0f, "N/A");

            for (IPatientEncounter encounter : encounters) {
                IPatient patient = encounter.getPatient();

                float gender = -1;
                // Do case in-sensitve comparison to be safe
                //1 = female
                //0 = male
                //2 = no sex
                if (patient.getSex() == null) {
                    gender = 2;
                } else if (patient.getSex().matches("(?i:Male)")) {
                    gender = 0;
                } else if (patient.getSex().matches("(?i:Female)")) {
                    gender = 1;
                }
                resultItems.put(encounter.getId(), gender);
            }
            break;

        case "pregnancyStatus":

            for (IPatientEncounter encounter : encounters) {

                resultMap.put(0.0f, "No");
                resultMap.put(1.0f, "Yes");

                Integer wksPregnant = encounter.getWeeksPregnant();
                if (wksPregnant == null)
                    wksPregnant = 0;
                float pregnancyStatus = 0;
                if (wksPregnant > 0) {
                    pregnancyStatus = 1;
                }
                resultItems.put(encounter.getId(), pregnancyStatus);
            }
            break;

        case "pregnancyTime":

            unitOfMeasurement = "weeks";
            for (IPatientEncounter encounter : encounters) {

                Integer weeksPregnant = encounter.getWeeksPregnant();
                if (weeksPregnant == null)
                    weeksPregnant = 0;

                if (weeksPregnant >= filters.getRangeStart() && weeksPregnant <= filters.getRangeEnd()) {

                    resultItems.put(encounter.getId(), (float) weeksPregnant);
                }
            }
            break;
        }

        resultObj.setDataType(attributeName);
        resultObj.setUnitOfMeasurement(unitOfMeasurement);
        resultObj.setDataset(resultItems);
        resultObj.setValueMap(resultMap);

        return resultObj;
    }

    public ResearchResult getPatientHeights(ResearchFilterItem filters) {

        String startDateString = filters.getStartDate();
        String endDateString = filters.getEndDate();
        Integer medicationID = filters.getMedicationId();

        ResearchResult resultObj = new ResearchResult();
        Map<Integer, Float> buildItems = new HashMap<>();

        Date startDateObj;
        Date endDateObj;
        SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {

            // Set Start Date to start of day
            String startParseDate = startDateString + " 00:00:00";
            startDateObj = sqlFormat.parse(startParseDate);

            // Set End Date to end of day
            String parseEndDate = endDateString + " 23:59:59";
            endDateObj = sqlFormat.parse(parseEndDate);

        } catch (ParseException e) {

            startDateObj = new Date();
            endDateObj = new Date();
        }

        Query<PatientEncounterVital> q = QueryProvider.getPatientEncounterVitalQuery();

        List<Integer> scriptEncounterIds = new ArrayList<>();
        if (medicationID > 0) {

            Query<PatientPrescription> pQ = QueryProvider.getPatientPrescriptionQuery();

            pQ.fetch("patientEncounter").where().eq("medication.id", medicationID);
            List<? extends IPatientPrescription> patientPrescriptions = prescriptionRepository.find(pQ);

            int i = 0;
            for (IPatientPrescription script : patientPrescriptions) {

                scriptEncounterIds.add(script.getPatientEncounter().getId());
            }

            q.where().in("patientEncounterId", scriptEncounterIds);
        }

        q.fetch("vital").where().gt("dateTaken", sqlFormat.format(startDateObj))
                .lt("dateTaken", sqlFormat.format(endDateObj)).eq("vital.name", "heightFeet")
                .orderBy("patient_encounter_id").findList();
        List<? extends IPatientEncounterVital> patientFeet = patientEncounterVitalRepository.find(q);

        q = QueryProvider.getPatientEncounterVitalQuery();

        if (medicationID > 0 && scriptEncounterIds.size() > 0) {

            q.where().in("patientEncounterId", scriptEncounterIds);
        }
        q.fetch("vital").where().gt("dateTaken", sqlFormat.format(startDateObj))
                .lt("dateTaken", sqlFormat.format(endDateObj)).eq("vital.name", "heightInches")
                .orderBy("patient_encounter_id").findList();
        List<? extends IPatientEncounterVital> patientInches = patientEncounterVitalRepository.find(q);

        //Map<Integer, ResearchItem> researchItems = new HashMap<>();
        // Convert feet to inches
        String unitOfMeasurement = "feet/inches";
        for (IPatientEncounterVital eVital : patientFeet) {

            float heightInches = 12 * eVital.getVitalValue();
            buildItems.put(eVital.getPatientEncounterId(), heightInches);

        }

        for (IPatientEncounterVital eVital : patientInches) {

            if (buildItems.containsKey(eVital.getPatientEncounterId())) {

                float heightInches = buildItems.get(eVital.getPatientEncounterId());
                heightInches = heightInches + eVital.getVitalValue();
                buildItems.put(eVital.getPatientEncounterId(), heightInches);
            }
        }

        Map<Integer, Float> resultItems = new HashMap<>();
        for (Integer key : buildItems.keySet()) {

            Float value = buildItems.get(key);
            if (value >= filters.getRangeStart() && value <= filters.getRangeEnd()) {

                resultItems.put(key, value);
            }

        }

        resultObj.setDataType("height");
        resultObj.setUnitOfMeasurement(unitOfMeasurement);
        resultObj.setDataset(resultItems);

        return resultObj;

    }

    public ResearchResult getPrescribedMedications(ResearchFilterItem filters) {

        String startDateString = filters.getStartDate();
        String endDateString = filters.getEndDate();

        ResearchResult resultObj = new ResearchResult();
        Map<Integer, Float> resultItems = new HashMap<>();
        Map<Float, String> resultMap = new HashMap<>();

        Date startDateObj;
        Date endDateObj;
        SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {

            // Set Start Date to start of day
            String startParseDate = startDateString + " 00:00:00";
            startDateObj = sqlFormat.parse(startParseDate);

            // Set End Date to end of day
            String parseEndDate = endDateString + " 23:59:59";
            endDateObj = sqlFormat.parse(parseEndDate);

        } catch (ParseException e) {

            startDateObj = new Date();
            endDateObj = new Date();
        }

        Query<PatientPrescription> q = QueryProvider.getPatientPrescriptionQuery();
        q.fetch("medication").where().gt("dateTaken", sqlFormat.format(startDateObj))
                .lt("dateTaken", sqlFormat.format(endDateObj)).findList();

        List<? extends IPatientPrescription> patientMedication = prescriptionRepository.find(q);

        String unitOfMeasurement = "";
        for (IPatientPrescription prescription : patientMedication) {

            Integer key = prescription.getMedication().getId();

            // Build prescription name map
            if (!resultMap.containsKey((float) key)) {

                resultMap.put((float) key, prescription.getMedication().getName());
            }

            Float count = 1.0f;
            if (resultItems.containsKey(key)) {

                count = resultItems.get(key) + 1.0f;
            }
            resultItems.put(key, count);

        }

        resultObj.setDataType("prescribedMeds");
        resultObj.setUnitOfMeasurement(unitOfMeasurement);
        resultObj.setDataset(resultItems);
        resultObj.setValueMap(resultMap);

        return resultObj;
    }

    // @TODO - need to make this work correctly
    // want Medication Name (xAxis) vs Total Dispensed (yAxis)
    // Need to tweak some things, this is different than # of patients
    public ResearchResult getDispensedMedications(ResearchFilterItem filters) {

        String startDateString = filters.getStartDate();
        String endDateString = filters.getEndDate();

        ResearchResult resultObj = new ResearchResult();
        Map<Integer, Float> resultItems = new HashMap<>();
        Map<Float, String> resultMap = new HashMap<>();

        Date startDateObj;
        Date endDateObj;
        SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {

            // Set Start Date to start of day
            String startParseDate = startDateString + " 00:00:00";
            startDateObj = sqlFormat.parse(startParseDate);

            // Set End Date to end of day
            String parseEndDate = endDateString + " 23:59:59";
            endDateObj = sqlFormat.parse(parseEndDate);

        } catch (ParseException e) {

            startDateObj = new Date();
            endDateObj = new Date();
        }

        Query<PatientPrescription> q = QueryProvider.getPatientPrescriptionQuery();
        q.fetch("medication").where().gt("dateTaken", sqlFormat.format(startDateObj))
                .lt("dateTaken", sqlFormat.format(endDateObj)).findList();

        List<? extends IPatientPrescription> patientMedication = prescriptionRepository.find(q);

        String unitOfMeasurement = "";
        for (IPatientPrescription prescription : patientMedication) {

            Integer key = prescription.getMedication().getId();

            // Build prescription name map
            if (!resultMap.containsKey((float) key)) {

                resultMap.put((float) key, prescription.getMedication().getName());
            }

            Float count = (float) prescription.getAmount();
            if (resultItems.containsKey(key)) {

                count = resultItems.get(key) + count;
            }
            resultItems.put(key, count);

        }

        resultObj.setDataType("dispensedMeds");
        resultObj.setUnitOfMeasurement(unitOfMeasurement);
        resultObj.setDataset(resultItems);
        resultObj.setValueMap(resultMap);

        return resultObj;

    }

}

// Sorts Strings in format "float - float" by using the first float of the string
class GroupedCompare implements Comparator<String> {

    @Override
    public int compare(String s1, String s2) {

        String[] s1Matches = s1.split("-");
        String s1First;
        Float s1Val = 0.0f;
        if (s1Matches.length > 1) {
            s1First = s1Matches[0];
        } else {
            s1First = s1;
        }
        s1Val = Float.parseFloat(s1First);

        String[] s2Matches = s2.split("-");
        String s2First;
        Float s2Val = 0.0f;
        if (s2Matches.length > 1) {
            s2First = s2Matches[0];
        } else {
            s2First = s2;
        }
        s2Val = Float.parseFloat(s2First);

        if (s1Val < s2Val) {

            return -1;
        } else if (s1Val > s2Val) {

            return 1;
        }
        return 0;
    }
}