Newer
Older
import java.text.DateFormat;
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";
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
* 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!");
}
}
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* 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 {
if (lowerDate == null || upperDate == null || icdCode == null) {
return null;
}
validateZipCode(zip);
validateIcdCode(icdCode);
Date lower = parseDate(lowerDate);
Date upper = parseDate(upperDate);
if (lower.after(upper)) {
throw new FormValidationException("Start date must be before end date!");
}
dsBean = diagnosesDAO.getDiagnosisCounts(icdCode, zip, lower, upper);
sbobo3
committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* 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) {
if (!(icdCode.equals(ICD_MALARIA) || icdCode.equals(ICD_INFLUENZA)) ) {
throw new FormValidationException("Invalid ICD code.");
}
} catch(NumberFormatException e) {
throw new FormValidationException("Threshold must be an integer.");
}
}
validateZipCode(zip);
Date lower = parseDate(startDate);
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
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);
cal.setTime(wkDate);
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;
return epidemicL1 && epidemicL2;
}
/**
* 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
* prior to the current date.
*
* @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);
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) {
totalHistL2 += d.getRegionStats();
}
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);
return epidemicL1 && epidemicL2;