Skip to content
Snippets Groups Projects
ViewDiagnosisStatisticsAction.java 15.5 KiB
Newer Older
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.time.LocalDate;
import java.time.format.DateTimeFormatter;
HMoss's avatar
HMoss committed
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";
    
    /**
     * 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;
        try {
            
            if (lowerDate == null || upperDate == null || icdCode == null)
                return null;
            
            Date lower = new SimpleDateFormat("MM/dd/yyyy").parse(lowerDate);
            Date upper = new SimpleDateFormat("MM/dd/yyyy").parse(upperDate);
HMoss's avatar
HMoss committed

            if (lower.after(upper))
                throw new FormValidationException("Start date must be before end date!");
            
            if (!zip.matches("([0-9]{5})|([0-9]{5}-[0-9]{4})"))
                throw new FormValidationException("Zip Code must be 5 digits!");
HMoss's avatar
HMoss committed

            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!");
            }
HMoss's avatar
HMoss committed

            dsBean = diagnosesDAO.getDiagnosisCounts(icdCode, zip, lower, upper);
            
        } catch (ParseException e) {
            throw new FormValidationException("Enter dates in MM/dd/yyyy");
        } 
        
        
        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) {
        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!");
        }

        if (!zip.matches("([0-9]{5})|([0-9]{5}-[0-9]{4})")) {
            throw new FormValidationException("Zip Code must be 5 digits!");
        }
        
        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.");
        }
        
        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("84.50") || icdCode.equals("487.00")) ) {
            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;  //lower, which is parsed to startDate
        try {
            DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
            formatter.setLenient(false);
            lower = formatter.parse(startDate);
        } catch (ParseException e) {
            throw new FormValidationException("Enter dates in MM/dd/yyyy");
        }
        if (lower.after(new Date())) {
            throw new FormValidationException("Provided date is in the future.");
        }

        if (!zip.matches("([0-9]{5})|([0-9]{5}-[0-9]{4})")) {
            throw new FormValidationException("Zip Code must be 5 digits!");
        
        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));
        
        while( cal.getTime().before(wkDate) && cal.get(Calendar.YEAR) != wkDateCal.get(Calendar.YEAR)) {
            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
}