/* * Copyright 2012 Shared Learning Collaborative, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.slc.sli.dashboard.manager.impl; import java.text.DateFormat; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.lang.WordUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.slc.sli.dashboard.entity.Config; import org.slc.sli.dashboard.entity.Config.Data; import org.slc.sli.dashboard.entity.GenericEntity; import org.slc.sli.dashboard.entity.util.GenericEntityComparator; import org.slc.sli.dashboard.entity.util.GenericEntityEnhancer; import org.slc.sli.dashboard.manager.ApiClientManager; import org.slc.sli.dashboard.manager.EntityManager; import org.slc.sli.dashboard.manager.PopulationManager; import org.slc.sli.dashboard.util.CacheableUserData; import org.slc.sli.dashboard.util.Constants; import org.slc.sli.dashboard.util.TimedLogic; /** * Facilitates creation of logical aggregations of EdFi entities/associations * such as a student summary comprised of student profile, * program, and assessment information in order to deliver the * Population Summary interaction. * * @author Robert Bloh * */ public class PopulationManagerImpl extends ApiClientManager implements PopulationManager { private static final String ATTENDANCE_TARDY = "Tardy"; private static final String ATTENDANCE_ABSENCE = "Absence"; private static final int DEFAULT_YEARS_BACK = 3; private static final int NO_LIMIT = -1; private static Logger log = LoggerFactory.getLogger(PopulationManagerImpl.class); @Autowired private EntityManager entityManager; public PopulationManagerImpl() { } /* * (non-Javadoc) * * @see * org.slc.sli.dashboard.manager.PopulationManagerI#getAssessments(java.lang.String, * java.util.List) */ @Override public List<GenericEntity> getAssessments(String token, List<GenericEntity> studentSummaries) { Set<GenericEntity> assessments = new TreeSet<GenericEntity>(new Comparator<GenericEntity>() { @Override public int compare(GenericEntity att1, GenericEntity att2) { return (att2.getString("id")).compareTo(att1.getString("id")); } }); for (GenericEntity studentSummary : studentSummaries) { List<Map<String, Object>> studentAssessments = (List<Map<String, Object>>) studentSummary .get(Constants.ATTR_STUDENT_ASSESSMENTS); for (Map<String, Object> studentAssessment : studentAssessments) { try { GenericEntity assessment = new GenericEntity((Map) studentAssessment.get("assessments")); assessments.add(assessment); } catch (ClassCastException cce) { log.warn(cce.getMessage()); } } } return new ArrayList<GenericEntity>(assessments); } /* * (non-Javadoc) * * @see * org.slc.sli.dashboard.manager.PopulationManagerI#getStudentSummaries(java.lang. * String, java.util.List, org.slc.sli.config.ViewConfig, java.lang.String, * java.lang.String) */ @Override public List<GenericEntity> getStudentSummaries(String token, List<String> studentIds, String sessionId, String sectionId) { long startTime = System.nanoTime(); // Initialize student summaries List<GenericEntity> studentSummaries = entityManager.getStudents(token, sectionId); log.warn("@@@@@@@@@@@@@@@@@@ Benchmark for student section view: {}", (System.nanoTime() - startTime) * 1.0e-9); return studentSummaries; } /* * (non-Javadoc) * * @see * org.slc.sli.dashboard.manager.PopulationManagerI#getListOfStudents(java.lang.String * , java.lang.Object, org.slc.sli.dashboard.entity.Config.Data) */ @Override @CacheableUserData public GenericEntity getListOfStudents(String token, Object sectionId, Config.Data config) { String id = (String) sectionId; // get student summary data List<GenericEntity> studentSummaries = getStudentSummaries(token, null, null, id); // apply assmt filters and flatten assmt data structure for easy // fetching applyAssessmentFilters(studentSummaries, config); // data enhancements enhanceListOfStudents(studentSummaries, id); GenericEntity result = new GenericEntity(); Collections.sort(studentSummaries, STUDENT_COMPARATOR); result.put(Constants.ATTR_STUDENTS, studentSummaries); return result; } /** * Make enhancements that make it easier for front-end javascript to use the * data * * @param studentSummaries */ public void enhanceListOfStudents(List<GenericEntity> studentSummaries, String sectionId) { if (studentSummaries != null) { for (GenericEntity student : studentSummaries) { if (student == null) { continue; } // clean out some unneeded gunk scrubStudentData(student); // add full name addFullName(student); // add the final grade addFinalGrades(student, sectionId); // add the grade book addCurrentSessionGrades(student, sectionId); // transform assessment score format transformAssessmentFormat(student); // tally up attendance data tallyAttendanceData(student); } } } /** * Create an attribute for the full student name (first name + last name) * * @param student */ public void addFullName(GenericEntity student) { Map name = (Map) student.get(Constants.ATTR_NAME); if (name != null) { String fullName = (String) name.get(Constants.ATTR_FIRST_NAME) + " " + (String) name.get(Constants.ATTR_LAST_SURNAME); name.put(Constants.ATTR_FULL_NAME, fullName); } } /** * Tally up individual attendance events. Front-end needs to show aggregated * results. * * @param student */ public void tallyAttendanceData(GenericEntity student) { Map<String, Object> attendanceBody = (Map<String, Object>) student.get(Constants.ATTR_STUDENT_ATTENDANCES); if (attendanceBody != null) { List<Map<String, Object>> attendances = (List<Map<String, Object>>) attendanceBody .get(Constants.ATTR_STUDENT_ATTENDANCES); int absenceCount = 0; int tardyCount = 0; if (attendances != null && attendances.size() > 0) { for (Map attendance : attendances) { String eventCategory = (String) attendance.get(Constants.ATTR_ATTENDANCE_EVENT_CATEGORY); if (eventCategory.contains(ATTENDANCE_ABSENCE)) { absenceCount++; } else if (eventCategory.contains(ATTENDANCE_TARDY)) { tardyCount++; } } int attendanceRate = Math .round((((float) (attendances.size() - absenceCount)) / attendances.size()) * 100); int tardyRate = Math.round((((float) tardyCount) / attendances.size()) * 100); attendanceBody.remove(Constants.ATTR_STUDENT_ATTENDANCES); attendanceBody.put(Constants.ATTR_ABSENCE_COUNT, absenceCount); attendanceBody.put(Constants.ATTR_TARDY_COUNT, tardyCount); attendanceBody.put(Constants.ATTR_ATTENDANCE_RATE, attendanceRate); attendanceBody.put(Constants.ATTR_TARDY_RATE, tardyRate); } } } /** * Modify the data structure for assessments, for front-end convenience * * @param student */ public void transformAssessmentFormat(GenericEntity student) { Map studentAssmtAssocs = (Map) student.get(Constants.ATTR_ASSESSMENTS); if (studentAssmtAssocs == null) { return; } Collection<Map> assmtResults = studentAssmtAssocs.values(); for (Map assmtResult : assmtResults) { if (assmtResult == null) { continue; } // for each score result, create a new attribute that makes the // score easily accessible without looping through this list List<Map> scoreResults = (List<Map>) assmtResult.get(Constants.ATTR_SCORE_RESULTS); if (scoreResults != null) { for (Map scoreResult : scoreResults) { String type = (String) scoreResult.get(Constants.ATTR_ASSESSMENT_REPORTING_METHOD); String result = (String) scoreResult.get(Constants.ATTR_RESULT); assmtResult.put(type, result); } } // create a new attribute "perfLevel" List<List<Map>> perfLevelsDescs = (List<List<Map>>) assmtResult .get(Constants.ATTR_PERFORMANCE_LEVEL_DESCRIPTOR); if (perfLevelsDescs != null) { for (List<Map> perfLevelsDesc : perfLevelsDescs) { if (perfLevelsDesc != null && perfLevelsDesc.size() > 0) { String perfLevel = (String) perfLevelsDesc.get(0).get(Constants.ATTR_CODE_VALUE); assmtResult.put(Constants.ATTR_PERF_LEVEL, perfLevel); } } } } } /** * Clean out some student data, so we don't pass too much unnecessary stuff * to the front-end * * @param student */ public void scrubStudentData(GenericEntity student) { student.remove(Constants.ATTR_LINKS); } /** * Grabs the subject area from the data based on the section ID. * * @param stuSectAssocs * @param sectionId * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) private String getSubjectArea(List<Map<String, Object>> stuSectAssocs, String sectionId) { String subjectArea = null; for (Map<String, Object> assoc : stuSectAssocs) { if (sectionId.equalsIgnoreCase((String) assoc.get(Constants.ATTR_SECTION_ID))) { Map<String, Object> sections = (Map) assoc.get(Constants.ATTR_SECTIONS); subjectArea = getSubjectArea(sections); break; } } return subjectArea; } /** * Grabs the Subject Area from a section. * * @param sections * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) private String getSubjectArea(Map<String, Object> sections) { if (sections == null) { return null; } Map<String, Object> courses = (Map) sections.get(Constants.ATTR_COURSES); if (courses == null) { return null; } return (String) courses.get(Constants.ATTR_SUBJECTAREA); } /** * * Finds the chronologically (strictly) earlier date on the list as compared to the anchorDate. * Note that this method assumes the dates list has been chronologically sorted, the * anchorDate is contained on it, and it has no "null" entries. The input parameters themselves * can be null, in which case null is returned. * * @param dates * @param anchorDate */ @SuppressWarnings({ "unchecked", "rawtypes" }) private Date findPrevDate(List<Date> dates, Date anchorDate) { if (anchorDate == null || dates == null) { return null; } Date prevDate = null; ListIterator<Date> li = dates.listIterator(dates.indexOf(anchorDate)); while (li.hasPrevious()) { Date d = li.previous(); if (d.before(anchorDate)) { prevDate = d; break; } } return prevDate; } /** * Extracts grades from transcriptAssociationRecord based on sections in the * past. For each section where a transcript with final letter grade * exist, the grade is added to the list of grades for the semester. * * @param student * @param interSections * @param stuTransAssocs * @param curSessionEndDate */ @SuppressWarnings({ "unchecked", "rawtypes" }) private void addSemesterFinalGrades(GenericEntity student, List<Map<String, Object>> interSections, List<Map<String, Object>> stuTransAssocs, Date curSessionEndDate) { // populate the end dates for all the relevant sections List<Date> sectionDates = new LinkedList<Date>(); sectionDates.add(curSessionEndDate); for (Map<String, Object> section : interSections) { List<Date> dates = getSessionDates(section); Date sectionEndDate = (dates == null) ? null : dates.get(1); if (sectionEndDate != null) { sectionDates.add(sectionEndDate); } } Collections.sort(sectionDates); // find the related sections chronologically preceding the current one on the list Date prevDate = findPrevDate(sectionDates, curSessionEndDate); Date prevPrevDate = findPrevDate(sectionDates, prevDate); // Iterate through the course Id's and grab transcripts grades, once // we have NUMBER_OF_SEMESTERS transcript grades, we're done for (Map<String, Object> section : interSections) { if (section != null) { Map<String, Object> course = ((Map<String, Object>) section.get(Constants.ATTR_COURSES)); if (course != null) { String courseId = (String) course.get(Constants.ATTR_ID); // we need to keep track of special cases, e.g. previous semester and two // semesters ago // data List<Date> dates = getSessionDates(section); Date sectionEndDate = (dates == null) ? null : dates.get(1); String tag; if (sectionEndDate != null) { if (prevDate != null && prevDate.equals(sectionEndDate)) { // this is the previous semester's section tag = "previousSemester"; } else if (prevPrevDate != null && prevPrevDate.equals(sectionEndDate)) { // this is two semesters ago tag = "twoSemestersAgo"; } else { // this is neither of the cases of interest continue; } } else { // no section end date means we cannot determine where this data belongs continue; } // Find the correct course. If that course is found in // the transcript, then record that letter grade to the // semesterScores. for (Map<String, Object> assoc : stuTransAssocs) { if (courseId.equalsIgnoreCase((String) assoc.get(Constants.ATTR_COURSE_ID))) { String finalLetterGrade = (String) assoc.get(Constants.ATTR_FINAL_LETTER_GRADE); String courseTitle = (String) course.get(Constants.ATTR_COURSE_TITLE); if (finalLetterGrade != null) { Map<String, Object> grade = new LinkedHashMap<String, Object>(); grade.put(Constants.SECTION_LETTER_GRADE, finalLetterGrade); grade.put(Constants.SECTION_COURSE, courseTitle); List<Map<String, Object>> semesterScores = (List<Map<String, Object>>) student.get(tag); if (semesterScores == null) { semesterScores = new ArrayList<Map<String, Object>>(); } semesterScores.add(grade); student.put(tag, semesterScores); break; } } } } } } } /** * This method adds the final grades of a student to the student data. It * will only grab the latest two grades. Ideally we would filter on subject * area, but there is currently no subject area data in the SDS. * * @param student */ @SuppressWarnings({ "unchecked" }) private void addFinalGrades(GenericEntity student, String sectionId) { try { Map<String, Object> transcripts = (Map<String, Object>) student.get(Constants.ATTR_TRANSCRIPT); if (transcripts == null) { return; } /* * For each student section association, we have to determine if it is in the same * subject area as the sectionid passed. If it is then we add it to our List. * Once we have a list of sections. We can grab all of the semester grades * for those sections whose subject area intersect. */ List<Map<String, Object>> stuSectAssocs = (List<Map<String, Object>>) transcripts .get(Constants.ATTR_STUDENT_SECTION_ASSOC); if (stuSectAssocs == null) { return; } String subjectArea = getSubjectArea(stuSectAssocs, sectionId); List<Date> dates = getSessionDates(stuSectAssocs, sectionId); Date curSessionEndDate = (dates == null) ? null : dates.get(1); if (curSessionEndDate == null) { // if we have no current session end date, we cannot determine what the previous // semesters were return; } List<Map<String, Object>> interSections = new ArrayList<Map<String, Object>>(); for (Map<String, Object> assoc : stuSectAssocs) { Map<String, Object> sections = (Map<String, Object>) assoc.get(Constants.ATTR_SECTIONS); // This case will catch if the subjectArea is null // Add the section only if it's not null if (sections != null && (subjectArea == null || subjectArea.equalsIgnoreCase(getSubjectArea(sections)))) { interSections.add(sections); } } addSemesterFinalGrades(student, interSections, (List<Map<String, Object>>) transcripts.get(Constants.ATTR_COURSE_TRANSCRIPTS), curSessionEndDate); } catch (ClassCastException ex) { log.error("Error occured processing Final Grades", ex); Map<String, Object> grade = new LinkedHashMap<String, Object>(); student.put(Constants.ATTR_SCORE_RESULTS, grade.put(Constants.ATTR_FINAL_LETTER_GRADE, "?")); } catch (NullPointerException ex) { log.error("Error occured processing Final Grades", ex); Map<String, Object> grade = new LinkedHashMap<String, Object>(); student.put(Constants.ATTR_SCORE_RESULTS, grade.put(Constants.ATTR_FINAL_LETTER_GRADE, "?")); } } /** * Returns the term and the year as a string for a given student Section association. * * @param stuSectAssocs * @param sectionId * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) private String getSessionYear(List<Map<String, Object>> stuSectAssocs, String sectionId) { String sessionString = null; for (Map<String, Object> assoc : stuSectAssocs) { if (((String) assoc.get(Constants.ATTR_SECTION_ID)).equalsIgnoreCase(sectionId)) { Map<String, Object> sections = (Map) assoc.get(Constants.ATTR_SECTIONS); sessionString = buildSemesterYearString(sections); break; } } return sessionString; } /** * Extracts the semester+Year from the section passed. * * @param sections * @return (e.g. FallSemester2010-2011 ) */ @SuppressWarnings({ "rawtypes", "unchecked" }) private String buildSemesterYearString(Map<String, Object> section) { String semesterString = null; if (section == null) { return semesterString; } Map<String, Object> sessions = (Map) section.get(Constants.ATTR_SESSIONS); if (sessions == null) { return semesterString; } String term = (String) sessions.get(Constants.ATTR_TERM); String year = (String) sessions.get(Constants.ATTR_SCHOOL_YEAR); if (term != null && year != null) { semesterString = term.replaceAll(" ", "") + year.replaceAll(" ", ""); } return semesterString; } /** * Extracts the session start and end dates from the specified section. * * @param section * @return session start and end dates or null if information is not available * @author iivanisevic */ @SuppressWarnings({ "rawtypes", "unchecked" }) private List<Date> getSessionDates(Map<String, Object> section) { List<Date> dates; // get the session for this section Map<String, Object> session = (Map) section.get(Constants.ATTR_SESSIONS); if (session == null) { // no session for this section, bad news return null; } dates = new LinkedList<Date>(); dates.add(parseDate((String) session.get(Constants.ATTR_SESSION_BEGIN_DATE))); dates.add(parseDate((String) session.get(Constants.ATTR_SESSION_END_DATE))); return dates; } /** * Extracts the session start and end dates from the specified section and * studentSectionAssociation. * * @param stuSectAssocs * @param sectionId * @return session start and end dates or null if information is not available * @author iivanisevic */ @SuppressWarnings({ "rawtypes", "unchecked" }) private List<Date> getSessionDates(List<Map<String, Object>> stuSectAssocs, String sectionId) { List<Date> dates = null; for (Map<String, Object> assoc : stuSectAssocs) { // find the association containing this sectionId String tempSectionId = (String) assoc.get(Constants.ATTR_SECTION_ID); if (tempSectionId != null && tempSectionId.equalsIgnoreCase(sectionId)) { // we found the section we were looking for Map<String, Object> section = (Map) assoc.get(Constants.ATTR_SECTIONS); if (section != null) { dates = getSessionDates(section); break; } } } return dates; } private Date parseDate(String date) { final DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); try { if (date != null) { return formatter.parse(date); } } catch (ParseException e) { log.error("Error parsing dates. Date string was: " + date); } return null; } /** * Adds the current session grades to the student in a easily retrievable manner. * * @param student * @param sectionId */ @SuppressWarnings("unchecked") private void addCurrentSessionGrades(GenericEntity student, String sectionId) { final DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); // Sort the grades SortedSet<GenericEntity> sortedList = new TreeSet<GenericEntity>(new Comparator<GenericEntity>() { @Override public int compare(GenericEntity a, GenericEntity b) { Object dateA = a.get(Constants.ATTR_DATE_FULFILLED); Object dateB = b.get(Constants.ATTR_DATE_FULFILLED); if (dateA == null) { return 1; } else if (dateB == null) { return -1; } return ((Date) dateA).compareTo((Date) dateB); } }); // Get the term and year try { Map<String, Object> transcripts = (Map<String, Object>) student.get(Constants.ATTR_TRANSCRIPT); if (transcripts == null) { return; } List<Map<String, Object>> stuSectAssocs = (List<Map<String, Object>>) transcripts .get(Constants.ATTR_STUDENT_SECTION_ASSOC); if (stuSectAssocs == null) { return; } // figure out what the start and end dates are for the current session List<Date> dates = getSessionDates(stuSectAssocs, sectionId); Date sessionStart = (dates == null) ? null : dates.get(0); Date sessionEnd = (dates == null) ? null : dates.get(1); // next we ensure that gradebook entries belong to the current session // iterate and add to letter grade List<Map<String, Object>> gradeEntries = (List<Map<String, Object>>) student .get(Constants.ATTR_STUDENT_GRADEBOOK_ENTRIES); if (gradeEntries == null) { return; } for (Map<String, Object> currentGrade : gradeEntries) { GenericEntity gradeDate = new GenericEntity(); // get the grade itself Object grade = currentGrade.get(Constants.ATTR_LETTER_GRADE_EARNED); if (grade == null) { grade = currentGrade.get(Constants.ATTR_NUMERIC_GRADE_EARNED); if (grade == null) { continue; } } // when was it earned? try { Object dateString = currentGrade.get(Constants.ATTR_DATE_FULFILLED); // only keep it if it belongs to this session, or there is no date // associated with it or the current session if (dateString != null) { Date date = formatter.parse((String) dateString); if (((sessionStart == null || sessionStart.compareTo(date) <= 0) && (sessionEnd == null || sessionEnd .compareTo(date) >= 0))) { gradeDate.put(Constants.ATTR_DATE_FULFILLED, date); } else { // this grade does not belong in the current session continue; } } } catch (ParseException e) { log.error("Error parsing dates for a Current Section grade."); } gradeDate.put(Constants.ATTR_GRADE_EARNED, grade); sortedList.add(gradeDate); } int count = 0; for (GenericEntity entity : sortedList) { student.put("currentSession-" + count++, entity); } } catch (ClassCastException ex) { log.error("Error occured processing Gradebook Entries", ex); } catch (NullPointerException ex) { log.error("Error occured processing Gradebook Entries", ex); } } /** * Find the required assessment results according to the data configuration. * Filter out the rest. */ @SuppressWarnings("unchecked") public void applyAssessmentFilters(List<GenericEntity> studentSummaries, Config.Data config) { // Loop through student summaries if (studentSummaries != null) { for (GenericEntity summary : studentSummaries) { // Grab the student's assmt results. Grab assmt filters from config List<Map<String, Object>> assmtResults = (List<Map<String, Object>>) (summary .remove(Constants.ATTR_STUDENT_ASSESSMENTS)); Map<String, Object> param = config.getParams(); if (param == null) { return; } Map<String, String> assmtFilters = (Map<String, String>) (config.getParams() .get(Constants.CONFIG_ASSESSMENT_FILTER)); if (assmtFilters == null) { return; } Map<String, Object> newAssmtResults = new LinkedHashMap<String, Object>(); // Loop through assmt filters for (String assmtFamily : assmtFilters.keySet()) { String timeSlotStr = assmtFilters.get(assmtFamily); if (timeSlotStr != null) { TimedLogic.TimeSlot timeSlot = TimedLogic.TimeSlot.valueOf(timeSlotStr); // Apply filter. Add result to student summary. Map assmt = applyAssessmentFilter(assmtResults, assmtFamily, timeSlot); // since we flip data to be the property name, we cannot allow dots in it newAssmtResults.put(assmtFamily.replace('.', '_'), assmt); } } summary.put(Constants.ATTR_ASSESSMENTS, newAssmtResults); } } } @SuppressWarnings("unchecked") private List<Map<String, Object>> filterAssessmentByFamily(List<?> assmtResults, String assmtFamily) { // filter by assmt family name List<Map<String, Object>> studentAssessmentFiltered = new ArrayList<Map<String, Object>>(); Map<String, Object> assmtResult, assessments; String family; for (Object assmtResultObj : assmtResults) { assmtResult = (Map<String, Object>) assmtResultObj; assessments = (Map<String, Object>) assmtResult.get(Constants.ATTR_ASSESSMENTS); if (assessments != null) { family = (String) assessments.get(Constants.ATTR_ASSESSMENT_FAMILY_HIERARCHY_NAME); if (family != null && family.contains(assmtFamily)) { studentAssessmentFiltered.add(assmtResult); } } } return studentAssessmentFiltered; } /** * Filter a list of assessment results, based on the assessment family and * timed logic * * @param assmtResults * @param assmtFamily * @param timedLogic * @return */ private Map applyAssessmentFilter(List<Map<String, Object>> assmtResults, String assmtFamily, TimedLogic.TimeSlot timeSlot) { // filter by assmt family name List<Map<String, Object>> studentAssessmentFiltered = filterAssessmentByFamily(assmtResults, assmtFamily); if (studentAssessmentFiltered.size() == 0) { return null; } Map chosenAssessment = null; String objAssmtCode = ""; // call timeslot logic to pick out the assessment we want switch (timeSlot) { case MOST_RECENT_RESULT: chosenAssessment = TimedLogic.getMostRecentAssessment(studentAssessmentFiltered); break; case HIGHEST_EVER: if (!objAssmtCode.equals("")) { chosenAssessment = TimedLogic.getHighestEverObjAssmt(studentAssessmentFiltered, objAssmtCode); } else { chosenAssessment = TimedLogic.getHighestEverAssessment(studentAssessmentFiltered); } break; case MOST_RECENT_WINDOW: List<Map<String, Object>> assessmentMetaData = new ArrayList<Map<String, Object>>(); // TODO: get the assessment meta data /* * Set<String> assessmentIds = new HashSet<String>(); for (Map * studentAssessment : studentAssessmentFiltered) { String * assessmentId = (String) * studentAssessment.get(Constants.ATTR_ASSESSMENT_ID); if * (!assessmentIds.contains(assessmentId)) { GenericEntity * assessment = metaDataResolver.getAssmtById(assessmentId); * assessmentMetaData.add(assessment); * assessmentIds.add(assessmentId); } } */ chosenAssessment = TimedLogic.getMostRecentAssessmentWindow(studentAssessmentFiltered, assessmentMetaData); break; default: chosenAssessment = TimedLogic.getMostRecentAssessment(studentAssessmentFiltered); break; } return chosenAssessment; } private List<GenericEntity> getStudentAttendance(String token, String studentId, String startDate, String endDate) { List<GenericEntity> list = entityManager.getAttendance(token, studentId, startDate, endDate); if (list == null) { return Collections.emptyList(); } return list; } /* * (non-Javadoc) * * @see * org.slc.sli.dashboard.manager.PopulationManagerI#setEntityManager(org.slc.sli.dashboard.manager * .EntityManager) */ @Override public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } /* * (non-Javadoc) * * @see org.slc.sli.dashboard.manager.PopulationManagerI#getStudent(java.lang.String, * java.lang.String) */ @Override public GenericEntity getStudent(String token, String studentId) { return entityManager.getStudent(token, studentId); } @Override public GenericEntity getTeacher(String token, Object teacherId, Config.Data config) { return getApiClient().getTeacherWithSections(token, (String) teacherId); } @Override public GenericEntity getTeachersForSchool(String token, Object schoolId, Config.Data config) { List<GenericEntity> teachers = getApiClient().getTeachersForSchool(token, (String) schoolId); if (teachers != null) { for (GenericEntity teacher : teachers) { addFullName(teacher); } } GenericEntity result = new GenericEntity(); result.put(Constants.ATTR_TEACHERS, teachers); return result; } /* * (non-Javadoc) * * @see org.slc.sli.dashboard.manager.PopulationManagerI#getStudent(java.lang.String, * java.lang.Object, org.slc.sli.dashboard.entity.Config.Data) */ @Override public GenericEntity getStudent(String token, Object studentId, Config.Data config) { return entityManager.getStudentForCSIPanel(token, (String) studentId); } /* * (non-Javadoc) * * @see * org.slc.sli.dashboard.manager.PopulationManagerI#getAttendance(java.lang.String, * java.lang.Object, org.slc.sli.dashboard.entity.Config.Data) */ @Override public GenericEntity getAttendance(String token, Object studentIdObj, Config.Data config) { // get yearsBack from param String yearsBack = config.getParams() == null ? null : (String) config.getParams().get("yearsBack"); int intYearsBack = DEFAULT_YEARS_BACK; if (yearsBack != null) { try { intYearsBack = Integer.parseInt(yearsBack); } catch (Exception e) { log.error("params: value of yearsBack was not integer. [" + intYearsBack + "]. Using default value [" + DEFAULT_YEARS_BACK + "]"); intYearsBack = DEFAULT_YEARS_BACK; } } GenericEntity attendance = new GenericEntity(); String studentId = (String) studentIdObj; // get Enrollment History List<GenericEntity> enrollments = getApiClient().getEnrollmentForStudent(token, studentId); // creating lookup index for enrollment, key is term (yyyy-yyyy) int currentSchoolYear = 0; Map<String, LinkedHashMap<String, Object>> enrollmentsIndex = new HashMap<String, LinkedHashMap<String, Object>>(); for (LinkedHashMap<String, Object> enrollment : enrollments) { String entryDateYear = ""; String exitWithdrawDateYear = ""; String entryDate = (String) enrollment.get(Constants.ATTR_ENROLLMENT_ENTRY_DATE); String exitWithdrawDate = (String) enrollment.get(Constants.ATTR_ENROLLMENT_EXIT_WITHDRAW_DATE); // find a year for entryDate if (entryDate != null && entryDate.length() > 3) { entryDateYear = entryDate.substring(0, 4); } // find a year for exitWithdarwDate if (exitWithdrawDate != null && exitWithdrawDate.length() > 3) { exitWithdrawDateYear = exitWithdrawDate.substring(0, 4); } else { // exitWithdrawDate is null because it is in current term. // add one year to entryDateYear. currentSchoolYear = Integer.parseInt(entryDateYear); exitWithdrawDateYear = Integer.toString(currentSchoolYear + 1); } // creating index lookup key String key = entryDateYear + "-" + exitWithdrawDateYear; enrollmentsIndex.put(key, enrollment); } // Numberformat for %Present - no fraction NumberFormat numberFormat = NumberFormat.getNumberInstance(); numberFormat.setMaximumFractionDigits(0); // get attendance for the student List<GenericEntity> attendanceList = this.getStudentAttendance(token, studentId, null, null); for (Map<String, Object> targetAttendance : attendanceList) { // get schoolYearAttendance List<Map<String, Object>> schoolYearAttendances = (List<Map<String, Object>>) targetAttendance .get(Constants.ATTR_ATTENDANCE_SCHOOLYEAR_ATTENDANCE); if (schoolYearAttendances != null) { // sort by schoolYear GenericEntityComparator comparator = new GenericEntityComparator(Constants.ATTR_SCHOOL_YEAR, String.class); Collections.sort(schoolYearAttendances, Collections.reverseOrder(comparator)); for (Map<String, Object> schoolYearAttendance : schoolYearAttendances) { int inAttendanceCount = 0; int absenceCount = 0; int excusedAbsenceCount = 0; int unexcusedAbsenceCount = 0; int tardyCount = 0; int earlyDepartureCount = 0; int totalCount = 0; // get schoolYear String schoolYear = (String) schoolYearAttendance.get(Constants.ATTR_SCHOOL_YEAR); // if some reasons we cannot find currentSchoolYear, then display all histories // if intYearsBack is not set to NO_LIMIT (-1) and found currentSchoolYear, // then exam whether current loop is within user defined yearsBack if (intYearsBack != NO_LIMIT && currentSchoolYear != 0) { int targetYear = Integer.parseInt(schoolYear.substring(0, 4)); // if yearsBack is 1, it means current schoolYear. // break from the loop if currentSchoolYear-targetYear is over yearsBack. if ((currentSchoolYear - targetYear) >= intYearsBack) { break; } } // get attendanceEvent List<LinkedHashMap<String, Object>> attendanceEvents = (List<LinkedHashMap<String, Object>>) schoolYearAttendance .get(Constants.ATTR_ATTENDANCE_ATTENDANCE_EVENT); // count each attendance event if (attendanceEvents != null) { for (Map<String, Object> attendanceEvent : attendanceEvents) { String event = (String) attendanceEvent.get(Constants.ATTR_ATTENDANCE_EVENT_CATEGORY); if (event != null) { totalCount++; if (event.equals(Constants.ATTR_ATTENDANCE_IN_ATTENDANCE)) { inAttendanceCount++; } else if (event.equals(Constants.ATTR_ATTENDANCE_ABSENCE)) { absenceCount++; } else if (event.equals(Constants.ATTR_ATTENDANCE_EXCUSED_ABSENCE)) { excusedAbsenceCount++; } else if (event.equals(Constants.ATTR_ATTENDANCE_UNEXCUSED_ABSENCE)) { unexcusedAbsenceCount++; } else if (event.equals(Constants.ATTR_ATTENDANCE_TARDY)) { tardyCount++; } else if (event.equals(Constants.ATTR_ATTENDANCE_EARLY_DEPARTURE)) { earlyDepartureCount++; } } } } // get target school year enrollment LinkedHashMap<String, Object> enrollment = enrollmentsIndex.get(schoolYear); GenericEntity currentTermAttendance = new GenericEntity(); // set school term currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_TERM, schoolYear); String nameOfInstitution = ""; // get school name from enrollment if (enrollment != null) { Map<String, Object> school = (Map<String, Object>) enrollment.get(Constants.ATTR_SCHOOL); if (school != null) { nameOfInstitution = (String) school.get(Constants.ATTR_NAME_OF_INST); } } // set school name currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_SCHOOL, nameOfInstitution); String gradeLevel = ""; // set grade level if (enrollment != null) { gradeLevel = (String) enrollment.get(Constants.ATTR_ENROLLMENT_ENTRY_GRADE_LEVEL_CODE); } currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_GRADE_LEVEL, gradeLevel); // set %Present currentTermAttendance .put(Constants.ATTENDANCE_HISTORY_PRESENT, numberFormat .format(totalCount == 0 ? 0 : ((inAttendanceCount + tardyCount + earlyDepartureCount) / (double) totalCount) * 100)); // set In Attendance currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_IN_ATTENDANCE, inAttendanceCount); // set Total Absences currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_TOTAL_ABSENCES, absenceCount + excusedAbsenceCount + unexcusedAbsenceCount); // set Absence currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_ABSENCE, absenceCount); // set Excused Absences currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_EXCUSED, excusedAbsenceCount); // set Unexcused Absences currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_UNEXCUSED, unexcusedAbsenceCount); // set Tardy currentTermAttendance.put(Constants.ATTENDANCE_HISTORY_TARDY, tardyCount); // set Early departure currentTermAttendance.put(Constants.ATTENDANCE_EARLY_DEPARTURE, earlyDepartureCount); // Add to attendance list attendance.appendToList("attendance", currentTermAttendance); } } } return attendance; } /* * (non-Javadoc) * * @see * org.slc.sli.dashboard.manager.PopulationManagerI#getSessionDates(java.lang.String, * java.lang.String) */ @Override public List<String> getSessionDates(String token, String sessionId) { // This method appears to not return the begin/end dates for the input session, as the name // implies, but rather the begin/end dates, respectively, for the first and last session of // the school year associated with the input session id. // Get the session first. GenericEntity currentSession = entityManager.getSession(token, sessionId); List<String> dates = new ArrayList<String>(); if (currentSession != null) { String beginDate = currentSession.getString("beginDate"); String endDate = currentSession.getString("endDate"); List<GenericEntity> potentialSessions = entityManager.getSessionsByYear(token, currentSession.getString("schoolYear")); for (GenericEntity session : potentialSessions) { if (session.getString("beginDate").compareTo(beginDate) < 0) { beginDate = session.getString("beginDate"); } if (session.getString("endDate").compareTo(endDate) > 0) { endDate = session.getString("endDate"); } } dates.add(beginDate); dates.add(endDate); } else { dates.add(""); dates.add(""); } return dates; } @Override public GenericEntity getStudentsBySearch(String token, Object nameQuery, Config.Data config) { // Map<String, String> nameQueryMap = (Map<String, String>) nameQuery; GenericEntity studentSearch = new GenericEntity(); // make sure the incoming nameQuery is of the proper format: // expected is an array of Strings generated by StudentSearch.get() String[] nameList; try { nameList = (String[]) nameQuery; } catch (ClassCastException cce) { setStudentSearchEntity(studentSearch, new LinkedList<GenericEntity>(), "", "", "", 0, 1, 50, 1, ""); return studentSearch; } // the query should contain at a minimum either a first or a last name // both fields must be present in input, but one (not both) can optionally be empty String firstName = null, lastName = null; if (nameList.length >= 2) { firstName = nameList[0]; lastName = nameList[1]; } if ((firstName == null || firstName.isEmpty()) && (lastName == null || lastName.isEmpty())) { setStudentSearchEntity(studentSearch, new LinkedList<GenericEntity>(), "", "", "", 0, 1, 50, 1, ""); return studentSearch; } // optionally (but typically), it should also contain pagination information int pageNum = 1, pageSize = 50; if (nameList.length >= 4) { try { pageNum = Integer.parseInt(nameList[2]); pageSize = Integer.parseInt(nameList[3]); } catch (NumberFormatException nfe) { // pagination information was in an incorrect format, use default values. pageNum = 1; pageSize = 50; } } String searchString = firstName + " " + lastName; searchString = searchString.trim(); String searchingSchoolId = null; if (nameList.length >= 5) { searchingSchoolId = nameList[4]; } if (searchingSchoolId == null || searchingSchoolId.isEmpty()) { setStudentSearchEntity(studentSearch, new LinkedList<GenericEntity>(), searchString, firstName == null ? "" : firstName, lastName == null ? "" : lastName, 0, pageNum, pageSize, 1, ""); return studentSearch; } List<GenericEntity> students = entityManager.getStudentsFromSearch(token, firstName, lastName, searchingSchoolId); List<GenericEntity> titleCaseStudents = entityManager.getStudentsFromSearch(token, WordUtils.capitalize(firstName), WordUtils.capitalize(lastName), searchingSchoolId); HashSet<GenericEntity> studentSet = new HashSet<GenericEntity>(); studentSet.addAll(students); studentSet.addAll(titleCaseStudents); // API returns student entities with a current attending school when searching query is used // for school id he/she used to attend. // Dashboard search is interested only current attending school. // Filtering old school Iterator<GenericEntity> studentIterator = studentSet.iterator(); while (studentIterator.hasNext()) { GenericEntity student = studentIterator.next(); if (!searchingSchoolId.equals(student.get("schoolId"))) { studentIterator.remove(); } } List<GenericEntity> enhancedStudents = new LinkedList<GenericEntity>(); HashMap<String, GenericEntity> retrievedSchools = new HashMap<String, GenericEntity>(); GenericEntity school; Iterator<GenericEntity> studentSetIterator = studentSet.iterator(); while (studentSetIterator.hasNext()) { GenericEntity student = studentSetIterator.next(); student = entityManager.getStudent(token, student.getId()); addFullName(student); String schoolId = student.getString(Constants.ATTR_SCHOOL_ID); if (schoolId != null && !schoolId.equals("")) { if (retrievedSchools.containsKey(schoolId)) { school = retrievedSchools.get(schoolId); student.put("currentSchoolName", school.get(Constants.ATTR_NAME_OF_INST)); } else { school = entityManager.getEntity(token, Constants.ATTR_SCHOOLS, schoolId, new HashMap()); retrievedSchools.put(school.getString(Constants.ATTR_ID), school); student.put("currentSchoolName", school.get(Constants.ATTR_NAME_OF_INST)); } } GenericEntityEnhancer.enhanceStudent(student); enhancedStudents.add(student); } // sort students by last & first name Collections.sort(enhancedStudents, STUDENT_COMPARATOR); // This is a temporary solution until we decide how to integrate the search with the API // pagination calls. Currently, when API is used, the total number of search results is // stored in the header which is not accessible. Also, code above performs two searches and // combines results - this is a problem if API pagination is used. int numResults = enhancedStudents.size(); // verify sensible page number was requested (negatives not allowed) if (pageNum < 1) { pageNum = 1; } // verify sensible page size was specified (negatives not allowed) if (pageSize < 1) { pageSize = 1; } // calculate the last available page from the number of results and page size int maxPageNum = numResults / pageSize; if (numResults % pageSize != 0) { maxPageNum++; } // requested page number cannot exceed last available page if (pageNum > maxPageNum) { pageNum = maxPageNum; } // fetch the subset of search results specified by the pagination request if (numResults > pageSize) { int beginIndex = (pageNum - 1) * pageSize; int endIndex = beginIndex + pageSize; if (endIndex > numResults) { endIndex = numResults; } enhancedStudents = enhancedStudents.subList(beginIndex, endIndex); } // fill the search map with results setStudentSearchEntity(studentSearch, enhancedStudents, searchString, firstName, lastName, numResults, pageNum, pageSize, maxPageNum, searchingSchoolId); return studentSearch; } private void setStudentSearchEntity(GenericEntity studentSearch, List<GenericEntity> students, String searchStr, String firstName, String lastName, int numResults, int pageNum, int pageSize, int maxPageNum, String schoolId) { studentSearch.put(Constants.ATTR_STUDENTS, students); studentSearch.put(Constants.ATTR_SEARCH_STRING, searchStr); studentSearch.put(Constants.ATTR_FIRST_NAME, firstName); studentSearch.put(Constants.ATTR_LAST_SURNAME, lastName); studentSearch.put(Constants.ATTR_NUM_RESULTS, numResults); studentSearch.put(Constants.ATTR_SEARCH_PAGE_NUM, pageNum); studentSearch.put(Constants.ATTR_SEARCH_PAGE_SIZE, pageSize); studentSearch.put(Constants.ATTR_SEARCH_MAX_PAGE_NUM, maxPageNum); studentSearch.put(Constants.ATTR_SCHOOL_ID, schoolId); } @SuppressWarnings("unchecked") @Override public GenericEntity getAssessments(String token, Object id, Config.Data config) { GenericEntity entity = new GenericEntity(); GenericEntity student = entityManager.getStudentWithOptionalFields(token, (String) id, Arrays.asList(Constants.ATTR_ASSESSMENTS)); if (student == null) { log.error("Requested data for non-existing ID" + id); return entity; } List<Map<String, Object>> assessements = filterAssessmentByFamily( student.getList(Constants.ATTR_STUDENT_ASSESSMENTS), (String) config.getParams().get(Constants.ATTR_ASSESSMENT_FAMILY)); // get all assessments for student entity.put(Constants.ATTR_ASSESSMENTS, assessements); Set<String> scoreResultNames = new LinkedHashSet<String>(); List<Map<String, Object>> scoreResults; Map<String, Object> assessmentDetails; List<List<Map<String, Object>>> perfLevelsDescs; String reportingMethod; // inline assessments, perf attributes and convert grade to gradelevel for (Map<String, Object> elem : assessements) { scoreResults = (List<Map<String, Object>>) elem.get(Constants.ATTR_SCORE_RESULTS); if (scoreResults != null) { for (Map<String, Object> oneScore : scoreResults) { reportingMethod = (String) oneScore.get(Constants.ATTR_ASSESSMENT_REPORTING_METHOD); scoreResultNames.add(reportingMethod); elem.put(reportingMethod, oneScore.get(Constants.ATTR_RESULT)); } } assessmentDetails = (Map<String, Object>) elem.get(Constants.ATTR_ASSESSMENTS); if (assessmentDetails != null) { GenericEntityEnhancer.convertGradeLevel(assessmentDetails, Constants.ATTR_GRADE_LEVEL_ASSESSED); } perfLevelsDescs = (List<List<Map<String, Object>>>) elem.get(Constants.ATTR_PERFORMANCE_LEVEL_DESCRIPTOR); if (perfLevelsDescs != null) { for (List<Map<String, Object>> perfLevelsDesc : perfLevelsDescs) { if (perfLevelsDesc != null && perfLevelsDesc.size() > 0) { String perfLevel = (String) perfLevelsDesc.get(0).get(Constants.ATTR_CODE_VALUE); elem.put(Constants.ATTR_PERF_LEVEL, perfLevel); } } } } entity.put("scoreResultsSet", new ArrayList<String>(scoreResultNames)); return entity; } /** * Comparator for student names */ public static final Comparator<GenericEntity> STUDENT_COMPARATOR = new Comparator<GenericEntity>() { @Override public int compare(GenericEntity o1, GenericEntity o2) { String name1 = (String) o1.getNode(Constants.ATTR_NAME + "." + Constants.ATTR_FULL_NAME); String name2 = (String) o2.getNode(Constants.ATTR_NAME + "." + Constants.ATTR_FULL_NAME); if (name1 == null && name2 == null) { return 0; } if (name1 == null) { name1 = StringUtils.EMPTY; } return name1.compareTo(name2); } }; /** * Retrieves info required to create section profile. */ @Override public GenericEntity getSectionForProfile(String token, Object sectionId, Config.Data config) { return entityManager.getSectionForProfile(token, (String) sectionId); } @Override public GenericEntity getCoursesAndGrades(String token, Object studentId, Data config) { return entityManager.getCurrentCoursesAndGrades(token, (String) studentId); } /** * Retrieves attendance in a sorted order, removes all events where the student is present. * Returns a GenericEntity with startDate, endDate, and attendanceList. */ @Override public GenericEntity getStudentAttendanceForCalendar(String token, Object studentId, Data config) { GenericEntity ge = new GenericEntity(); List<GenericEntity> attendanceList = getStudentAttendance(token, (String) studentId, null, null); if (attendanceList == null || attendanceList.size() < 1) { return ge; } GenericEntity firstWrapper = attendanceList.get(0); String schoolId = (String) firstWrapper.get("schoolId"); List<Map<String, Object>> schoolYearAttendance = (List<Map<String, Object>>) firstWrapper .get(Constants.ATTR_ATTENDANCE_SCHOOLYEAR_ATTENDANCE); if (schoolYearAttendance == null || schoolYearAttendance.size() < 1) { return ge; } // Comparator, sort by "schoolYear" descending order Comparator<Map<String, Object>> schoolYearAttendanceComparator = new Comparator<Map<String, Object>>() { @Override public int compare(Map<String, Object> arg0, Map<String, Object> arg1) { Object schoolYearObj0 = arg0.get(Constants.ATTR_SCHOOL_YEAR); Object schoolYearObj1 = arg1.get(Constants.ATTR_SCHOOL_YEAR); if (schoolYearObj0 == null || schoolYearObj1 == null) { return 0; } String schoolYear0 = schoolYearObj0.toString(); String schoolYear1 = schoolYearObj1.toString(); return schoolYear1.compareTo(schoolYear0); } }; Collections.sort(schoolYearAttendance, schoolYearAttendanceComparator); Map<String, Object> secondWrapper = schoolYearAttendance.get(0); List<Map> attList = (List<Map>) secondWrapper.get(Constants.ATTR_ATTENDANCE_ATTENDANCE_EVENT); if (attList == null) { return ge; } LinkedList<Map> absentList = new LinkedList<Map>(); List<String> currentYearDates = null; try { // get begin/end dates for the current school year currentYearDates = getCurrentYearDates(token, schoolId); // filter out 'In Attendance' events, remove whitespace for (Map attEvent : attList) { String event = (String) attEvent.get(Constants.ATTR_ATTENDANCE_EVENT_CATEGORY); if (!event.equals(Constants.ATTR_ATTENDANCE_IN_ATTENDANCE)) { String strippedWhiteSpaceEvent = ((String) attEvent.get(Constants.ATTR_ATTENDANCE_EVENT_CATEGORY)) .replace(" ", ""); attEvent.put(Constants.ATTR_ATTENDANCE_EVENT_CATEGORY, strippedWhiteSpaceEvent); absentList.addLast(attEvent); } } } catch (Exception e) { log.error(e.getMessage(), e); } ge.put(Constants.ATTR_ATTENDANCE_LIST, absentList); ge.put(Constants.ATTR_START_DATE, currentYearDates.get(0)); ge.put(Constants.ATTR_END_DATE, currentYearDates.get(1)); return ge; } /** * Returns the begin and end dates of the current school year. * The current school year is determined by comparing the current date to * the begin/end dates for sessions the user can see. If the current date * is not in any session, it returns the most recent session. * * @param token * @return * @throws Exception */ private List<String> getCurrentYearDates(String token, String schoolId) throws Exception { final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); List<GenericEntity> sessions = getApiClient().getSessions(token, schoolId, null); // sort sessions latest to earliest Comparator<Map> c = new Comparator<Map>() { @Override public int compare(Map arg0, Map arg1) { Map<String, String> map1, map2; map1 = arg0; map2 = arg1; Date date1, date2; try { date1 = sdf.parse(map1.get(Constants.ATTR_BEGIN_DATE)); date2 = sdf.parse(map2.get(Constants.ATTR_BEGIN_DATE)); return date2.compareTo(date1); } catch (ParseException e) { return 0; } } }; Collections.sort(sessions, c); // find the current year String schoolYear = null; for (GenericEntity session : sessions) { Date currentDate = new Date(); Date sessionStart = sdf.parse(session.getString(Constants.ATTR_BEGIN_DATE)); Date sessionEnd = sdf.parse(session.getString(Constants.ATTR_END_DATE)); if (sessionStart != null && sessionStart.before(currentDate) && sessionEnd != null && sessionEnd.after(currentDate)) { schoolYear = session.getString(Constants.ATTR_SCHOOL_YEAR); break; } else if (sessionEnd.before(currentDate)) { schoolYear = session.getString(Constants.ATTR_SCHOOL_YEAR); break; } } Date startDate = null; Date endDate = null; // find all sessions for current school year, find earliest start date, latest end date for (GenericEntity session : sessions) { if (session.getString(Constants.ATTR_SCHOOL_YEAR).equals(schoolYear)) { Date sessionStart = sdf.parse(session.getString(Constants.ATTR_BEGIN_DATE)); Date sessionEnd = sdf.parse(session.getString(Constants.ATTR_END_DATE)); if (startDate == null || sessionStart.before(startDate)) { startDate = sessionStart; } if (endDate == null || sessionEnd.after(endDate)) { endDate = sessionEnd; } } } List<String> ret = new ArrayList<String>(); ret.add(sdf.format(startDate)); ret.add(sdf.format(endDate)); return ret; } @Override public GenericEntity getEdorgProfile(String token, Object edorgId, Config.Data config) { return entityManager.getEdorgProfile(token, (String) edorgId); } @Override public GenericEntity getStateEdorgProfile(String token, Object edorgId, Config.Data config) { return entityManager.getStateEdorgProfile(token, (String) edorgId); } }