/**
   * Starts new {@link CandidateSession} for the given {@link User} on the given {@link Delivery}
   *
   * <p>NB: No checks are made on whether the {@link User} should be allowed to start a session on
   * this {@link Delivery}.
   */
  public CandidateSession launchCandidateSession(
      final User candidate,
      final Delivery delivery,
      final boolean authorMode,
      final String lisOutcomeServiceUrl,
      final String lisResultSourcedid)
      throws CandidateException {
    Assert.notNull(candidate, "candidate");
    Assert.notNull(delivery, "delivery");

    /* Make sure Candidate's account is not disabled */
    if (candidate.isLoginDisabled()) {
      logAndThrowLaunchException(
          candidate, delivery, CandidateExceptionReason.USER_ACCOUNT_DISABLED);
    }

    /* Make sure Delivery is runnable */
    if (delivery.getAssessment() == null) {
      logAndThrowLaunchException(
          candidate, delivery, CandidateExceptionReason.LAUNCH_INCOMPLETE_DELIVERY);
    }

    /* If the candidate already has any non-terminated sessions open for this Delivery,
     * then we shall reconnect to the (most recent) session instead of creating a new one.
     */
    final List<CandidateSession> existingSessions =
        candidateSessionDao.getNonTerminatedForDeliveryAndCandidate(delivery, candidate);
    if (!existingSessions.isEmpty()) {
      final CandidateSession mostRecent = existingSessions.get(existingSessions.size() - 1);
      auditLogger.recordEvent(
          "Reconnected to existing CandidateSession #"
              + mostRecent.getId()
              + " on Delivery #"
              + delivery.getId());
      return mostRecent;
    }

    /* No existing session to reconnect to, so create a new session.
     *
     * (NB: The session will later need to be explicitly entered before anything can be done
     * with it.)
     */
    final CandidateSession candidateSession = new CandidateSession();
    candidateSession.setLisOutcomeServiceUrl(lisOutcomeServiceUrl);
    candidateSession.setLisResultSourcedid(lisResultSourcedid);
    candidateSession.setCandidate(candidate);
    candidateSession.setDelivery(delivery);
    candidateSession.setAuthorMode(authorMode);
    candidateSession.setFinishTime(null);
    candidateSession.setTerminationTime(null);
    candidateSession.setExploded(false);
    candidateSessionDao.persist(candidateSession);
    auditLogger.recordEvent(
        "Created and initialised new CandidateSession #"
            + candidateSession.getId()
            + " on Delivery #"
            + delivery.getId());
    return candidateSession;
  }
 @RequestMapping(value = "/candidate-results-{lrid}.zip", method = RequestMethod.GET)
 public void streamDeliveryCandidateResults(final HttpServletResponse response)
     throws PrivilegeException, DomainEntityNotFoundException, IOException {
   final Delivery thisDelivery = identityService.getCurrentThreadLtiResource().getDelivery();
   response.setContentType("application/zip");
   assessmentReportingService.streamAssessmentReports(
       thisDelivery.getId(), response.getOutputStream());
 }
 @RequestMapping(value = "/candidate-summary-report-{lrid}.csv", method = RequestMethod.GET)
 public void streamDeliveryCandidateSummaryReportCsv(final HttpServletResponse response)
     throws PrivilegeException, DomainEntityNotFoundException, IOException {
   final Delivery thisDelivery = identityService.getCurrentThreadLtiResource().getDelivery();
   response.setContentType("text/plain");
   response.setCharacterEncoding("UTF-8");
   assessmentReportingService.streamDeliveryCandidateSummaryReportCsv(
       thisDelivery.getId(), response.getOutputStream());
 }
  @RequestMapping(value = "/delete-all-sessions", method = RequestMethod.POST)
  public String deleteAllCandidateSessions(final RedirectAttributes redirectAttributes)
      throws PrivilegeException, DomainEntityNotFoundException {
    final Delivery thisDelivery = identityService.getCurrentThreadLtiResource().getDelivery();
    final int deletedCount =
        assessmentProctoringService.deleteCandidateSessionsForDelivery(thisDelivery.getId());

    GlobalRouter.addFlashMessage(
        redirectAttributes,
        "Deleted " + deletedCount + " candidate session" + (deletedCount != 1 ? "s" : ""));
    return ltiInstructorRouter.buildInstructorRedirect("/candidate-sessions");
  }
  @RequestMapping(value = "/candidate-sessions", method = RequestMethod.GET)
  public String showCandidateSummaryReport(final Model model)
      throws PrivilegeException, DomainEntityNotFoundException {
    final Delivery thisDelivery = identityService.getCurrentThreadLtiResource().getDelivery();
    final DeliveryCandidateSummaryReport report =
        assessmentReportingService.buildDeliveryCandidateSummaryReport(thisDelivery.getId());

    model.addAttribute(report);
    model.addAttribute(
        "candidateSessionListRouting",
        ltiInstructorRouter.buildCandidateSessionListRouting(report));
    return "instructor/listCandidateSessions";
  }
 private void ensureTestDelivery(final Delivery delivery) {
   Assert.notNull(delivery, "delivery");
   if (delivery.getAssessment().getAssessmentType() != AssessmentObjectType.ASSESSMENT_TEST) {
     throw new IllegalArgumentException("Expected " + delivery + " to correspond to a Test");
   }
 }
 public void ensureItemDelivery(final Delivery delivery) {
   Assert.notNull(delivery, "delivery");
   if (delivery.getAssessment().getAssessmentType() != AssessmentObjectType.ASSESSMENT_ITEM) {
     throw new IllegalArgumentException("Expected " + delivery + " to correspond to an Item");
   }
 }