@Test
  public void testToString() {
    EvaluationAttributes e;
    e = generateValidEvaluationAttributesObject();

    String inStringFormat = Utils.getTeammatesGson().toJson(e, EvaluationAttributes.class);
    assertEquals(inStringFormat, e.toString());
  }
  @Test
  public void testSanitizeForSaving() {
    EvaluationAttributes e = generateValidEvaluationAttributesObject();
    EvaluationAttributes original = e.getCopy();

    // make it unsanitized
    e.courseId = "  " + e.courseId + "   ";
    e.name = "\t " + e.name + "  \t";
    e.instructions = new Text("   " + e.instructions.getValue() + "\n\t  ");

    e.sanitizeForSaving();

    assertEquals(original.toString(), e.toString());
  }
  private void verifyEvalUpdateFromAwaitingToClosed(int timeZone) throws Exception {

    EvaluationAttributes eval = getTypicalDataBundle().evaluations.get("evaluation1InCourse1");
    int milliSecondsPerMinute = 60 * 1000;

    // first, make it AWAITING
    eval.timeZone = timeZone;
    eval.gracePeriod = 15;
    eval.startTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(milliSecondsPerMinute, timeZone);
    eval.endTime = TimeHelper.getDateOffsetToCurrentTime(2);
    eval.published = false;
    eval.activated = false;
    assertEquals(EvalStatus.AWAITING, eval.getStatus());

    // then, make it CLOSED
    eval.startTime = TimeHelper.getDateOffsetToCurrentTime(-2);
    eval.endTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(
            -milliSecondsPerMinute * (eval.gracePeriod + 1), timeZone);
    eval.setDerivedAttributes();

    // check if derived attributes are set correctly
    assertEquals(true, eval.activated);
    assertEquals(false, eval.published);
    assertEquals(EvalStatus.CLOSED, eval.getStatus());
  }
  public static EvaluationAttributes generateValidEvaluationAttributesObject() {
    EvaluationAttributes e;
    e = new EvaluationAttributes();

    e.courseId = "valid-course";
    e.name = "valid name";
    e.instructions = new Text("1st line of instructions \n 2nd line of instructions");
    e.startTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(2);
    e.activated = false;
    e.published = false;
    e.timeZone = 0.0;
    e.gracePeriod = 5;
    e.p2pEnabled = true;
    return e;
  }
  @Test
  public void testCalculateEvalStatus() throws InterruptedException {

    double timeZone;
    int gracePeriod;
    EvaluationAttributes evaluation = new EvaluationAttributes();
    int safetyMargin = 1000; // we use this to compensate for test execution time

    ______TS("in the awaiting period");

    evaluation.startTime = TimeHelper.getMsOffsetToCurrentTime(safetyMargin);
    evaluation.endTime = TimeHelper.getDateOffsetToCurrentTime(1);

    timeZone = 0.0;
    evaluation.timeZone = timeZone;

    gracePeriod = 0;
    ;
    evaluation.gracePeriod = gracePeriod;

    evaluation.published = false;
    AssertJUnit.assertEquals(EvalStatus.AWAITING, evaluation.getStatus());

    ______TS("in the middle of open period");

    evaluation.startTime = TimeHelper.getDateOffsetToCurrentTime(-1);
    AssertJUnit.assertEquals(EvalStatus.OPEN, evaluation.getStatus());

    ______TS("just before grace period expires");

    gracePeriod = 5;
    int gracePeriodInMs = gracePeriod * 60 * 1000;
    evaluation.gracePeriod = gracePeriod;

    evaluation.endTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(gracePeriodInMs - safetyMargin, timeZone);

    timeZone = 0.0;
    evaluation.timeZone = timeZone;

    AssertJUnit.assertEquals(EvalStatus.OPEN, evaluation.getStatus());

    ______TS("just after the grace period expired");

    gracePeriod = 5;
    gracePeriodInMs = gracePeriod * 60 * 1000;
    evaluation.gracePeriod = gracePeriod;

    timeZone = 0.0;
    evaluation.timeZone = timeZone;

    evaluation.endTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(
            -gracePeriodInMs - safetyMargin, timeZone);

    AssertJUnit.assertEquals(EvalStatus.CLOSED, evaluation.getStatus());

    ______TS("already published");

    evaluation.published = true;
    AssertJUnit.assertEquals(EvalStatus.PUBLISHED, evaluation.getStatus());
    evaluation.published = false;

    ______TS("checking for user in different time zone");
    // do similar testing for +1.0 time zone

    timeZone = 1.0;
    evaluation.timeZone = timeZone;

    // in AWAITING period
    evaluation.startTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(safetyMargin, timeZone);
    evaluation.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    AssertJUnit.assertEquals(EvalStatus.AWAITING, evaluation.getStatus());

    // in OPEN period
    evaluation.startTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(-safetyMargin, timeZone);
    AssertJUnit.assertEquals(EvalStatus.OPEN, evaluation.getStatus());

    // TODO: just before grace period expired

    gracePeriod = 5;
    evaluation.gracePeriod = gracePeriod;

    timeZone = 1.0;
    evaluation.timeZone = timeZone;

    evaluation.startTime = TimeHelper.getDateOffsetToCurrentTime(-2);
    evaluation.endTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(
            -gracePeriodInMs + safetyMargin, timeZone);

    AssertJUnit.assertEquals(EvalStatus.OPEN, evaluation.getStatus());

    // just after grace period
    gracePeriod = 5;
    evaluation.gracePeriod = gracePeriod;

    timeZone = 1.0;
    evaluation.timeZone = timeZone;

    evaluation.startTime = TimeHelper.getDateOffsetToCurrentTime(-2);
    evaluation.endTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(
            -gracePeriodInMs - safetyMargin, timeZone);

    AssertJUnit.assertEquals(EvalStatus.CLOSED, evaluation.getStatus());

    // already PUBLISHED
    evaluation.published = true;
    AssertJUnit.assertEquals(EvalStatus.PUBLISHED, evaluation.getStatus());
  }
  @Test
  public void testIsReady() throws InvalidParametersException {

    // Create evaluation object to use as the test object
    Calendar start = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    Calendar end = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    EvaluationAttributes e =
        new EvaluationAttributes(
            new Evaluation(
                "course1",
                "evalution 1",
                new Text("instructions"),
                true,
                start.getTime(),
                end.getTime(),
                0.0,
                0));
    int oneSecInMilliSeconds = 1 * 1000;
    double timeZone = 0.0;

    ______TS("ready, just after start time");

    // start time set to 1 sec before current time
    e.startTime = TimeHelper.getMsOffsetToCurrentTime(-oneSecInMilliSeconds);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = false;
    e.timeZone = 0.0;
    assertEquals(true, e.isReadyToActivate());

    // negative time zone, starting just before current time
    timeZone = -2.0;
    e.startTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(-oneSecInMilliSeconds, timeZone);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = false;
    e.timeZone = timeZone;
    assertEquals(true, e.isReadyToActivate());

    // positive time zone, starting just before current time
    timeZone = 2.0;
    e.startTime =
        TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(-oneSecInMilliSeconds, timeZone);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = false;
    e.timeZone = timeZone;
    assertEquals(true, e.isReadyToActivate());

    ______TS("not ready, just before start time");
    // start time set to 1 sec after current time
    oneSecInMilliSeconds = 1 * 1000;
    e.startTime = TimeHelper.getMsOffsetToCurrentTime(+oneSecInMilliSeconds);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = false;
    e.timeZone = 0.0;
    assertEquals(false, e.isReadyToActivate());

    // negative time zone, starting just after current time
    timeZone = -2.0;
    e.startTime = TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(oneSecInMilliSeconds, timeZone);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = false;
    e.timeZone = timeZone;
    assertEquals(false, e.isReadyToActivate());

    // positive time zone, starting just after current time
    timeZone = 2.0;
    e.startTime = TimeHelper.getMsOffsetToCurrentTimeInUserTimeZone(oneSecInMilliSeconds, timeZone);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = false;
    e.timeZone = timeZone;
    assertEquals(false, e.isReadyToActivate());

    ______TS("not ready, already activated");

    // start time set to 1 sec before current time
    e.startTime = TimeHelper.getMsOffsetToCurrentTime(-oneSecInMilliSeconds);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = true;
    e.timeZone = 0.0;
    assertEquals(false, e.isReadyToActivate());

    // start time set to 1 sec after current time
    e.startTime = TimeHelper.getMsOffsetToCurrentTime(+oneSecInMilliSeconds);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.activated = true;
    e.timeZone = 0.0;
    assertEquals(false, e.isReadyToActivate());
  }
  @Test
  public void testValidate() {
    EvaluationAttributes e = new EvaluationAttributes();

    e.courseId = "";
    e.name = "";
    e.instructions = new Text("Instruction to students.");
    e.startTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(2);
    e.activated = false;
    e.published = false;
    e.timeZone = 0.0;
    e.gracePeriod = 5;
    e.p2pEnabled = true;

    assertEquals("invalid values", false, e.isValid());
    String errorMessage =
        String.format(COURSE_ID_ERROR_MESSAGE, e.courseId, REASON_EMPTY)
            + EOL
            + String.format(EVALUATION_NAME_ERROR_MESSAGE, e.name, REASON_EMPTY);
    assertEquals("valid values", errorMessage, StringHelper.toString(e.getInvalidityInfo()));

    e.courseId = "valid-course";
    e.name = "valid name";
    e.instructions = new Text("valid instructions");
    assertTrue("valid, minimal properties", e.isValid());

    assertEquals("valid values", true, e.isValid());

    e.startTime = null;
    try {
      e.getInvalidityInfo();
      signalFailureToDetectException("null start time not detected");
    } catch (AssertionError e1) {
      ignoreExpectedException();
    }

    e.startTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.endTime = null;
    try {
      e.getInvalidityInfo();
      signalFailureToDetectException("null end time not detected");
    } catch (AssertionError e1) {
      ignoreExpectedException();
    }

    // SUCCESS : end == start
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.startTime = e.endTime;
    assertTrue(e.isValid());

    // FAIL : end before start
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.startTime = TimeHelper.getDateOffsetToCurrentTime(2);
    assertFalse(e.isValid());
    errorMessage =
        String.format(
            TIME_FRAME_ERROR_MESSAGE, END_TIME_FIELD_NAME, EVALUATION_NAME, START_TIME_FIELD_NAME);
    assertEquals(errorMessage, StringHelper.toString(e.getInvalidityInfo()));

    // FAIL : published before endtime: invalid
    e.published = true;
    e.startTime = TimeHelper.getDateOffsetToCurrentTime(0);
    e.endTime = TimeHelper.getMsOffsetToCurrentTime(5);
    assertFalse(e.isValid());
    assertEquals(
        FieldValidator.EVALUATION_END_TIME_ERROR_MESSAGE,
        StringHelper.toString(e.getInvalidityInfo()));

    // SUCCESS : just after endtime and published: valid
    e.startTime = TimeHelper.getDateOffsetToCurrentTime(-1);
    e.endTime = TimeHelper.getMsOffsetToCurrentTime(-5);
    e.published = true;
    assertTrue(e.isValid());

    // FAIL : activated before start time: invalid
    e.startTime = TimeHelper.getDateOffsetToCurrentTime(1);
    e.endTime = TimeHelper.getDateOffsetToCurrentTime(2);
    e.published = false;
    e.activated = true;
    assertFalse(e.isValid());
    assertEquals(
        FieldValidator.EVALUATION_START_TIME_ERROR_MESSAGE,
        StringHelper.toString(e.getInvalidityInfo()));
  }
  @Override
  public ActionResult execute() throws EntityDoesNotExistException {

    String courseId = getRequestParamValue(Const.ParamsNames.COURSE_ID);
    String evalName = getRequestParamValue(Const.ParamsNames.EVALUATION_NAME);
    String fromEmail = getRequestParamValue(Const.ParamsNames.FROM_EMAIL);
    String teamName = getRequestParamValue(Const.ParamsNames.TEAM_NAME);

    if (isParameterNull(courseId)
        || isParameterNull(evalName)
        || isParameterNull(fromEmail)
        || isParameterNull(teamName)) {
      return redirectAndShowExpiredRequest();
    } else {

      String[] toEmails = getRequestParamValues(Const.ParamsNames.TO_EMAIL);
      String[] points = getRequestParamValues(Const.ParamsNames.POINTS);
      String[] justifications = getRequestParamValues(Const.ParamsNames.JUSTIFICATION);
      String[] comments = getRequestParamValues(Const.ParamsNames.COMMENTS);

      EvaluationAttributes eval = logic.getEvaluation(courseId, evalName);

      if (eval.getStatus() == EvalStatus.PUBLISHED) {
        throw new UnauthorizedAccessException(Const.Tooltips.EVALUATION_STATUS_PUBLISHED);
      } else if (eval.getStatus() == EvalStatus.CLOSED) {
        throw new UnauthorizedAccessException(Const.Tooltips.EVALUATION_STATUS_CLOSED);
      } else if (eval.getStatus() == EvalStatus.AWAITING) {
        throw new UnauthorizedAccessException(Const.Tooltips.EVALUATION_STATUS_AWAITING);
      } else if (eval.getStatus() == EvalStatus.DOES_NOT_EXIST) {
        throw new UnauthorizedAccessException(Const.StatusMessages.EVALUATION_DELETED);
      }

      // extract submission data
      ArrayList<SubmissionAttributes> submissionData = new ArrayList<SubmissionAttributes>();
      int submissionCount = (toEmails == null ? 0 : toEmails.length);
      boolean emptyPointExists = false;
      for (int i = 0; i < submissionCount; i++) {
        SubmissionAttributes sub = new SubmissionAttributes();
        sub.course = courseId;
        sub.evaluation = evalName;
        sub.justification = new Text(justifications[i]);

        if (eval.p2pEnabled) {
          sub.p2pFeedback = new Text(comments[i]);
        }

        try {
          sub.points = Integer.parseInt(points[i]);
        } catch (NumberFormatException e) {
          // The point dropdown is unfilled and is blank
          sub.points = Const.POINTS_NOT_SUBMITTED;
          emptyPointExists = true;
        }

        sub.reviewee = toEmails[i];
        sub.reviewer = fromEmail;
        sub.team = teamName;
        submissionData.add(sub);
      }

      if (emptyPointExists) {
        isError = true;
        statusToUser.add("Please give contribution scale to everyone");
      }

      new GateKeeper()
          .verifyAccessible(
              logic.getStudentForGoogleId(courseId, account.googleId), submissionData);

      try {
        logic.updateSubmissions(submissionData);
        statusToAdmin =
            createLogMesage(
                courseId,
                evalName,
                teamName,
                fromEmail,
                toEmails,
                points,
                justifications,
                comments);
      } catch (InvalidParametersException e) {
        // TODO: Let the user retry?
        setStatusForException(e);
      }

      RedirectResult response;
      if (isError) {
        String submissionUrl = Const.ActionURIs.STUDENT_EVAL_SUBMISSION_EDIT_PAGE;
        submissionUrl = Url.addParamToUrl(submissionUrl, Const.ParamsNames.COURSE_ID, courseId);
        submissionUrl =
            Url.addParamToUrl(submissionUrl, Const.ParamsNames.EVALUATION_NAME, evalName);
        submissionUrl =
            Url.addParamToUrl(submissionUrl, Const.ParamsNames.USER_ID, account.googleId);
        response = createRedirectResult(submissionUrl);
      } else {
        statusToUser.add(
            String.format(
                Const.StatusMessages.STUDENT_EVALUATION_SUBMISSION_RECEIVED,
                Sanitizer.sanitizeForHtml(evalName),
                courseId));
        String submissionUrl = Const.ActionURIs.STUDENT_HOME_PAGE;

        String submittedEvaluationName = courseId + evalName;
        submissionUrl =
            Url.addParamToUrl(
                submissionUrl,
                Const.ParamsNames.CHECK_PERSISTENCE_EVALUATION,
                submittedEvaluationName);
        log.info(submittedEvaluationName);
        response = createRedirectResult(submissionUrl);
      }
      return response;
    }
  }
  public void testGetCourseDetailsListForStudent() throws Exception {

    ______TS("student having multiple evaluations in multiple courses");

    CourseAttributes expectedCourse1 = dataBundle.courses.get("typicalCourse1");
    CourseAttributes expectedCourse2 = dataBundle.courses.get("typicalCourse2");

    EvaluationAttributes expectedEval1InCourse1 =
        dataBundle.evaluations.get("evaluation1InCourse1");
    EvaluationAttributes expectedEval2InCourse1 =
        dataBundle.evaluations.get("evaluation2InCourse1");

    EvaluationAttributes expectedEval1InCourse2 =
        dataBundle.evaluations.get("evaluation1InCourse2");

    // This student is in both course 1 and 2
    StudentAttributes studentInBothCourses = dataBundle.students.get("student2InCourse1");

    // Make sure all evaluations in course1 are visible (i.e., not AWAITING)
    expectedEval1InCourse1.startTime = TimeHelper.getDateOffsetToCurrentTime(-2);
    expectedEval1InCourse1.endTime = TimeHelper.getDateOffsetToCurrentTime(-1);
    expectedEval1InCourse1.published = false;
    assertEquals(EvalStatus.CLOSED, expectedEval1InCourse1.getStatus());
    BackDoorLogic backDoorLogic = new BackDoorLogic();
    backDoorLogic.updateEvaluation(expectedEval1InCourse1);

    expectedEval2InCourse1.startTime = TimeHelper.getDateOffsetToCurrentTime(-1);
    expectedEval2InCourse1.endTime = TimeHelper.getDateOffsetToCurrentTime(1);
    assertEquals(EvalStatus.OPEN, expectedEval2InCourse1.getStatus());
    backDoorLogic.updateEvaluation(expectedEval2InCourse1);

    // Make sure all evaluations in course2 are still AWAITING
    expectedEval1InCourse2.startTime = TimeHelper.getDateOffsetToCurrentTime(1);
    expectedEval1InCourse2.endTime = TimeHelper.getDateOffsetToCurrentTime(2);
    expectedEval1InCourse2.activated = false;
    assertEquals(EvalStatus.AWAITING, expectedEval1InCourse2.getStatus());
    backDoorLogic.updateEvaluation(expectedEval1InCourse2);

    // Get course details for student
    List<CourseDetailsBundle> courseList =
        coursesLogic.getCourseDetailsListForStudent(studentInBothCourses.googleId);

    // Verify number of courses received
    assertEquals(2, courseList.size());

    // Verify details of course 1 (note: index of course 1 is not 0)
    CourseDetailsBundle actualCourse1 = courseList.get(1);
    assertEquals(expectedCourse1.id, actualCourse1.course.id);
    assertEquals(expectedCourse1.name, actualCourse1.course.name);
    assertEquals(2, actualCourse1.evaluations.size());

    // Verify details of evaluation 1 in course 1
    EvaluationAttributes actualEval1InCourse1 = actualCourse1.evaluations.get(1).evaluation;
    TestHelper.verifySameEvaluationData(expectedEval1InCourse1, actualEval1InCourse1);

    // Verify some details of evaluation 2 in course 1
    EvaluationAttributes actualEval2InCourse1 = actualCourse1.evaluations.get(0).evaluation;
    TestHelper.verifySameEvaluationData(expectedEval2InCourse1, actualEval2InCourse1);

    // For course 2, verify no evaluations returned (because the evaluation
    // in this course is still AWAITING.
    CourseDetailsBundle actualCourse2 = courseList.get(0);
    assertEquals(expectedCourse2.id, actualCourse2.course.id);
    assertEquals(expectedCourse2.name, actualCourse2.course.name);
    assertEquals(0, actualCourse2.evaluations.size());

    ______TS("student in a course with no evaluations");

    StudentAttributes studentWithNoEvaluations = dataBundle.students.get("student1InCourse2");
    courseList = coursesLogic.getCourseDetailsListForStudent(studentWithNoEvaluations.googleId);
    assertEquals(1, courseList.size());
    assertEquals(0, courseList.get(0).evaluations.size());

    // student with no courses is not applicable

    ______TS("non-existent student");

    try {
      coursesLogic.getCourseDetailsListForStudent("non-existent-student");
      signalFailureToDetectException();
    } catch (EntityDoesNotExistException e) {
      AssertHelper.assertContains("does not exist", e.getMessage());
    }

    ______TS("null parameter");

    try {
      coursesLogic.getCourseDetailsListForStudent(null);
      signalFailureToDetectException();
    } catch (AssertionError e) {
      assertEquals("Supplied parameter was null\n", e.getMessage());
    }
  }