@RequestMapping(method = RequestMethod.GET)
  @PreAuthorize(Permission.SECURITY_REPORT_READ)
  public @ResponseBody void getNumberOfEarlyAlertsByReasons(
      final HttpServletResponse response,
      final @RequestParam(required = false) UUID campusId,
      final @RequestParam(required = false) String termCode,
      final @RequestParam(required = false) Date createDateFrom,
      final @RequestParam(required = false) Date createDateTo,
      final @RequestParam(required = false) ObjectStatus objectStatus,
      final @RequestParam(required = false, defaultValue = DEFAULT_REPORT_TYPE) String reportType)
      throws ObjectNotFoundException, IOException {

    final DateTerm dateTerm = new DateTerm(createDateFrom, createDateTo, termCode, termService);
    final Map<String, Object> parameters = Maps.newHashMap();
    final Campus campus = SearchParameters.getCampus(campusId, campusService);

    if (StringUtils.isBlank(termCode)
        || termCode.trim().toLowerCase().equals("not used") && createDateFrom != null) {
      dateTerm.setTerm(null);
    } else if (termCode != null && createDateFrom == null) {
      dateTerm.setStartEndDates(null, null);
    }

    SearchParameters.addCampusToParameters(campus, parameters);
    SearchParameters.addDateTermToMap(dateTerm, parameters);

    List<Triple<String, Long, Long>> reasonTotals =
        earlyAlertService.getEarlyAlertReasonTypeCountByCriteria(
            campus,
            dateTerm.getTermCodeNullPossible(),
            dateTerm.getStartDate(),
            dateTerm.getEndDate(),
            objectStatus);

    List<EarlyAlertReasonCountsTO> results =
        earlyAlertService.getStudentEarlyAlertReasonCountByCriteria(
            dateTerm.getTermCodeNullPossible(),
            dateTerm.getStartDate(),
            dateTerm.getEndDate(),
            campus,
            objectStatus);

    if (results == null) {
      results = new ArrayList<>();
    }

    SearchParameters.addDateTermToMap(dateTerm, parameters);
    parameters.put(REASON_TOTALS, reasonTotals);

    renderReport(response, parameters, results, REPORT_URL, reportType, REPORT_FILE_TITLE);
  }
  @RequestMapping(value = "/studentactivity", method = RequestMethod.GET)
  @PreAuthorize(Permission.SECURITY_PERSON_READ)
  public @ResponseBody List<RecentActivityTO> loadRecentStudentActivity(final @PathVariable UUID id)
      throws ObjectNotFoundException {
    List<RecentActivityTO> recentActivities = new ArrayList<RecentActivityTO>();
    Person person = personService.get(id);
    SortingAndPaging sAndP =
        SortingAndPaging.createForSingleSortWithPaging(
            ObjectStatus.ACTIVE, 0, 1000, "createdDate", "DESC", "createdDate");

    PagingWrapper<EarlyAlert> earlyAlerts = earlyAlertService.getAllForPerson(person, sAndP);
    SspUser currentUser = securityService.currentUser();
    List<EarlyAlertTO> earlyAlertTOs = earlyAlertTOFactory.asTOList(earlyAlerts.getRows());

    PagingWrapper<JournalEntry> journalEntries =
        journalEntryService.getAllForPerson(person, currentUser, sAndP);

    List<JournalEntryTO> journalEntriesTOs =
        journalEntryTOFactory.asTOList(journalEntries.getRows());

    PagingWrapper<Task> actions = taskService.getAllForPerson(person, currentUser, sAndP);

    List<TaskTO> actionsTOs = taskTOFactory.asTOList(actions.getRows());

    PagingWrapper<Plan> plans =
        planService.getAllForStudent(
            SortingAndPaging.createForSingleSortWithPaging(
                ObjectStatus.ALL, 0, 1000, null, null, null),
            id);

    List<PlanTO> planTOs = planTOFactory.asTOList(plans.getRows());

    for (EarlyAlertTO earlyAlert : earlyAlertTOs) {
      if (earlyAlert.getClosedDate() != null) {
        recentActivities.add(
            new RecentActivityTO(
                earlyAlert.getClosedById(),
                earlyAlert.getClosedByName(),
                "Early Alert Closed",
                earlyAlert.getClosedDate()));
      } else {
        recentActivities.add(
            new RecentActivityTO(
                earlyAlert.getCreatedBy().getId(),
                getPersonLiteName(earlyAlert.getCreatedBy()),
                "Early Alert Created",
                earlyAlert.getCreatedDate()));
      }
    }

    for (JournalEntryTO journalEntry : journalEntriesTOs) {
      recentActivities.add(
          new RecentActivityTO(
              journalEntry.getCreatedBy().getId(),
              getPersonLiteName(journalEntry.getCreatedBy()),
              "Journal Entry",
              journalEntry.getEntryDate()));
    }

    for (TaskTO action : actionsTOs) {
      if (action.isCompleted()) {
        recentActivities.add(
            new RecentActivityTO(
                action.getModifiedBy().getId(),
                getPersonLiteName(action.getModifiedBy()),
                "Action Plan Task Created",
                action.getCompletedDate()));
      } else {
        recentActivities.add(
            new RecentActivityTO(
                action.getCreatedBy().getId(),
                getPersonLiteName(action.getCreatedBy()),
                "Action Plan Task Created",
                action.getCreatedDate()));
      }
    }

    for (PlanTO planTO : planTOs) {
      Date testDate = DateUtils.addDays(planTO.getCreatedDate(), 1);
      String planName = planTO.getName();
      if (planTO.getModifiedDate().before(testDate)) {
        recentActivities.add(
            new RecentActivityTO(
                planTO.getCreatedBy().getId(),
                getPersonLiteName(planTO.getCreatedBy()),
                "Map Plan (" + planName + ") Created",
                planTO.getModifiedDate()));
      } else {
        recentActivities.add(
            new RecentActivityTO(
                planTO.getModifiedBy().getId(),
                getPersonLiteName(planTO.getModifiedBy()),
                "Map Plan (" + planName + ") Updated",
                planTO.getModifiedDate()));
      }
    }

    if (person.getStudentIntakeCompleteDate() != null) {
      recentActivities.add(
          new RecentActivityTO(
              person.getCoach().getId(),
              person.getCoach().getFullName(),
              "Student Intake Completed",
              person.getStudentIntakeCompleteDate()));
    }
    if (person.getStudentIntakeRequestDate() != null) {
      recentActivities.add(
          new RecentActivityTO(
              person.getCoach().getId(),
              person.getCoach().getFullName(),
              "Student Intake Requested",
              person.getStudentIntakeRequestDate()));
    }

    Collections.sort(recentActivities, RecentActivityTO.RECENT_ACTIVITY_TO_DATE_COMPARATOR);
    return recentActivities;
  }
  @RequestMapping(method = RequestMethod.GET)
  @PreAuthorize(Permission.SECURITY_REPORT_READ)
  public @ResponseBody void getEarlyAlertCaseCountsReport(
      final HttpServletResponse response,
      final @RequestParam(required = false) UUID campusId,
      final @RequestParam(required = false) String rosterStatus,
      final @RequestParam(required = false) List<String> termCodes,
      final @RequestParam(required = false, defaultValue = DEFAULT_REPORT_TYPE) String reportType)
      throws ObjectNotFoundException, IOException {

    Campus campus = SearchParameters.getCampus(campusId, campusService);

    final List<EarlyAlertTermCaseCountsTO> caseLoads = new ArrayList<EarlyAlertTermCaseCountsTO>();
    final List<String> cleanTermCodes = SearchParameters.cleanStringListOfNulls(termCodes);
    final List<Term> terms = SearchParameters.getTerms(cleanTermCodes, termService);
    if (terms.size() > 0) {
      for (Term term : terms) {
        EarlyAlertTermCaseCountsTO caseCounts =
            new EarlyAlertTermCaseCountsTO(
                term.getCode(),
                term.getName(),
                earlyAlertService.getStudentCountForEarlyAlertCreatedDateRange(
                    term.getStartDate(), term.getEndDate(), campus, rosterStatus),
                earlyAlertService.getEarlyAlertCountForCreatedDateRange(
                    term.getStartDate(), term.getEndDate(), campus, rosterStatus),
                earlyAlertResponseService
                    .getRespondedToEarlyAlertCountForEarlyAlertCreatedDateRange(
                        term.getStartDate(), term.getEndDate(), campus, rosterStatus),
                earlyAlertService.getClosedEarlyAlertsCountForEarlyAlertCreatedDateRange(
                    term.getStartDate(), term.getEndDate(), campus, rosterStatus));

        caseLoads.add(caseCounts);
      }

    } else {

      EarlyAlertTermCaseCountsTO caseCounts =
          new EarlyAlertTermCaseCountsTO(
              "All",
              "All",
              earlyAlertService.getStudentCountForEarlyAlertCreatedDateRange(
                  null, null, campus, rosterStatus),
              earlyAlertService.getEarlyAlertCountForCreatedDateRange(
                  null, null, campus, rosterStatus),
              earlyAlertResponseService.getRespondedToEarlyAlertCountForEarlyAlertCreatedDateRange(
                  null, null, campus, rosterStatus),
              earlyAlertService.getClosedEarlyAlertsCountForEarlyAlertCreatedDateRange(
                  null, null, campus, rosterStatus));
      caseLoads.add(caseCounts);
    }

    final Map<String, Object> parameters = Maps.newHashMap();
    SearchParameters.addCampusToParameters(campus, parameters);

    SearchParameters.addTermsToMap(terms, parameters);
    renderReport(
        response,
        parameters,
        caseLoads,
        reportType.equals("csv") ? REPORT_URL_CSV : REPORT_URL,
        reportType,
        REPORT_FILE_TITLE);
  }
  @RenderMapping(params = "action=enterAlert")
  public ModelAndView showForm(
      final PortletRequest req,
      @RequestParam(required = false) final String schoolId,
      @RequestParam(required = false) final String formattedCourse,
      @RequestParam(required = false) final String studentUserName,
      @RequestParam(required = false) final String sectionCode,
      @RequestParam(required = false) final String termCode,
      ModelMap model) {
    // Do not use a @ModelAttribute-annotated argument to get the user
    // out of the model b/c Spring will attempt to set properties on it
    // by matching up request param names. This will overwrite user.schoolId
    // with the method param of that name, effectively copying the student's
    // school ID into the faculty user's record.
    if (!StringUtils.isNotBlank(schoolId) && !StringUtils.isNotBlank(studentUserName)) {
      throw new EarlyAlertPortletControllerRuntimeException("Missing student identifier.");
    }

    if (!StringUtils.isNotBlank(formattedCourse) && !StringUtils.isNotBlank(sectionCode)) {
      throw new EarlyAlertPortletControllerRuntimeException("Missing course identifier/s.");
    }
    Person user = (Person) model.get("user");
    if (user == null) {
      throw new EarlyAlertPortletControllerRuntimeException(
          "Missing or deactivated account for current user.");
    }
    FacultyCourse course = null;
    Person student = null;
    ExternalFacultyCourseRoster enrollment = null;
    try {
      // Should really always have a term code (see deprecation notes for
      // getCourseByFacultySchoolIdAndFormattedCourse) but we know at
      // least one real-world deployment (SPC) cannot/does not send term
      // codes when deep linking to the EA form *and* this just happens to
      // work b/c their formattedCourse values are globally unique. So
      // we preserve the option of not filtering by term code.
      course =
          facultyCourseService.getCourseBySearchFacultyCourseTO(
              new SearchFacultyCourseTO(
                  user.getSchoolId(), termCode, sectionCode, formattedCourse));

      if (course == null) {
        throw new EarlyAlertPortletControllerRuntimeException(
            buildErrorMesssage(
                "Course not found or current user is not listed as the instructor of record:",
                user.getSchoolId(),
                schoolId,
                studentUserName,
                formattedCourse,
                termCode,
                sectionCode));
      }

      /*
       * NB:  It's on us to translate from schoolId <-> studentId (SSP
       * UUID) at this point in the Early Alert process.  Previous APIs
       * user the former where following APIs use the later.
       */
      if (StringUtils.isNotBlank(schoolId)) {
        try {
          student = personService.getBySchoolId(schoolId, true); // TODO:  Handle error better??
          if (student == null) {
            throw new EarlyAlertPortletControllerRuntimeException(
                "Student not found by school ID: " + schoolId);
          }
        } catch (ObjectNotFoundException e) {
          throw new EarlyAlertPortletControllerRuntimeException(
              "Student not found by school ID: " + schoolId, e);
        }
      } else {
        try {
          student = personService.getByUsername(studentUserName, true);
          if (student == null) {
            throw new EarlyAlertPortletControllerRuntimeException(
                "Student not found by username: "******"Student not found by username: "******"Selected student has no school ID. Username: "******"Enrollment not found for: ",
                user.getSchoolId(),
                student.getSchoolId(),
                student.getUsername(),
                formattedCourse,
                termCode,
                sectionCode));
      }
    } catch (EarlyAlertPortletControllerRuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(
          buildErrorMesssage(
              "System error looking up course or enrollment for: ",
              user.getSchoolId(),
              student == null ? schoolId : student.getSchoolId(),
              student == null ? studentUserName : student.getUsername(),
              formattedCourse,
              termCode,
              sectionCode),
          e);
    }
    /*
     *  SANITY CHECK (is this even necessary?  wanted?)
     *    - Confirm that the logged in user is the faculty of record on the
     *      course
     */
    if (!course.getFacultySchoolId().equals(user.getSchoolId())) {
      throw new EarlyAlertPortletControllerRuntimeException(
          buildErrorMesssage(
              "Current user is not listed as the instructor of record on the specified course: ",
              user.getSchoolId(),
              student.getSchoolId(),
              student.getUsername(),
              formattedCourse,
              termCode,
              sectionCode));
    }

    EarlyAlertSearchForm form = new EarlyAlertSearchForm();
    form.setAuthor(user);
    form.setStudent(student);

    form.setSortAndPage(buildSortAndPage(-1, 0));
    PagedResponse<EarlyAlertSearchResultTO> results = earlyAlertService.searchEarlyAlert(form);

    model.put(KEY_STUDENT_ID, student.getId()); // Student UUID
    model.put(KEY_COURSE, course);
    model.put(KEY_ENROLLMENT, enrollment);
    model.put(KEY_EARLY_ALERT_RESULTS, results.getRows());
    try {
      Term term = termService.getByCode(course.getTermCode());
      model.put(KEY_COURSE_TERM_NAME, term.getName());
    } catch (Exception exp) {
      model.put(KEY_COURSE_TERM_NAME, course.getTermCode());
    }

    return new ModelAndView("ea-form", model);
  }