1 package org.kuali.student.enrollment.acal;
2
3
4 import java.io.BufferedReader;
5 import java.io.BufferedWriter;
6 import java.io.DataInputStream;
7 import java.io.File;
8 import java.io.FileInputStream;
9 import java.io.FileWriter;
10 import java.io.IOException;
11 import java.io.InputStreamReader;
12 import java.text.SimpleDateFormat;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Calendar;
16 import java.util.Date;
17 import java.util.GregorianCalendar;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.LinkedHashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24
25 public class AcalReferenceDataParser {
26 private List<Map<String, String>> dataSet = new ArrayList<Map<String, String>>();
27 private Map<String, Atp> allAcals = new LinkedHashMap<String, Atp>();
28 private Map<String, Atp> allHolidayCalendars = new LinkedHashMap<String, Atp>();
29
30 private static final String FALL_ATP_TYPE = "Fall";
31 private static final String WINTER_ATP_TYPE = "Winter";
32 private static final String SPRING_ATP_TYPE = "Spring";
33 private static final String SUMMER_ATP_TYPE = "Summer";
34 private static final String SESSION1_ATP_TYPE = "Session 1";
35 private static final String SESSION2_ATP_TYPE = "Session 2";
36 private static final String FALL_ATP_TYPE_KEY = "kuali.atp.type.Fall";
37 private static final String WINTER_ATP_TYPE_KEY = "kuali.atp.type.Winter";
38 private static final String SPRING_ATP_TYPE_KEY = "kuali.atp.type.Spring";
39 private static final String SUMMER_ATP_TYPE_KEY = "kuali.atp.type.Summer";
40 private static final String SESSION1_ATP_TYPE_KEY = "kuali.atp.type.Session1";
41 private static final String SESSION2_ATP_TYPE_KEY = "kuali.atp.type.Session2";
42
43 private static final String RECORD_KEY = "table_key";
44
45 private static final String RECORD_FIRST_DAY = "first_day";
46 private static final String RECORD_CENSUS_DAY = "tenth_day";
47 private static final String RECORD_LAST_DAY_ADD = "last_day_add";
48 private static final String RECORD_LAST_DAY_DROP_NOT = "last_drop_not";
49 private static final String RECORD_DROP_UG = "last_drop_ug";
50 private static final String RECORD_DROP_GRAD = "last_drop_grad";
51 private static final String RECORD_LAST_DAY_CLASSES = "last_day_classes";
52 private static final String RECORD_LAST_DAY_EXAMS = "last_day_exams";
53 private static final String RECORD_GRADE_SUBMIT_DEADLINE_DATE = "grade_submit_ddln";
54 private static final String RECORD_GRADE_SUBMIT_DEADLINE_TIME = "grade_submit_ddtm";
55
56 private static final String RECORD_SUB_1_FIRST_DAY = "first_day";
57 private static final String RECORD_SUB_1_CENSUS_DAY = "tenth_day";
58 private static final String RECORD_SUB_1_LAST_DAY_ADD = "last_day_add_a";
59 private static final String RECORD_SUB_1_LAST_DAY_DROP_NOT = "last_drop_not_a";
60 private static final String RECORD_SUB_1_DROP_UG = "last_a_drop_ug";
61 private static final String RECORD_SUB_1_DROP_GRAD = "last_a_drop_grad";
62 private static final String RECORD_SUB_1_LAST_DAY_CLASSES = "last_day_class_a";
63 private static final String RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_DATE = "grade_submit_ddln";
64 private static final String RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_TIME = "grade_submit_ddtm";
65
66 private static final String RECORD_SUB_2_FIRST_DAY = "first_day_b";
67 private static final String RECORD_SUB_2_LAST_DAY_ADD = "last_day_add_b";
68 private static final String RECORD_SUB_2_LAST_DAY_DROP_NOT = "last_drop_not_b";
69 private static final String RECORD_SUB_2_DROP_UG = "last_b_drop_ug";
70 private static final String RECORD_SUB_2_DROP_GRAD = "last_b_drop_grad";
71 private static final String RECORD_SUB_2_LAST_DAY_CLASSES = "last_day_classes";
72 private static final String RECORD_SUB_2_LAST_DAY_EXAMS = "last_day_exams";
73 private static final String RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_DATE = "grade_submit_ddln";
74 private static final String RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_TIME = "grade_submit_ddtm";
75
76
77 private static final String KEYDATE_INSTRUCTIONAL_PERIOD_KEY = "kuali.atp.milestone.InstructionalPeriod";
78 private static final String KEYDATE_FINAL_EXAM_PERIOD_KEY = "kuali.atp.milestone.FinalExamPeriod";
79 private static final String KEYDATE_CENSUS_KEY = "kuali.atp.milestone.FinancialAidCensus";
80 private static final String KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY = "kuali.atp.milestone.DropDeadlineWithoutRecord";
81 private static final String KEYDATE_DROP_DEADLINE_KEY = "kuali.atp.milestone.DropDate";
82 private static final String KEYDATE_GRADES_DUE_KEY = "kuali.atp.milestone.GradesDue";
83 private static final String KEYDATE_COURSE_SELECTION_PERIOD_END_KEY = "kuali.atp.milestone.CourseSelectionPeriodEnd";
84
85 private static final Map<String, String> keydateNames = new LinkedHashMap<String, String>();
86 {
87 keydateNames.put(KEYDATE_INSTRUCTIONAL_PERIOD_KEY, "Instructional Period");
88 keydateNames.put(KEYDATE_COURSE_SELECTION_PERIOD_END_KEY, "Course Selection Period End");
89 keydateNames.put(KEYDATE_CENSUS_KEY, "Census");
90 keydateNames.put(KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY, "Drop Deadline Without Record");
91 keydateNames.put(KEYDATE_DROP_DEADLINE_KEY, "Drop Deadline");
92 keydateNames.put(KEYDATE_FINAL_EXAM_PERIOD_KEY, "Final Exam Period");
93 keydateNames.put(KEYDATE_GRADES_DUE_KEY, "Grades Due");
94 }
95
96 private static final Set<String> relativeToInstructionPeriod = new HashSet<String>();
97 {
98 relativeToInstructionPeriod.add(KEYDATE_CENSUS_KEY);
99 relativeToInstructionPeriod.add(KEYDATE_COURSE_SELECTION_PERIOD_END_KEY);
100 relativeToInstructionPeriod.add(KEYDATE_FINAL_EXAM_PERIOD_KEY);
101 }
102
103 private static final String HOLIDAY_NEWYEARSDAY_KEY = "kuali.atp.milestone.NewYearsDay";
104 private static final String HOLIDAY_MLKDAY_KEY = "kuali.atp.milestone.MLKDay";
105 private static final String HOLIDAY_PRESIDENTSDAY_KEY = "kuali.atp.milestone.PresidentsDay";
106 private static final String HOLIDAY_MEMORIALDAY_KEY = "kuali.atp.milestone.MemorialDay";
107 private static final String HOLIDAY_INDEPENDENCEDAY_KEY = "kuali.atp.milestone.IndependenceDay";
108 private static final String HOLIDAY_LABORDAY_KEY = "kuali.atp.milestone.LaborDay";
109 private static final String HOLIDAY_VETERANSDAY_KEY = "kuali.atp.milestone.VeteransDay";
110 private static final String HOLIDAY_THANKSGIVINGBREAK_KEY = "kuali.atp.milestone.ThanksgivingBreak";
111 private static final String HOLIDAY_CHRISTMAS_KEY = "kuali.atp.milestone.Christmas";
112 private static final String HOLIDAY_OTHERNONINSTRUCTIONALHOLIDAY_KEY = "kuali.atp.milestone.OtherNonInstructionalHoliday";
113 private static final String HOLIDAY_OTHERNONINSTRUCTIONALDAY_KEY = "kuali.atp.milestone.OtherNonInstructionalDay";
114
115 private static final String HOLIDAY_NEWYEARSDAYOBSERVED_KEY = "kuali.atp.milestone.NewYearsDayObserved";
116 private static final String HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY = "kuali.atp.milestone.IndependenceDayObserved";
117 private static final String HOLIDAY_VETERANSDAYOBSERVED_KEY = "kuali.atp.milestone.VeteransDayObserved";
118 private static final String HOLIDAY_CHRISTMASOBSERVED_KEY = "kuali.atp.milestone.ChristmasObserved";
119
120
121 private static final Map<String, String> holidayNames = new LinkedHashMap<String, String>();
122 {
123 holidayNames.put(HOLIDAY_NEWYEARSDAY_KEY, "New Years Day");
124 holidayNames.put(HOLIDAY_MLKDAY_KEY, "Martin Luther King, Jr. Day");
125 holidayNames.put(HOLIDAY_PRESIDENTSDAY_KEY, "Presidents Day");
126 holidayNames.put(HOLIDAY_MEMORIALDAY_KEY, "Memorial Day");
127 holidayNames.put(HOLIDAY_INDEPENDENCEDAY_KEY, "Independence Day");
128 holidayNames.put(HOLIDAY_LABORDAY_KEY, "Labor Day");
129 holidayNames.put(HOLIDAY_VETERANSDAY_KEY, "Veterans Day");
130 holidayNames.put(HOLIDAY_THANKSGIVINGBREAK_KEY, "Thanksgiving Break");
131 holidayNames.put(HOLIDAY_CHRISTMAS_KEY, "Christmas Day");
132 holidayNames.put(HOLIDAY_NEWYEARSDAYOBSERVED_KEY, "New Years Day Observed");
133 holidayNames.put(HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY, "Independence Day Observed");
134 holidayNames.put(HOLIDAY_VETERANSDAYOBSERVED_KEY, "Veterans Day Observed");
135 holidayNames.put(HOLIDAY_CHRISTMASOBSERVED_KEY, "Christmas Day Observed");
136 }
137
138
139 private static final Map<String, String> observedHolidays = new LinkedHashMap<String, String>();
140 {
141 observedHolidays.put(HOLIDAY_NEWYEARSDAY_KEY, HOLIDAY_NEWYEARSDAYOBSERVED_KEY);
142 observedHolidays.put(HOLIDAY_MLKDAY_KEY, null);
143 observedHolidays.put(HOLIDAY_PRESIDENTSDAY_KEY, null);
144 observedHolidays.put(HOLIDAY_MEMORIALDAY_KEY, null);
145 observedHolidays.put(HOLIDAY_INDEPENDENCEDAY_KEY, HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY);
146 observedHolidays.put(HOLIDAY_LABORDAY_KEY, null);
147 observedHolidays.put(HOLIDAY_VETERANSDAY_KEY, HOLIDAY_VETERANSDAYOBSERVED_KEY);
148 observedHolidays.put(HOLIDAY_THANKSGIVINGBREAK_KEY, null);
149 observedHolidays.put(HOLIDAY_CHRISTMAS_KEY, HOLIDAY_CHRISTMASOBSERVED_KEY);
150 }
151
152
153 public static void main(String[] args) {
154 AcalReferenceDataParser parser = new AcalReferenceDataParser();
155 File inputFile = new File("/kuali/temp/Reference Insitution Data_ Terms - Data.tsv");
156 File outputFile = new File("/kuali/temp/output.sql");
157
158 parser.loadDataSet(inputFile);
159
160 int[] records = new int[] {35};
161 for (int record : records) {
162
163 }
164
165 parser.parse();
166 SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
167 for (Atp acal : parser.allAcals.values()) {
168 int startYear = Integer.parseInt(yearFormat.format(acal.getStartDate()));
169 int endYear = Integer.parseInt(yearFormat.format(acal.getEndDate()));
170 for (int year = startYear; year <= endYear; year++) {
171 Atp holiCal = parser.getHolidayCalendar(Integer.toString(year));
172 acal.getHolidayCalendars().add(holiCal);
173 }
174 }
175
176 parser.alterEndDates(new ArrayList<Atp>(parser.allAcals.values()));
177
178
179
180
181 SqlGenerator sqlGenerator = new SqlGenerator();
182 StringBuilder sql = new StringBuilder();
183 for (Atp acal : parser.allAcals.values()) {
184 sqlGenerator.getSqlForAcademicCalendar(sql, acal);
185 }
186 parser.writeToFile(outputFile, sql.toString());
187
188
189 }
190
191 private void parse() {
192 int i = 0;
193 for (Map<String, String> record : dataSet) {
194 i++;
195
196
197 Atp term = createTerm(record);
198
199
200 Atp acal = getAcalForTermId(record.get(RECORD_KEY));
201 List<Atp> terms = acal.getTerms();
202 terms.add(term);
203 }
204 for (Atp acal : allAcals.values()) {
205 List<Atp> terms = acal.getTerms();
206 Atp firstTerm = terms.get(0);
207 Atp lastTerm = terms.get(terms.size() - 1);
208 acal.setStartDate(firstTerm.getStartDate());
209 acal.setEndDate(lastTerm.getEndDate());
210 }
211 }
212
213 private Atp createTerm(Map<String, String> record) {
214 String recordKey = record.get(RECORD_KEY);
215 int year = getYear(recordKey);
216 int quarter = getQuarter(recordKey);
217 String termName = getAtpTypeName(quarter);
218
219 Atp term = new Atp();
220
221 term.setId((year + "" + termName).replaceAll(" ", "").toUpperCase());
222 term.setName(termName + " " + year);
223 term.setDescriptionPlain(termName + " " + year);
224 term.setDescriptionFormatted("<p>" + term.getDescriptionPlain() + "</p>");
225 term.setStartDate(parseDate(record.get(RECORD_FIRST_DAY)));
226 Date termEnd = parseDate(record.get(RECORD_LAST_DAY_EXAMS));
227 term.setEndDate(termEnd);
228 term.setType(getAtpTypeKey(quarter));
229 term.setAtpCode(termName.substring(0,3).toUpperCase() + year);
230
231 for (String keydateType : keydateNames.keySet()) {
232 term.addMilestone(getKeydateForTerm(keydateType, record));
233 }
234
235
236 if (3 == quarter) {
237 String subtermName = SESSION1_ATP_TYPE;
238 Atp subterm = new Atp();
239
240 subterm.setId((year + "" + subtermName).replaceAll(" ", "").toUpperCase());
241 subterm.setName(subtermName + " " + year);
242 subterm.setDescriptionPlain(subtermName + " " + year);
243 subterm.setDescriptionFormatted("<p>" + subterm.getDescriptionPlain() + "</p>");
244 subterm.setStartDate(parseDate(record.get(RECORD_SUB_1_FIRST_DAY)));
245 Date subTermEnd = parseDate(record.get(RECORD_SUB_1_LAST_DAY_CLASSES));
246 subterm.setEndDate(subTermEnd);
247 subterm.setType(SESSION1_ATP_TYPE_KEY);
248 subterm.setAtpCode(termName.substring(0, 3).toUpperCase() + "A" + year);
249
250 for (String keydateType : keydateNames.keySet()) {
251 subterm.addMilestone(getKeydateForSubterm1(keydateType, record));
252 }
253
254 term.getTerms().add(subterm);
255 }
256
257
258
259
260 if (3 == quarter) {
261 String subtermName = SESSION2_ATP_TYPE;
262 Atp subterm = new Atp();
263
264 subterm.setId((year + "" + subtermName).replaceAll(" ", "").toUpperCase());
265 subterm.setName(subtermName + " " + year);
266 subterm.setDescriptionPlain(subtermName + " " + year);
267 subterm.setDescriptionFormatted("<p>" + subterm.getDescriptionPlain() + "</p>");
268 subterm.setStartDate(parseDate(record.get(RECORD_SUB_2_FIRST_DAY)));
269 Date subTermEnd = parseDate(record.get(RECORD_SUB_2_LAST_DAY_EXAMS));
270 subterm.setEndDate(subTermEnd);
271 subterm.setType(SESSION2_ATP_TYPE_KEY);
272 subterm.setAtpCode(termName.substring(0, 3).toUpperCase() + "A" + year);
273
274 for (String keydateType : keydateNames.keySet()) {
275 subterm.addMilestone(getKeydateForSubterm2(keydateType, record));
276 }
277
278 term.getTerms().add(subterm);
279 }
280
281 addMilestoneRelations(term);
282 return term;
283 }
284
285 private Milestone getKeydateForTerm(String keydateType, Map<String, String> record) {
286 String recordKey = record.get(RECORD_KEY);
287 int termYear = getYear(recordKey);
288 String termName = getAtpTypeName(getQuarter(recordKey));
289 String keydateName = keydateNames.get(keydateType);
290
291 String id = (termYear + termName + keydateName).replaceAll(" ", "").toUpperCase();
292 String name = termName + " " + termYear + " " + keydateName;
293 String descriptionPlain = keydateName + " for " + termName + " " + termYear;
294 String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
295
296 Date start = null;
297 Date end = null;
298 boolean allDay;
299 boolean range;
300
301 if (KEYDATE_COURSE_SELECTION_PERIOD_END_KEY.equals(keydateType)) {
302 start = parseDate(record.get(RECORD_LAST_DAY_ADD));
303 if (start == null) {
304
305 return null;
306 }
307 allDay = true;
308 range = false;
309
310 } else if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keydateType)) {
311 start = parseDate(record.get(RECORD_FIRST_DAY));
312 if (start == null) {
313 throw new RuntimeException(recordKey);
314 }
315 end = parseDate(record.get(RECORD_LAST_DAY_CLASSES));
316 if (end == null) {
317 throw new RuntimeException(recordKey);
318 }
319 allDay = true;
320 range = true;
321
322 } else if (KEYDATE_FINAL_EXAM_PERIOD_KEY.equals(keydateType)) {
323 if (3 == getQuarter(record.get(RECORD_KEY))) {
324 return null;
325 }
326 start = parseDate(record.get(RECORD_LAST_DAY_CLASSES));
327 if (start == null) {
328 throw new RuntimeException(recordKey);
329 }
330 start = addToDate(start, Calendar.DATE, 1);
331 end = parseDate(record.get(RECORD_LAST_DAY_EXAMS));
332 if (end == null) {
333 throw new RuntimeException(recordKey);
334 }
335 allDay = true;
336 range = true;
337
338 } else if (KEYDATE_CENSUS_KEY.equals(keydateType)) {
339 start = parseDate(record.get(RECORD_CENSUS_DAY));
340 if (start == null) {
341 throw new RuntimeException(recordKey);
342 }
343 allDay = true;
344 range = false;
345
346 } else if (KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY.equals(keydateType)) {
347 start = parseDate(record.get(RECORD_LAST_DAY_DROP_NOT));
348 if (start == null) {
349 throw new RuntimeException(recordKey);
350 }
351 allDay = true;
352 range = false;
353
354 } else if (KEYDATE_DROP_DEADLINE_KEY.equals(keydateType)) {
355 start = parseDate(record.get(RECORD_DROP_UG));
356 if (start == null) {
357 throw new RuntimeException(recordKey);
358 }
359 allDay = true;
360 range = false;
361
362 } else if (KEYDATE_GRADES_DUE_KEY.equals(keydateType)) {
363 start = parseDate(record.get(RECORD_GRADE_SUBMIT_DEADLINE_DATE), record.get(RECORD_GRADE_SUBMIT_DEADLINE_TIME));
364 if (start == null) {
365 return null;
366 }
367 allDay = false;
368 range = false;
369
370 } else {
371 throw new RuntimeException(recordKey + " Keydate type not known.");
372 }
373
374 Milestone milestone = new Milestone(id, name, keydateType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
375 return milestone;
376 }
377
378 private Milestone getKeydateForSubterm1(String keydateType, Map<String, String> record) {
379 String recordKey = record.get(RECORD_KEY);
380 int termYear = getYear(recordKey);
381 String termName = SESSION1_ATP_TYPE;
382 String keydateName = keydateNames.get(keydateType);
383
384 String id = (termYear + termName + keydateName).replaceAll(" ", "").toUpperCase();
385 String name = termName + " " + termYear + " " + keydateName;
386 String descriptionPlain = keydateName + " for " + termName + " " + termYear;
387 String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
388
389 Date start = null;
390 Date end = null;
391 boolean allDay;
392 boolean range;
393
394 if (KEYDATE_COURSE_SELECTION_PERIOD_END_KEY.equals(keydateType)) {
395 start = parseDate(record.get(RECORD_SUB_1_LAST_DAY_ADD));
396 if (start == null) {
397
398 return null;
399 }
400 allDay = true;
401 range = false;
402
403 } else if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keydateType)) {
404 start = parseDate(record.get(RECORD_SUB_1_FIRST_DAY));
405 if (start == null) {
406 throw new RuntimeException(recordKey);
407 }
408 end = parseDate(record.get(RECORD_SUB_1_LAST_DAY_CLASSES));
409 if (end == null) {
410 end = parseDate(record.get(RECORD_SUB_2_FIRST_DAY));
411 end = addToDate(end, Calendar.DATE, -1);
412 if (end == null) {
413 throw new RuntimeException(record.get(RECORD_KEY));
414 }
415 }
416 allDay = true;
417 range = true;
418
419 } else if (KEYDATE_FINAL_EXAM_PERIOD_KEY.equals(keydateType)) {
420 return null;
421
422 } else if (KEYDATE_CENSUS_KEY.equals(keydateType)) {
423 start = parseDate(record.get(RECORD_SUB_1_CENSUS_DAY));
424 if (start == null) {
425 throw new RuntimeException(recordKey);
426 }
427 allDay = true;
428 range = false;
429
430 } else if (KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY.equals(keydateType)) {
431 start = parseDate(record.get(RECORD_SUB_1_LAST_DAY_DROP_NOT));
432 if (start == null) {
433 throw new RuntimeException(recordKey);
434 }
435 allDay = true;
436 range = false;
437
438 } else if (KEYDATE_DROP_DEADLINE_KEY.equals(keydateType)) {
439 start = parseDate(record.get(RECORD_SUB_1_DROP_UG));
440 if (start == null) {
441 throw new RuntimeException(recordKey);
442 }
443 allDay = true;
444 range = false;
445
446 } else if (KEYDATE_GRADES_DUE_KEY.equals(keydateType)) {
447 start = parseDate(record.get(RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_DATE), record.get(RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_TIME));
448 if (start == null) {
449 return null;
450 }
451 allDay = false;
452 range = false;
453
454 } else {
455 throw new RuntimeException(recordKey + " Keydate type not known.");
456 }
457
458 Milestone milestone = new Milestone(id, name, keydateType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
459 return milestone;
460 }
461
462 private Milestone getKeydateForSubterm2(String keydateType, Map<String, String> record) {
463 String recordKey = record.get(RECORD_KEY);
464 int termYear = getYear(recordKey);
465 String termName = SESSION2_ATP_TYPE;
466 String keydateName = keydateNames.get(keydateType);
467
468 String id = (termYear + termName + keydateName).replaceAll(" ", "").toUpperCase();
469 String name = termName + " " + termYear + " " + keydateName;
470 String descriptionPlain = keydateName + " for " + termName + " " + termYear;
471 String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
472
473 Date start = null;
474 Date end = null;
475 boolean allDay;
476 boolean range;
477
478 if (KEYDATE_COURSE_SELECTION_PERIOD_END_KEY.equals(keydateType)) {
479 start = parseDate(record.get(RECORD_SUB_2_LAST_DAY_ADD));
480 if (start == null) {
481
482 return null;
483 }
484 allDay = true;
485 range = false;
486
487 } else if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keydateType)) {
488 start = parseDate(record.get(RECORD_SUB_2_FIRST_DAY));
489 if (start == null) {
490 throw new RuntimeException(recordKey);
491 }
492 end = parseDate(record.get(RECORD_SUB_2_LAST_DAY_CLASSES));
493 if (end == null) {
494 throw new RuntimeException(recordKey);
495 }
496 allDay = true;
497 range = true;
498
499 } else if (KEYDATE_FINAL_EXAM_PERIOD_KEY.equals(keydateType)) {
500 return null;
501
502 } else if (KEYDATE_CENSUS_KEY.equals(keydateType)) {
503 return null;
504
505 } else if (KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY.equals(keydateType)) {
506 start = parseDate(record.get(RECORD_SUB_2_LAST_DAY_DROP_NOT));
507 if (start == null) {
508 throw new RuntimeException(recordKey);
509 }
510 allDay = true;
511 range = false;
512
513 } else if (KEYDATE_DROP_DEADLINE_KEY.equals(keydateType)) {
514 start = parseDate(record.get(RECORD_SUB_2_DROP_UG));
515 if (start == null) {
516 throw new RuntimeException(recordKey);
517 }
518 allDay = true;
519 range = false;
520
521 } else if (KEYDATE_GRADES_DUE_KEY.equals(keydateType)) {
522 start = parseDate(record.get(RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_DATE), record.get(RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_TIME));
523 if (start == null) {
524 return null;
525 }
526 allDay = false;
527 range = false;
528
529 } else {
530 throw new RuntimeException("Keydate type not known.");
531 }
532
533 Milestone milestone = new Milestone(id, name, keydateType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
534 return milestone;
535 }
536
537 private Date addToDate(Date date, int field, int amount) {
538 if (date == null) {
539 return null;
540 }
541 Calendar cal = new GregorianCalendar();
542 cal.setTime(date);
543 cal.add(field, amount);
544 return cal.getTime();
545 }
546 private Date parseDate(String dateString) {
547 Date date = null;
548 if (dateString != null && dateString.length() > 0) {
549 String[] dateSegments = dateString.split("/");
550 int day = Integer.parseInt(dateSegments[1]);
551 int month = Integer.parseInt(dateSegments[0]) - 1;
552 int year = Integer.parseInt(dateSegments[2]);
553 if (year > 99) {
554 throw new RuntimeException("Unexpected year format: " + dateString);
555 }
556 if (year > 20) {
557 year = 1900 + year;
558 } else {
559 year = 2000 + year;
560 }
561 Calendar cal = new GregorianCalendar(year, month, day);
562 date = cal.getTime();
563 }
564 return date;
565 }
566
567 private Date parseDate(String dateString, String timeString) {
568 Date date = null;
569 if (dateString != null && dateString.length() > 0 && timeString != null && timeString.length() > 0) {
570 while (timeString.length() < 6) {
571 timeString = "0" + timeString;
572 }
573
574 int hour = Integer.parseInt(timeString.substring(0,2));
575 int minute = Integer.parseInt(timeString.substring(2,4));
576 int second = Integer.parseInt(timeString.substring(4,6));
577
578 Calendar cal = new GregorianCalendar();
579 cal.setTime(parseDate(dateString));
580 cal.set(Calendar.HOUR_OF_DAY, hour);
581 cal.set(Calendar.MINUTE, minute);
582 cal.set(Calendar.SECOND, second);
583 date = cal.getTime();
584 }
585 return date;
586 }
587
588 int getQuarter(String recordKey) {
589 if (recordKey.length() != 5) {
590 throw new IllegalArgumentException("recordKey: " + recordKey);
591 }
592 int quater = Integer.parseInt(recordKey.substring(4, 5));
593 return quater;
594 }
595 int getYear(String recordKey) {
596 if (recordKey.length() != 5) {
597 throw new IllegalArgumentException("recordKey: " + recordKey);
598 }
599 int year = Integer.parseInt(recordKey.substring(0, 4));
600 return year;
601 }
602 String getAtpTypeName(int quarter) {
603 String name = null;
604 switch (quarter) {
605 case 1: name = WINTER_ATP_TYPE; break;
606 case 2: name = SPRING_ATP_TYPE; break;
607 case 3: name = SUMMER_ATP_TYPE; break;
608 case 4: name = FALL_ATP_TYPE; break;
609 }
610 return name;
611 }
612 String getAtpTypeKey(int quarter) {
613 String name = null;
614 switch (quarter) {
615 case 1: name = WINTER_ATP_TYPE_KEY; break;
616 case 2: name = SPRING_ATP_TYPE_KEY; break;
617 case 3: name = SUMMER_ATP_TYPE_KEY; break;
618 case 4: name = FALL_ATP_TYPE_KEY; break;
619 }
620 return name;
621 }
622
623 private Atp getAcalForTermId(String termId) {
624 if (termId.length() != 5) {
625 throw new IllegalArgumentException("termId: " + termId);
626 }
627 int year = getYear(termId);
628 int quater = getQuarter(termId);
629 if (quater < 3) {
630 year--;
631 }
632 Atp acal = allAcals.get(String.valueOf(year));
633 if (acal == null) {
634 acal = new Atp();
635 allAcals.put(String.valueOf(year), acal);
636 acal.setId(year + "" + (year + 1) + "ACADEMICCALENDAR");
637 acal.setType("kuali.atp.type.AcademicCalendar");
638 acal.setAdminOrgId("102");
639 acal.setAtpCode("ACAL"+year);
640 acal.setName(year + "/" + (year + 1) + " Academic Calendar");
641 acal.setDescriptionPlain("Academic Calendar for " + year + "/" + (year + 1));
642 acal.setDescriptionFormatted("<p>" + acal.getDescriptionPlain() + "</p>");
643 }
644 return acal;
645 }
646
647 private void loadDataSet(File file) {
648 DataInputStream in = null;
649 try {
650 FileInputStream fstream = new FileInputStream(file);
651 in = new DataInputStream(fstream);
652 BufferedReader br = new BufferedReader(new InputStreamReader(in));
653
654 String[] headers = null;
655 String line;
656 int row = 0;
657 while ((line = br.readLine()) != null) {
658 String[] columnValues = line.split("\t");
659 if (0 == row) {
660 headers = columnValues;
661 } else {
662 Map<String, String> map = new LinkedHashMap<String, String>();
663 for (int i = 0; i < headers.length; i++) {
664 map.put(headers[i], columnValues[i]);
665 }
666 dataSet.add(map);
667 }
668 row++;
669 }
670 } catch (Exception e) {
671 e.printStackTrace();
672 } finally {
673 if (in != null) {
674 try {
675 in.close();
676 } catch (IOException e) {
677 e.printStackTrace();
678 }
679 }
680 }
681 }
682
683 private void writeToFile(File file, String content) {
684 try{
685 FileWriter fstream = new FileWriter(file);
686 BufferedWriter out = new BufferedWriter(fstream);
687 out.write(content);
688 out.close();
689 } catch (Exception e){
690 e.printStackTrace();
691 }
692 }
693
694 public String getReport(List<String> years) {
695 StringBuilder builder = new StringBuilder();
696 String tab = "\t";
697 String line = "\n";
698
699
700 for (String year : years) {
701 Atp acal = allAcals.get(year);
702 addReportLineForAtp(builder, acal);
703 for (Atp holiCal : acal.getHolidayCalendars()) {
704 addReportLineForAtp(builder, holiCal);
705 for (Milestone holiday : holiCal.getMilestones()) {
706 addReportLineForMilestone(builder, holiday);
707 }
708 }
709 for (Atp term : acal.getTerms()) {
710 addReportLineForAtp(builder, term);
711 for (Milestone milestone : term.getMilestones()) {
712 addReportLineForMilestone(builder, milestone);
713 }
714 for (Atp subTerm : term.getTerms()) {
715 addReportLineForAtp(builder, subTerm);
716 for (Milestone milestone : subTerm.getMilestones()) {
717 addReportLineForMilestone(builder, milestone);
718 }
719
720 }
721 }
722 }
723 return builder.toString();
724 }
725
726
727 private StringBuilder addReportLineForAtp(StringBuilder builder, Atp atp) {
728 String tab = "\t";
729 String line = "\n";
730
731 if (builder == null) {
732 builder = new StringBuilder();
733 }
734
735 builder.append(atp.getName()).append(tab);
736
737 Date startDate = atp.getStartDate();
738 builder.append(getDate(startDate)).append(tab);
739 builder.append(tab);
740
741 Date endDate = atp.getEndDate();
742 builder.append(getDate(endDate)).append(tab);
743 builder.append(tab);
744
745 builder.append(atp.getType());
746
747 builder.append(line);
748 return builder;
749 }
750
751 private StringBuilder addReportLineForMilestone(StringBuilder builder, Milestone milestone) {
752 String tab = "\t";
753 String line = "\n";
754
755 if (builder == null) {
756 builder = new StringBuilder();
757 }
758
759 builder.append(milestone.getName()).append(tab);
760
761 Date startDate = milestone.getStartDate();
762 builder.append(getDate(startDate)).append(tab);
763 if (!milestone.isAllDay()) {
764 builder.append(getTime(startDate));
765 }
766 builder.append(tab);
767
768 Date endDate = milestone.getEndDate();
769 if (milestone.isDateRange()) {
770 if (endDate == null) {
771 throw new RuntimeException("End date not supplied for range. " + milestone.getId());
772 }
773 builder.append(getDate(endDate)).append(tab);
774 if (!milestone.isAllDay()) {
775 builder.append(getTime(endDate));
776 }
777 } else {
778 builder.append(tab);
779 if (endDate != null) {
780 throw new RuntimeException("End date not expected for non range. " + milestone.getId());
781 }
782 }
783 builder.append(tab);
784 builder.append(milestone.getType());
785
786 builder.append(line);
787 return builder;
788 }
789
790 private String getDate(Date date) {
791 SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
792 if (date == null) {
793 return "";
794 }
795 return dateFormat.format(date);
796 }
797 private String getTime(Date date) {
798 SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
799 return timeFormat.format(date);
800 }
801
802 public Atp getHolidayCalendar(String yearString) {
803 Atp holiCal = allHolidayCalendars.get(yearString);
804 int year = Integer.parseInt(yearString);
805 if (null == holiCal) {
806 holiCal = new Atp();
807 holiCal.setId(year + "HOLIDAYCALENDAR");
808 holiCal.setName(year + " Holiday Calendar");
809 holiCal.setStartDate(new GregorianCalendar(year, Calendar.JANUARY, 1).getTime());
810 holiCal.setEndDate(new GregorianCalendar(year, Calendar.DECEMBER, 31).getTime());
811 holiCal.setDescriptionPlain("Holiday Calendar for " + year);
812 holiCal.setDescriptionFormatted("<p>" + holiCal.getDescriptionPlain() + "</p>");
813 holiCal.setType("kuali.atp.type.HolidayCalendar");
814 holiCal.setAtpCode("HCAL" + year);
815 holiCal.setAdminOrgId("102");
816
817 for (String holidayTypeKey : observedHolidays.keySet()) {
818 Milestone holiday = getHoliday(holidayTypeKey, year);
819 holiCal.addMilestone(holiday);
820 Milestone observedholiday = getHoliday(observedHolidays.get(holidayTypeKey), year);
821 if (null != observedholiday) {
822 holiCal.addMilestone(observedholiday);
823 }
824 }
825 allHolidayCalendars.put(yearString, holiCal);
826 }
827 return holiCal;
828 }
829
830 public Milestone getHoliday(String holidayType, int year) {
831 if (null == holidayType) {
832 return null;
833 }
834
835 String[] typeSplit = holidayType.split("\\.");
836
837 String id = (year + typeSplit[typeSplit.length-1]).toUpperCase();
838 String name = holidayNames.get(holidayType);
839 String descriptionPlain = name + " for " + year;
840 String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
841
842 Date start = null;
843 Date end = null;
844 boolean allDay = true;
845 boolean range = false;
846
847 if (HOLIDAY_NEWYEARSDAY_KEY.equals(holidayType)) {
848 Calendar cal = new GregorianCalendar(year, Calendar.JANUARY, 1);
849 start = cal.getTime();
850 } else if (HOLIDAY_NEWYEARSDAYOBSERVED_KEY.equals(holidayType)) {
851 Milestone milestone = getHoliday(HOLIDAY_NEWYEARSDAY_KEY, year);
852 int offset = getWeekendOffset(milestone.getStartDate());
853 if (0 == offset) {
854 return null;
855 } else {
856 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
857 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
858 }
859 } else if (HOLIDAY_MLKDAY_KEY.equals(holidayType)) {
860 start = getNthDayOfWeekInMonth(3, Calendar.MONDAY, Calendar.JANUARY, year);
861 } else if (HOLIDAY_PRESIDENTSDAY_KEY.equals(holidayType)) {
862 start = getNthDayOfWeekInMonth(3, Calendar.MONDAY, Calendar.FEBRUARY, year);
863 } else if (HOLIDAY_MEMORIALDAY_KEY.equals(holidayType)) {
864 start = getNthDayOfWeekInMonth(1, Calendar.MONDAY, Calendar.JUNE, year);
865 start = addToDate(start, Calendar.DATE, -7);
866 } else if (HOLIDAY_INDEPENDENCEDAY_KEY.equals(holidayType)) {
867 start = new GregorianCalendar(year, Calendar.JULY, 4).getTime();
868 } else if (HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY.equals(holidayType)) {
869 Milestone milestone = getHoliday(HOLIDAY_INDEPENDENCEDAY_KEY, year);
870 int offset = getWeekendOffset(milestone.getStartDate());
871 if (0 == offset) {
872 return null;
873 } else {
874 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
875 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
876 }
877 } else if (HOLIDAY_LABORDAY_KEY.equals(holidayType)) {
878 start = getNthDayOfWeekInMonth(1, Calendar.MONDAY, Calendar.SEPTEMBER, year);
879 } else if (HOLIDAY_VETERANSDAY_KEY.equals(holidayType)) {
880 start = new GregorianCalendar(year, Calendar.NOVEMBER, 11).getTime();
881 } else if (HOLIDAY_VETERANSDAYOBSERVED_KEY.equals(holidayType)) {
882 Milestone milestone = getHoliday(HOLIDAY_VETERANSDAY_KEY, year);
883 int offset = getWeekendOffset(milestone.getStartDate());
884 if (0 == offset) {
885 return null;
886 } else {
887 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
888 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
889 }
890 } else if (HOLIDAY_THANKSGIVINGBREAK_KEY.equals(holidayType)) {
891 start = getNthDayOfWeekInMonth(4, Calendar.THURSDAY, Calendar.NOVEMBER, year);
892 end = addToDate(start, Calendar.DATE, 1);
893 range = true;
894 } else if (HOLIDAY_CHRISTMAS_KEY.equals(holidayType)) {
895 start = new GregorianCalendar(year, Calendar.DECEMBER, 25).getTime();
896 } else if (HOLIDAY_CHRISTMASOBSERVED_KEY.equals(holidayType)) {
897 Milestone milestone = getHoliday(HOLIDAY_CHRISTMAS_KEY, year);
898 int offset = getWeekendOffset(milestone.getStartDate());
899 if (0 == offset) {
900 return null;
901 } else {
902 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
903 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
904 }
905 } else {
906 throw new RuntimeException("Holiday type not known. " + holidayType);
907 }
908
909 Milestone milestone = new Milestone(id, name, holidayType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
910 return milestone;
911 }
912
913 private void addMilestoneRelations(Atp term) {
914 List<Milestone> keyDates = term.getMilestones();
915 Milestone instructionperiod = null;
916 for (Milestone keyDate : keyDates) {
917 if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keyDate.getType())) {
918 instructionperiod = keyDate;
919 break;
920 }
921 }
922 if (null == instructionperiod) {
923 throw new RuntimeException("Could not find instruction period for term: " + term.getId());
924 }
925 for (Milestone keyDate : keyDates) {
926 if (relativeToInstructionPeriod.contains(keyDate.getType())) {
927 keyDate.setRelative(true);
928 keyDate.setRelativeMilestoneId(instructionperiod.getId());
929 } else {
930 keyDate.setRelative(false);
931 }
932 }
933
934 for (Atp atp : term.getTerms()) {
935 addMilestoneRelations(atp);
936 }
937 }
938
939 private void alterEndDates(List<Atp> acals) {
940 for (int i = acals.size()-1; i >= 0 ; i--) {
941 Atp acal = acals.get(i);
942 if (i > 0) {
943 Atp prevAcal = acals.get(i-1);
944 Date start = acal.getStartDate();
945 Date prevEndDate = addToDate(start, Calendar.DATE, -1);
946 prevAcal.setEndDate(prevEndDate);
947 List<Atp> prevTerms = prevAcal.getTerms();
948 if (!prevTerms.isEmpty()) {
949 Atp lastPrevTerm = prevTerms.get(prevTerms.size()-1);
950 lastPrevTerm.setEndDate(prevEndDate);
951 List<Atp> prevSubTerms = lastPrevTerm.getTerms();
952 if (!prevSubTerms.isEmpty()) {
953 Atp lastPrevTermSubTerm = prevSubTerms.get(prevSubTerms.size()-1);
954 lastPrevTermSubTerm.setEndDate(prevEndDate);
955 }
956 }
957 }
958
959 List<Atp> terms = acal.getTerms();
960 for (int j = terms.size()-1; j >= 0; j--) {
961 Atp term = terms.get(j);
962 if (j > 0) {
963 Atp prevTerm = terms.get(j-1);
964 Date prevEndDate = addToDate(term.getStartDate(), Calendar.DATE, -1);
965 prevTerm.setEndDate(prevEndDate);
966 List<Atp> prevSubTerms = prevTerm.getTerms();
967 if (!prevSubTerms.isEmpty()) {
968 Atp lastPrevSubTerm = prevSubTerms.get(prevSubTerms.size()-1);
969 lastPrevSubTerm.setEndDate(prevEndDate);
970 }
971 }
972 }
973
974 for (int j = terms.size()-1; j >= 0; j--) {
975 Atp term = terms.get(j);
976 if (j > 0) {
977 Atp prevTerm = terms.get(j-1);
978 Date start = term.getStartDate();
979 prevTerm.setEndDate(addToDate(start, Calendar.DATE, -1));
980 }
981 List<Atp> subTerms = term.getTerms();
982 for (int k = subTerms.size()-1; k >= 0; k--) {
983 Atp subTerm = subTerms.get(k);
984 if (k > 0) {
985 Atp prevSubTerm = subTerms.get(k-1);
986 Date start = subTerm.getStartDate();
987 prevSubTerm.setEndDate(addToDate(start, Calendar.DATE, -1));
988 }
989 }
990 }
991 }
992 }
993
994 private int getWeekendOffset(Date date) {
995 Calendar cal = new GregorianCalendar();
996 cal.setTime(date);
997 int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
998 if (Calendar.SATURDAY == dayOfWeek) {
999 return -1;
1000 } else if (Calendar.SUNDAY == dayOfWeek) {
1001 return 1;
1002 } else {
1003 return 0;
1004 }
1005 }
1006
1007 private Date getNthDayOfWeekInMonth(int n, int dayOfWeek, int month, int year) {
1008 Calendar cal = new GregorianCalendar(year, month, 1);
1009 if (dayOfWeek != cal.get(Calendar.DAY_OF_WEEK)) {
1010 int daysUntil = (dayOfWeek - cal.get(Calendar.DAY_OF_WEEK) + 7) % 7;
1011 cal.add(Calendar.DATE, daysUntil);
1012 }
1013 cal.add(Calendar.DATE, (n-1) * 7);
1014 return cal.getTime();
1015 }
1016
1017 }