@Override
  public boolean extractQuestionDetails(
      Map<String, String[]> requestParameters, FeedbackQuestionType questionType) {
    super.extractQuestionDetails(requestParameters, questionType);
    List<String> options = new ArrayList<>();

    String numOptionsCreatedString =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_NUMBEROFCHOICECREATED);
    Assumption.assertNotNull("Null number of choice for Rank", numOptionsCreatedString);
    int numOptionsCreated = Integer.parseInt(numOptionsCreatedString);

    for (int i = 0; i < numOptionsCreated; i++) {
      String rankOption =
          HttpRequestHelper.getValueFromParamMap(
              requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_RANKOPTION + "-" + i);
      if (rankOption != null && !rankOption.trim().isEmpty()) {
        options.add(rankOption);
      }
    }

    this.initialiseQuestionDetails(options);

    return true;
  }
  public FeedbackSubmissionAdjustmentAction(HttpServletRequest request) {
    super(request);

    this.courseId =
        HttpRequestHelper.getValueFromRequestParameterMap(request, ParamsNames.COURSE_ID);
    Assumption.assertNotNull(courseId);

    this.sessionName =
        HttpRequestHelper.getValueFromRequestParameterMap(
            request, ParamsNames.FEEDBACK_SESSION_NAME);
    Assumption.assertNotNull(sessionName);

    this.enrollmentDetails =
        HttpRequestHelper.getValueFromRequestParameterMap(request, ParamsNames.ENROLLMENT_DETAILS);
    Assumption.assertNotNull(enrollmentDetails);
  }
  private static FeedbackQuestionAttributes extractFeedbackQuestionData(
      Map<String, String[]> requestParameters) {
    FeedbackQuestionAttributes newQuestion = new FeedbackQuestionAttributes();

    newQuestion.setId(
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_ID));
    Assumption.assertNotNull("Null question id", newQuestion.getId());

    newQuestion.courseId =
        HttpRequestHelper.getValueFromParamMap(requestParameters, Const.ParamsNames.COURSE_ID);
    Assumption.assertNotNull("Null course id", newQuestion.courseId);

    newQuestion.feedbackSessionName =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_SESSION_NAME);
    Assumption.assertNotNull("Null feedback session name", newQuestion.feedbackSessionName);

    // TODO thoroughly investigate when and why these parameters can be null
    // and check all possibilities in the tests
    // should only be null when deleting. might be good to separate the delete action from this
    // class

    // Can be null
    String giverType =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_GIVERTYPE);
    if (giverType != null) {
      newQuestion.giverType = FeedbackParticipantType.valueOf(giverType);
    }

    // Can be null
    String recipientType =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_RECIPIENTTYPE);
    if (recipientType != null) {
      newQuestion.recipientType = FeedbackParticipantType.valueOf(recipientType);
    }

    String questionNumber =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_NUMBER);
    Assumption.assertNotNull("Null question number", questionNumber);
    newQuestion.questionNumber = Integer.parseInt(questionNumber);

    // Can be null
    String nEntityTypes =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_NUMBEROFENTITIESTYPE);
    if (numberOfEntitiesIsUserDefined(newQuestion.recipientType, nEntityTypes)) {
      String nEntities;
      nEntities =
          HttpRequestHelper.getValueFromParamMap(
              requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_NUMBEROFENTITIES);
      Assumption.assertNotNull(nEntities);
      newQuestion.numberOfEntitiesToGiveFeedbackTo = Integer.parseInt(nEntities);
    } else {
      newQuestion.numberOfEntitiesToGiveFeedbackTo = Const.MAX_POSSIBLE_RECIPIENTS;
    }

    newQuestion.showResponsesTo =
        getParticipantListFromParams(
            HttpRequestHelper.getValueFromParamMap(
                requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_SHOWRESPONSESTO));
    newQuestion.showGiverNameTo =
        getParticipantListFromParams(
            HttpRequestHelper.getValueFromParamMap(
                requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_SHOWGIVERTO));
    newQuestion.showRecipientNameTo =
        getParticipantListFromParams(
            HttpRequestHelper.getValueFromParamMap(
                requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_SHOWRECIPIENTTO));

    String questionType =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_TYPE);
    Assumption.assertNotNull(questionType);
    newQuestion.questionType = FeedbackQuestionType.valueOf(questionType);

    // Can be null
    String questionText =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_TEXT);
    if (questionText != null) {
      FeedbackAbstractQuestionDetails questionDetails =
          FeedbackAbstractQuestionDetails.createQuestionDetails(
              requestParameters, newQuestion.questionType);
      newQuestion.setQuestionDetails(questionDetails);
    }

    return newQuestion;
  }
  @Override
  protected ActionResult execute() throws EntityDoesNotExistException {
    courseId = getRequestParamValue(Const.ParamsNames.COURSE_ID);
    feedbackSessionName = getRequestParamValue(Const.ParamsNames.FEEDBACK_SESSION_NAME);
    Assumption.assertPostParamNotNull(Const.ParamsNames.COURSE_ID, courseId);
    Assumption.assertPostParamNotNull(Const.ParamsNames.FEEDBACK_SESSION_NAME, feedbackSessionName);

    setAdditionalParameters();
    verifyAccesibleForSpecificUser();

    String userEmailForCourse = getUserEmailForCourse();
    String userTeamForCourse = getUserTeamForCourse();
    String userSectionForCourse = getUserSectionForCourse();
    data = new FeedbackSubmissionEditPageData(account, student);
    data.bundle = getDataBundle(userEmailForCourse);
    Assumption.assertNotNull(
        "Feedback session " + feedbackSessionName + " does not exist in " + courseId + ".",
        data.bundle);

    checkAdditionalConstraints();

    setStatusToAdmin();

    if (!isSessionOpenForSpecificUser(data.bundle.feedbackSession)) {
      isError = true;
      statusToUser.add(
          new StatusMessage(
              Const.StatusMessages.FEEDBACK_SUBMISSIONS_NOT_OPEN, StatusMessageColor.WARNING));
      return createSpecificRedirectResult();
    }

    int numOfQuestionsToGet = data.bundle.questionResponseBundle.size();
    for (int questionIndx = 1; questionIndx <= numOfQuestionsToGet; questionIndx++) {
      String totalResponsesForQuestion =
          getRequestParamValue(
              Const.ParamsNames.FEEDBACK_QUESTION_RESPONSETOTAL + "-" + questionIndx);

      if (totalResponsesForQuestion == null) {
        continue; // question has been skipped (not displayed).
      }

      List<FeedbackResponseAttributes> responsesForQuestion =
          new ArrayList<FeedbackResponseAttributes>();
      String questionId =
          HttpRequestHelper.getValueFromParamMap(
              requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_ID + "-" + questionIndx);
      FeedbackQuestionAttributes questionAttributes = data.bundle.getQuestionAttributes(questionId);
      if (questionAttributes == null) {
        statusToUser.add(
            new StatusMessage(
                "The feedback session or questions may have changed while you were submitting. "
                    + "Please check your responses to make sure they are saved correctly.",
                StatusMessageColor.WARNING));
        isError = true;
        log.warning(
            "Question not found. (deleted or invalid id passed?) id: "
                + questionId
                + " index: "
                + questionIndx);
        continue;
      }

      FeedbackQuestionDetails questionDetails = questionAttributes.getQuestionDetails();

      int numOfResponsesToGet = Integer.parseInt(totalResponsesForQuestion);
      String qnId = "";

      Set<String> emailSet = data.bundle.getRecipientEmails(questionAttributes.getId());
      emailSet.add("");
      emailSet = StringHelper.recoverFromSanitizedText(emailSet);

      ArrayList<String> responsesRecipients = new ArrayList<String>();
      List<String> errors = new ArrayList<String>();

      for (int responseIndx = 0; responseIndx < numOfResponsesToGet; responseIndx++) {
        FeedbackResponseAttributes response =
            extractFeedbackResponseData(
                requestParameters, questionIndx, responseIndx, questionAttributes);

        if (response.feedbackQuestionType != questionAttributes.questionType) {
          errors.add(
              String.format(
                  Const.StatusMessages.FEEDBACK_RESPONSES_WRONG_QUESTION_TYPE, questionIndx));
        }

        qnId = response.feedbackQuestionId;

        boolean isExistingResponse = response.getId() != null;
        // test that if editing an existing response, that the edited response's id
        // came from the original set of existing responses loaded on the submission page
        if (isExistingResponse && !isExistingResponseValid(response)) {
          errors.add(
              String.format(Const.StatusMessages.FEEDBACK_RESPONSES_INVALID_ID, questionIndx));
          continue;
        }

        responsesRecipients.add(response.recipientEmail);
        // if the answer is not empty but the recipient is empty
        if (response.recipientEmail.isEmpty() && !response.responseMetaData.getValue().isEmpty()) {
          errors.add(
              String.format(
                  Const.StatusMessages.FEEDBACK_RESPONSES_MISSING_RECIPIENT, questionIndx));
        }

        if (response.responseMetaData.getValue().isEmpty()) {
          // deletes the response since answer is empty
          saveResponse(response);
        } else {
          response.giverEmail =
              questionAttributes.giverType.isTeam() ? userTeamForCourse : userEmailForCourse;
          response.giverSection = userSectionForCourse;
          responsesForQuestion.add(response);
        }
      }

      List<String> questionSpecificErrors =
          questionDetails.validateResponseAttributes(
              responsesForQuestion, data.bundle.recipientList.get(qnId).size());
      errors.addAll(questionSpecificErrors);

      if (!emailSet.containsAll(responsesRecipients)) {
        errors.add(
            String.format(Const.StatusMessages.FEEDBACK_RESPONSE_INVALID_RECIPIENT, questionIndx));
      }

      if (errors.isEmpty()) {
        for (FeedbackResponseAttributes response : responsesForQuestion) {
          saveResponse(response);
        }
      } else {
        List<StatusMessage> errorMessages = new ArrayList<StatusMessage>();

        for (String error : errors) {
          errorMessages.add(new StatusMessage(error, StatusMessageColor.DANGER));
        }

        statusToUser.addAll(errorMessages);
        isError = true;
      }
    }

    if (!isError) {
      statusToUser.add(
          new StatusMessage(
              Const.StatusMessages.FEEDBACK_RESPONSES_SAVED, StatusMessageColor.SUCCESS));
    }

    if (isUserRespondentOfSession()) {
      appendRespondant();
    } else {
      removeRespondant();
    }

    return createSpecificRedirectResult();
  }
  private FeedbackResponseAttributes extractFeedbackResponseData(
      Map<String, String[]> requestParameters,
      int questionIndx,
      int responseIndx,
      FeedbackQuestionAttributes feedbackQuestionAttributes) {

    FeedbackQuestionDetails questionDetails = feedbackQuestionAttributes.getQuestionDetails();
    FeedbackResponseAttributes response = new FeedbackResponseAttributes();

    // This field can be null if the response is new
    response.setId(
        HttpRequestHelper.getValueFromParamMap(
            requestParameters,
            Const.ParamsNames.FEEDBACK_RESPONSE_ID + "-" + questionIndx + "-" + responseIndx));

    response.feedbackSessionName =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_SESSION_NAME);
    Assumption.assertNotNull("Null feedback session name", response.feedbackSessionName);

    response.courseId =
        HttpRequestHelper.getValueFromParamMap(requestParameters, Const.ParamsNames.COURSE_ID);
    Assumption.assertNotNull("Null feedback courseId", response.courseId);

    response.feedbackQuestionId =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_ID + "-" + questionIndx);
    Assumption.assertNotNull("Null feedbackQuestionId", response.feedbackQuestionId);
    Assumption.assertEquals(
        "feedbackQuestionId Mismatch",
        feedbackQuestionAttributes.getId(),
        response.feedbackQuestionId);

    response.recipientEmail =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters,
            Const.ParamsNames.FEEDBACK_RESPONSE_RECIPIENT
                + "-"
                + questionIndx
                + "-"
                + responseIndx);
    Assumption.assertNotNull("Null feedback recipientEmail", response.recipientEmail);

    String feedbackQuestionType =
        HttpRequestHelper.getValueFromParamMap(
            requestParameters, Const.ParamsNames.FEEDBACK_QUESTION_TYPE + "-" + questionIndx);
    Assumption.assertNotNull("Null feedbackQuestionType", feedbackQuestionType);
    response.feedbackQuestionType = FeedbackQuestionType.valueOf(feedbackQuestionType);

    FeedbackParticipantType recipientType = feedbackQuestionAttributes.recipientType;
    if (recipientType == FeedbackParticipantType.INSTRUCTORS
        || recipientType == FeedbackParticipantType.NONE) {
      response.recipientSection = Const.DEFAULT_SECTION;
    } else if (recipientType == FeedbackParticipantType.TEAMS) {
      response.recipientSection =
          StudentsLogic.inst().getSectionForTeam(courseId, response.recipientEmail);
    } else if (recipientType == FeedbackParticipantType.STUDENTS) {
      StudentAttributes student = logic.getStudentForEmail(courseId, response.recipientEmail);
      response.recipientSection = (student == null) ? Const.DEFAULT_SECTION : student.section;
    } else {
      response.recipientSection = getUserSectionForCourse();
    }

    // This field can be null if the question is skipped
    String[] answer =
        HttpRequestHelper.getValuesFromParamMap(
            requestParameters,
            Const.ParamsNames.FEEDBACK_RESPONSE_TEXT + "-" + questionIndx + "-" + responseIndx);

    if (!questionDetails.isQuestionSkipped(answer)) {
      FeedbackResponseDetails responseDetails =
          FeedbackResponseDetails.createResponseDetails(
              answer,
              questionDetails.questionType,
              questionDetails,
              requestParameters,
              questionIndx,
              responseIndx);
      response.setResponseDetails(responseDetails);
    } else {
      response.responseMetaData = new Text("");
    }

    return response;
  }
 protected void logMessage(HttpServletRequest request, String message) {
   String url = HttpRequestHelper.getRequestedURL(request);
   ActivityLogEntry activityLogEntry =
       new ActivityLogEntry(servletName, action, null, message, url);
   log.log(Level.INFO, activityLogEntry.generateLogMessage());
 }