Skip to content
Snippets Groups Projects
ViewDiagnosisStatisticsAction.java 15.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • HMoss's avatar
    HMoss committed
    package edu.ncsu.csc.itrust.action;
    
    
    import java.text.DateFormat;
    
    HMoss's avatar
    HMoss committed
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.List;
    import edu.ncsu.csc.itrust.beans.DiagnosisBean;
    import edu.ncsu.csc.itrust.beans.DiagnosisStatisticsBean;
    import edu.ncsu.csc.itrust.dao.DAOFactory;
    import edu.ncsu.csc.itrust.dao.mysql.DiagnosesDAO;
    import edu.ncsu.csc.itrust.dao.mysql.ICDCodesDAO;
    import edu.ncsu.csc.itrust.exception.DBException;
    import edu.ncsu.csc.itrust.exception.FormValidationException;
    import edu.ncsu.csc.itrust.exception.ITrustException;
    
    /**
     * Used for the View Diagnosis Statistics page. Can return a list of all Diagnoses
     * and get diagnosis statistics for a specified Zip code, Diagnosis code, and date range.
     */
    public class ViewDiagnosisStatisticsAction {
    
        /** Database access methods for ICD codes (diagnoses) */
        private ICDCodesDAO icdDAO;
        /** Database access methods for diagnosis information */
        private DiagnosesDAO diagnosesDAO;
        /** ICD Code for malaria */
        private static final String ICD_MALARIA = "84.50";
        /** ICD Code for Influenza */
        private static final String ICD_INFLUENZA = "487.00";
        
    
        /**
         * Parses a string date into a java.util.Date object. Also checks that the date
         * is in the format MM/dd/yyyy and ensures that it is not in the future.
         * @param dateString string representation of the date
         * @return parsed date as a java.util.Date object
         * @throws FormValidationException if the date is invalid
         */
        private Date parseDate(String dateString) throws FormValidationException {
            Date next;
            try {
                DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
                formatter.setLenient(false);
                next = formatter.parse(dateString);
            } catch (ParseException e) {
                throw new FormValidationException("Enter dates in MM/dd/yyyy");
            }
            if (next.after(new Date())) {
                throw new FormValidationException("Provided date is in the future.");
            }
            return next;
        }
    
        /**
         * Throws an exception if the string provided is not a valid zip code.
         * @param zip zip code as a string
         * @throws FormValidationException if the zip code is invalid
         */
        private void validateZipCode(String zip) throws FormValidationException {
            if (!zip.matches("([0-9]{5})|([0-9]{5}-[0-9]{4})")) {
                throw new FormValidationException("Zip Code must be 5 digits!");
            }
        }
    
        /**
         * Throws an exception if the ICDCode is not valid.
         * @param icdCode the ICD code
         * @throws ITrustException wrapper for a DB error
         * @throws FormValidationException if the ICD code is not valid
         */
        private void validateIcdCode(String icdCode) throws ITrustException, FormValidationException {
            boolean validCode = false;
            for(DiagnosisBean diag : getDiagnosisCodes()) {
                if (diag.getICDCode().equals(icdCode)) {
                    validCode = true;
                }
            }
            if (validCode == false) {
                throw new FormValidationException("ICDCode must be valid diagnosis!");
            }
        }
    
    
        /**
         * Constructor for the action. Initializes DAO fields
         * @param factory The session's factory for DAOs
         */
        public ViewDiagnosisStatisticsAction(DAOFactory factory) {
            this.icdDAO = factory.getICDCodesDAO();
            this.diagnosesDAO = factory.getDiagnosesDAO();
        }
        
        /**
         * Gets all the diagnosis codes in iTrust and returns them in a list of beans.
         * 
         * @return List of DiagnosisBeans correlating to all ICDCodes
         * @throws ITrustException
         */
        public List<DiagnosisBean> getDiagnosisCodes() throws ITrustException  {
            return icdDAO.getAllICDCodes();
        }
        
        /**
         * Gets the counts of local and regional diagnoses for the specified input
         * 
         * @param lowerDate The beginning date for the time range
         * @param upperDate The ending date for the time range
         * @param icdCode The diagnosis code to examine
         * @param zip The zip code to examine
         * @return A bean containing the local and regional counts
         * @throws FormValidationException
         * @throws ITrustException
         */
        public DiagnosisStatisticsBean getDiagnosisStatistics(String lowerDate, String upperDate, String icdCode, String zip) throws FormValidationException, ITrustException {
            DiagnosisStatisticsBean dsBean;
    
            if (lowerDate == null || upperDate == null || icdCode == null) {
                return null;
            }
            
            validateZipCode(zip);
            validateIcdCode(icdCode);
    
    HMoss's avatar
    HMoss committed
    
    
            Date lower = parseDate(lowerDate);
            Date upper = parseDate(upperDate);
            if (lower.after(upper)) {
                throw new FormValidationException("Start date must be before end date!");
            }
    
    HMoss's avatar
    HMoss committed
    
    
            dsBean = diagnosesDAO.getDiagnosisCounts(icdCode, zip, lower, upper);
        
    
            return dsBean;
    
        /**
         * Gets the counts of local and regional diagnoses for the specified input (lower date 8 weeks prior to upper date)
         *
         * @param upperDate The ending date for the time range
         * @param icdCode The diagnosis code to examine
         * @param zip The zip code to examine
         * @return A bean containing the local and regional counts
         * @throws FormValidationException
         * @throws ITrustException
         */
        public DiagnosisStatisticsBean getDiagnosisStatistics(String upperDate, String icdCode, String zip) throws FormValidationException, ITrustException {
            DiagnosisStatisticsBean dsBean;
            try {
    
                if (upperDate == null || icdCode == null)
                    return null;
    
                Date upper = new SimpleDateFormat("MM/dd/yyyy").parse(upperDate);
    
                //calculate lower date by finding the date that is 8 weeks prior to upperDate
                Calendar cal = Calendar.getInstance();
                cal.setTime(upper);
                cal.add(Calendar.HOUR, -56*24);
                Date lower = cal.getTime();
    
                if (!zip.matches("([0-9]{5})|([0-9]{5}-[0-9]{4})"))
                    throw new FormValidationException("Zip Code must be 5 digits!");
    
                boolean validCode = false;
                for(DiagnosisBean diag : getDiagnosisCodes()) {
                    if (diag.getICDCode().equals(icdCode))
                        validCode = true;
                }
                if (validCode == false) {
                    throw new FormValidationException("ICDCode must be valid diagnosis!");
                }
    
                dsBean = diagnosesDAO.getDiagnosisCounts(icdCode, zip, lower, upper);
    
            } catch (ParseException e) {
                throw new FormValidationException("Enter date in MM/dd/yyyy");
            }
    
    
            return dsBean;
        }
    
    
        public List<DiagnosisStatisticsBean> getDiagnosisTrends(String dateString, String icdCode, String zip) throws FormValidationException, ITrustException {
            ArrayList<DiagnosisStatisticsBean> dsBean = null;
    
            if (dateString == null || icdCode == null) {
    
            validateIcdCode(icdCode);
            validateZipCode(zip);
            Date next = parseDate(dateString);
    
            
            dsBean = new ArrayList<>();
                
            Calendar cal = Calendar.getInstance();
            cal.setTime(next);
            for (int i = 0; i < 8; i++) {
                Date weekDate = cal.getTime();
                dsBean.add(diagnosesDAO.getCountForWeekBefore(icdCode, zip, weekDate));
                cal.add(Calendar.HOUR, -24*7);
            }
    
        
        /**
         * Gets the local and regional counts for the specified week and calculates the prior average.
         * 
         * @param startDate a date in the week to analyze
         * @param icdCode the diagnosis to analyze
         * @param zip the area to analyze
         * @param threshold threshold
         * @return statistics for the week and previous averages
         * @throws FormValidationException
         * @throws DBException
         */
        public ArrayList<DiagnosisStatisticsBean> getEpidemicStatistics(String startDate, String icdCode, String zip, String threshold) throws FormValidationException, DBException {
    
            if (startDate == null || icdCode == null) {
    
                return null;
    
            if (!(icdCode.equals(ICD_MALARIA) || icdCode.equals(ICD_INFLUENZA)) ) {
    
                throw new FormValidationException("Invalid ICD code.");
            }
    
            if(ICD_MALARIA.equals(icdCode)){
    
                    Integer.parseInt(threshold);
    
                } catch(NumberFormatException e) {
    
                    throw new FormValidationException("Threshold must be an integer.");
                }
            }
    
            Date lower = parseDate(startDate);
    
            
            DiagnosisStatisticsBean dbWeek = diagnosesDAO.getCountForWeekOf(icdCode, zip, lower);
            DiagnosisStatisticsBean dbAvg = new DiagnosisStatisticsBean(zip, 0, 0, lower, lower);
            
            Calendar cal = Calendar.getInstance();
            
            Date start = diagnosesDAO.findEarliestIncident(icdCode); //start, which is set to earliest incident
            Calendar startCal = Calendar.getInstance();
            if(start != null)
                startCal.setTime(start);
            
            ArrayList<DiagnosisStatisticsBean> ret = new ArrayList<DiagnosisStatisticsBean>();
            if (start == null) {
                ret.add(dbWeek);
                ret.add(dbAvg);
                return ret;
            }
            cal.setTime(lower); //cal, which is set to lower
            Calendar lowerCal = Calendar.getInstance();
            lowerCal.setTime(lower);
            int weekOfYr = cal.get(Calendar.WEEK_OF_YEAR);
            
            cal.set(Calendar.YEAR, startCal.get(Calendar.YEAR));  //cal's year then gets set to start's year
            ArrayList<DiagnosisStatisticsBean> dbList = new ArrayList<DiagnosisStatisticsBean>();
            
            while( cal.getTime().before(lower) && cal.get(Calendar.YEAR) != lowerCal.get(Calendar.YEAR)) {
                dbList.add( diagnosesDAO.getCountForWeekOf(icdCode, zip, cal.getTime()) );
                cal.add(Calendar.YEAR, 1);
                cal.set(Calendar.WEEK_OF_YEAR, weekOfYr);
                cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            }
            
            long avg = 0;
            long avgRegion = 0;
            if (dbList.size() > 0) {
                for (DiagnosisStatisticsBean d : dbList) {
                    avg += d.getZipStats();
                    avgRegion += d.getRegionStats();
                }
                avg /= dbList.size();
                avgRegion /= dbList.size();
            }
            
            dbAvg.setRegionStats(avgRegion);
            dbAvg.setZipStats(avg);
            
            ret.add(dbWeek);
            ret.add(dbAvg);
            return ret;
        }
        
        /**
         * Determines if an Influenza Epidemic is happening
         * 
    
         * @param weekDate a date in the currently evaluated week
    
         * @param zip the zip code to analyze
         * @return whether or not there is an epidemic
         * @throws ParseException
         * @throws DBException
         */
    
        public boolean isFluEpidemic(String weekDate, String zip) throws ParseException, DBException {
            Date wkDate = new SimpleDateFormat("MM/dd/yyyy").parse(weekDate);
    
            
            Calendar cal = Calendar.getInstance();
    
            
            int weekOfYr = cal.get(Calendar.WEEK_OF_YEAR);
    
            double threshold = calcInfluenzaThreshold(weekOfYr);
    
            DiagnosisStatisticsBean prev1 = diagnosesDAO.getCountForWeekBefore(ICD_INFLUENZA, zip, cal.getTime());
            cal.add(Calendar.HOUR, -7*24);
            DiagnosisStatisticsBean prev2 =  diagnosesDAO.getCountForWeekBefore(ICD_INFLUENZA, zip, cal.getTime());
    
    
            long weekL1 = prev1.getRegionStats();
            long weekL2 = prev2.getRegionStats();
    
            boolean epidemicL1 = weekL1 > threshold;
            boolean epidemicL2 = weekL2 > threshold;
    
        }
        
        /**
         * Calculates the threshold of an influenza epidemic
         * 
         * @param weekNumber the week of the year
         * @return the epidemic threshold for flu cases
         */
    
        private double calcInfluenzaThreshold(double weekNumber) {
    
            return 5.34 + 0.271 * weekNumber + 3.45 * Math.sin(2 * Math.PI * weekNumber / 52.0) + 8.41 * Math.cos(2 * Math.PI * weekNumber / 52.0);
        }
        
        /**
         * Determines whether a Malaria epidemic is happening for two consecutive weeks
    
         * 
         * @param weekDate a date in the currently evaluated week
         * @param zip the zip code to analyze
         * @param thresholdStr the threshold for an epidemic
         * @return whether or not there is an epidemic
         * @throws DBException
         * @throws ParseException
         */
        public boolean isMalariaEpidemic(String weekDate, String zip, String thresholdStr) throws DBException, ParseException {
            Date wkDate = new SimpleDateFormat("MM/dd/yyyy").parse(weekDate);
            
            ArrayList<DiagnosisStatisticsBean> historyWkL1 = new ArrayList<DiagnosisStatisticsBean>();
    
            ArrayList<DiagnosisStatisticsBean> historyWkL2 = new ArrayList<DiagnosisStatisticsBean>();
            
            double threshold = Double.parseDouble(thresholdStr);
            
    
            Calendar cal = Calendar.getInstance();
    
            cal.setTime(wkDate);
    
            DiagnosisStatisticsBean current = diagnosesDAO.getCountForWeekOf(ICD_MALARIA, zip, cal.getTime());
            
    
            DiagnosisStatisticsBean prev1 = diagnosesDAO.getCountForWeekBefore(ICD_MALARIA, zip, cal.getTime());
    
            long weekTotalL1 = prev1.getRegionStats();
            
    
            cal.add(Calendar.HOUR, -7*24);
    
            DiagnosisStatisticsBean prev2 = diagnosesDAO.getCountForWeekBefore(ICD_MALARIA, zip, cal.getTime());
            long weekTotalL2 = prev2.getRegionStats();
            
            cal.setTime(wkDate);
            
            //Find earliest Malaria Case. Set calendar's year to that year
            Date startData = diagnosesDAO.findEarliestIncident(ICD_MALARIA);
            if (startData == null) {
                if (current.getRegionStats() > 0) {
                    return true;
                }
                return false;
            }
            Calendar startDateCal = Calendar.getInstance();
            startDateCal.setTime(startData);
            Calendar wkDateCal = Calendar.getInstance();
            wkDateCal.setTime(wkDate);
    
            cal.set(Calendar.YEAR, startDateCal.get(Calendar.YEAR));
            
    
            boolean differentYears = cal.get(Calendar.YEAR) != wkDateCal.get(Calendar.YEAR);
            while (cal.getTime().before(wkDate) && differentYears) {
    
                historyWkL1.add( diagnosesDAO.getCountForWeekBefore(ICD_MALARIA, zip, cal.getTime()) );
                cal.add(Calendar.HOUR, -7*24);
    
                historyWkL2.add( diagnosesDAO.getCountForWeekBefore(ICD_MALARIA, zip, cal.getTime()) );
                cal.add(Calendar.HOUR, 7*24);
    
                cal.add(Calendar.YEAR, 1);
    
            long totalHistL1 = 0;
            long totalHistL2 = 0;
            long countHistL1 = historyWkL1.size();
            long countHistL2 = historyWkL1.size();
            for (DiagnosisStatisticsBean d : historyWkL1) {
                totalHistL1 += d.getRegionStats();
    
            }
            for (DiagnosisStatisticsBean d : historyWkL2) {
    
                    
            double avgL1 = ((double)totalHistL1) / ((double)countHistL1);
    
            double avgL2 = ((double)totalHistL2) / ((double)countHistL2);
    
    
            boolean epidemicL1 = (weekTotalL1*100.0) / threshold > avgL1 || (countHistL1 == 0 && weekTotalL1 != 0);
            boolean epidemicL2 = (weekTotalL2*100.0) / threshold > avgL2 || (countHistL2 == 0 && weekTotalL2 != 0);
    
    HMoss's avatar
    HMoss committed
    }