def test_break_day(self): """A break day is noted in the context and nothing is scheduled.""" today = timezone.localdate() user = self.make_user() school_year = SchoolYearFactory( school=user.school, start_date=today + relativedelta(month=1, day=1), end_date=today + relativedelta(month=12, day=31), days_of_week=SchoolYear.ALL_DAYS, ) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date, end_date=school_year.end_date, ) enrollment = EnrollmentFactory(grade_level__school_year=school_year) CourseTaskFactory(course__grade_levels=[enrollment.grade_level]) with self.login(user): self.get( "core:daily_for_date", year=school_year.start_date.year, month=school_year.start_date.month, day=school_year.start_date.day, ) assert self.get_context("is_break_day") assert self.get_context("schedules") == []
def test_get_date_type(self): """Each date type has a representation.""" today = timezone.localdate() single = SchoolBreakFactory.build(start_date=today, end_date=today) multi_day = SchoolBreakFactory.build(start_date=today, end_date=today + datetime.timedelta(days=2)) assert single.get_date_type(today) == SchoolBreak.DateType.SINGLE assert multi_day.get_date_type(today) == SchoolBreak.DateType.START assert (multi_day.get_date_type(today + datetime.timedelta( days=1)) == SchoolBreak.DateType.MIDDLE) assert multi_day.get_date_type( multi_day.end_date) == SchoolBreak.DateType.END assert (multi_day.get_date_type(today - datetime.timedelta( days=1)) == SchoolBreak.DateType.NOT_A_BREAK)
def test_school_dates(self): """The dates on the report have the expected states.""" user = self.make_user() enrollment = EnrollmentFactory( grade_level__school_year__school=user.school) school_year = enrollment.grade_level.school_year SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date, end_date=school_year.start_date, ) CourseworkFactory( student=enrollment.student, course_task__course__grade_levels=[enrollment.grade_level], completed_date=school_year.start_date + datetime.timedelta(days=1), ) with self.login(user): self.get_check_200("reports:attendance", pk=enrollment.id) school_dates = self.get_context("school_dates") assert school_dates[0]["is_break"] assert school_dates[1]["attended"] assert not school_dates[4]["is_school_day"] # First Saturday assert self.get_context("total_days_attended") == 1
def test_planned_date_accounts_for_breaks(self): """A planned date for a task will skip break days.""" today = timezone.localdate() user = self.make_user() school_year = SchoolYearFactory( school=user.school, start_date=today + relativedelta(years=1, month=1, day=1), end_date=today + relativedelta(years=1, month=12, day=31), days_of_week=SchoolYear.ALL_DAYS, ) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date, end_date=school_year.start_date, ) enrollment = EnrollmentFactory( student__school=user.school, grade_level__school_year=school_year ) course = CourseFactory( grade_levels=[enrollment.grade_level], days_of_week=Course.ALL_DAYS ) CourseTaskFactory(course=course) with self.login(user): self.get_check_200( "students:course", pk=enrollment.student.id, course_id=course.id ) task_item = self.get_context("task_items")[0] assert task_item["planned_date"] == school_year.start_date + datetime.timedelta( days=1 )
def test_factory(self): school_break = SchoolBreakFactory() assert school_break.start_date is not None assert school_break.end_date is not None assert school_break.description != "" assert school_break.school_year is not None
def test_is_break(self): """A school year can check if a date is a break day.""" school_break = SchoolBreakFactory() school_year = school_break.school_year assert school_year.is_break(school_break.start_date, student=None) assert not school_year.is_break( school_break.end_date + datetime.timedelta(days=1), student=None)
def test_get(self): user = self.make_user() school_break = SchoolBreakFactory(school_year__school=user.school) with self.login(user): self.get_check_200("schools:school_break_edit", pk=school_break.id) assert self.get_context("school_year") == school_break.school_year
def test_has_school_break(self): """A date can return an existing school break.""" expected_school_break = SchoolBreakFactory() school_year = expected_school_break.school_year school_break = school_year.get_break(expected_school_break.start_date) assert school_break == expected_school_break
def test_post_other_user(self): """A user may not delete another user's break.""" user = self.make_user() school_break = SchoolBreakFactory() with self.login(user): response = self.post("schools:school_break_delete", uuid=school_break.uuid) self.response_404(response)
def test_only_users_breaks(self): """A user can only edit their own school breaks.""" user = self.make_user() school_break = SchoolBreakFactory() with self.login(user): response = self.get("schools:school_break_edit", uuid=school_break.uuid) self.response_404(response)
def test_get_break_for_all_students(self): """Get a break for a student for a break that applies to all students.""" expected_school_break = SchoolBreakFactory() school_year = expected_school_break.school_year student = StudentFactory() school_break = school_year.get_break(expected_school_break.start_date, student=student) assert school_break == expected_school_break
def test_calendar_with_break_types(self): """The calendar supports single day breaks and multi-day breaks.""" # Freeze the time because the month dates may not be deterministic # for fitting the dates to check into a week. today = datetime.date(2020, 7, 18) school_year = SchoolYearFactory( start_date=today + relativedelta(day=1), end_date=today + relativedelta(months=1, day=1), ) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date, end_date=school_year.start_date, ) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date + datetime.timedelta(days=1), end_date=school_year.start_date + datetime.timedelta(days=3), ) year_calendar = YearCalendar(school_year, school_year.start_date) calendar = year_calendar.build() dates = calendar["months"][0]["weeks"][0] # There can be padding in the month so the test must search for the first day. first_date_data = {} first_day_index = 0 for index, date_data in enumerate(dates): if date_data["day"] == 1: first_date_data = date_data first_day_index = index break assert first_date_data["school_break"] is not None assert first_date_data["break_style"] == "single" start_date_data = dates[first_day_index + 1] assert start_date_data["school_break"] is not None assert start_date_data["break_style"] == "start" middle_date_data = dates[first_day_index + 2] assert middle_date_data["school_break"] is not None assert middle_date_data["break_style"] == "middle" end_date_data = dates[first_day_index + 3] assert end_date_data["school_break"] is not None assert end_date_data["break_style"] == "end"
def test_get_break_for_other_student_break(self): """A break for another student will return None for a different student.""" expected_school_break = SchoolBreakFactory() school_year = expected_school_break.school_year student = StudentFactory() other_student = StudentFactory() expected_school_break.students.add(other_student) school_break = school_year.get_break(expected_school_break.start_date, student=student) assert school_break is None
def test_post(self): user = self.make_user() school_break = SchoolBreakFactory(school_year__school=user.school) with self.login(user): response = self.post("schools:school_break_delete", pk=school_break.id) assert SchoolBreak.objects.count() == 0 self.response_302(response) assert response.get("Location") == self.reverse( "schools:school_year_detail", pk=school_break.school_year.id)
def test_no_date_change_with_breaks(self): """A school year must contain any breaks.""" school = SchoolFactory() school_year = SchoolYearFactory(school=school) start = school_year.start_date end = school_year.end_date SchoolBreakFactory(school_year=school_year, start_date=start, end_date=start) SchoolBreakFactory(school_year=school_year, start_date=end, end_date=end) data = { "school": str(school.id), "start_date": str(start + datetime.timedelta(days=1)), "end_date": str(end - datetime.timedelta(days=1)), "monday": True, } form = SchoolYearForm(user=school.admin, instance=school_year, data=data) is_valid = form.is_valid() assert not is_valid errors = form.non_field_errors() assert "You have a school break before the school year's start date." in errors assert "You have a school break after the school year's end date." in errors
def test_school_break_overlap_with_itself(self): """A school break is permitted to overlap with itself when updating.""" school = SchoolFactory() school_break = SchoolBreakFactory(school_year__school=school) data = { "school_year": str(school_break.school_year.id), "start_date": str(school_break.start_date - datetime.timedelta(days=1)), "end_date": str(school_break.end_date), } form = SchoolBreakForm(user=school.admin, instance=school_break, data=data) is_valid = form.is_valid() assert is_valid
def test_post(self): """A user can update a school break for their school year.""" user = self.make_user() school_break = SchoolBreakFactory(school_year__school=user.school) data = { "school_year": str(school_break.school_year.id), "description": "Christmas", "start_date": str(school_break.school_year.start_date), "end_date": str(school_break.school_year.start_date), } with self.login(user): response = self.post("schools:school_break_edit", pk=school_break.id, data=data) school_break.refresh_from_db() assert school_break.description == "Christmas" assert school_break.start_date == school_break.school_year.start_date assert school_break.end_date == school_break.school_year.start_date self.response_302(response) assert response.get("Location") == self.reverse( "schools:school_year_detail", pk=school_break.school_year.id)
def test_next_course_day_all_breaks(self): """When all school days are breaks, get the last possible course day.""" school_year = SchoolYearFactory(days_of_week=SchoolYear.ALL_DAYS) grade_level = GradeLevelFactory(school_year=school_year) course = CourseFactory(grade_levels=[grade_level], days_of_week=SchoolYear.ALL_DAYS) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date, end_date=school_year.end_date, ) next_course_day = school_year.get_next_course_day( course, school_year.start_date, student=None) assert next_course_day == school_year.end_date
def test_next_course_day_with_break(self): """A break affects when the next course day is.""" school_year = SchoolYearFactory(days_of_week=SchoolYear.ALL_DAYS) grade_level = GradeLevelFactory(school_year=school_year) course = CourseFactory(grade_levels=[grade_level], days_of_week=SchoolYear.ALL_DAYS) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date + datetime.timedelta(days=1), end_date=school_year.start_date + datetime.timedelta(days=1), ) next_course_day = school_year.get_next_course_day( course, school_year.start_date, student=None) assert next_course_day == school_year.start_date + datetime.timedelta( days=2)
def test_no_overlapping_breaks(self): """Two school breaks may not overlap.""" school = SchoolFactory() school_break = SchoolBreakFactory(school_year__school=school) cases = [ ( "surround", str(school_break.start_date - datetime.timedelta(days=1)), str(school_break.end_date + datetime.timedelta(days=1)), ), ( "inside", str(school_break.start_date + datetime.timedelta(days=1)), str(school_break.end_date - datetime.timedelta(days=1)), ), ( "overlap_start", str(school_break.start_date - datetime.timedelta(days=1)), str(school_break.end_date - datetime.timedelta(days=1)), ), ( "overlap_end", str(school_break.start_date + datetime.timedelta(days=1)), str(school_break.end_date + datetime.timedelta(days=1)), ), ] for case in cases: with self.subTest(case[0]): data = { "school_year": str(school_break.school_year.id), "start_date": case[1], "end_date": case[2], } form = SchoolBreakForm(user=school.admin, data=data) is_valid = form.is_valid() assert not is_valid assert ( "School breaks may not have overlapping dates." in form.non_field_errors()[0] )
def test_get(self): user = self.make_user() school_break = SchoolBreakFactory(school_year__school=user.school) # Make only one student on break. enrollment_1 = EnrollmentFactory( student__first_name="Alice", grade_level__school_year=school_break.school_year, ) school_break.students.add(enrollment_1.student) enrollment_2 = EnrollmentFactory(student__first_name="Bob", grade_level=enrollment_1.grade_level) with self.login(user): self.get_check_200("schools:school_break_edit", pk=school_break.id) assert self.get_context("school_year") == school_break.school_year students = self.get_context("students") assert students == [enrollment_1.student, enrollment_2.student] assert students[0].is_on_break assert not students[1].is_on_break
def test_at_least_one_enrolled(self): """When students are enrolled, at least one must be on a break.""" school = SchoolFactory() school_break = SchoolBreakFactory(school_year__school=school) enrollment = EnrollmentFactory( grade_level__school_year=school_break.school_year ) school_break.students.add(enrollment.student) EnrollmentFactory(grade_level=enrollment.grade_level) data = { "school_year": str(school_break.school_year.id), "start_date": str(school_break.start_date), "end_date": str(school_break.end_date), } form = SchoolBreakForm(user=school.admin, instance=school_break, data=data) is_valid = form.is_valid() assert not is_valid assert "At least one student must be on break." in form.non_field_errors()[0]
def test_breaks_in_task_count(self): """A break is factored into the course task count.""" school_year = SchoolYearFactory(days_of_week=SchoolYear.ALL_DAYS) grade_level = GradeLevelFactory(school_year=school_year) course = CourseFactory(grade_levels=[grade_level], days_of_week=SchoolYear.ALL_DAYS) SchoolBreakFactory( school_year=school_year, start_date=school_year.start_date, end_date=school_year.start_date, ) CourseTaskFactory(course=course) CourseTaskFactory(course=course) count = school_year.get_task_count_in_range( course, school_year.start_date, school_year.start_date + datetime.timedelta(days=1), ) assert count == 1
def test_school_break_edit_switches_student_break(self): """A school break will switch student who is on break.""" school = SchoolFactory() school_year = SchoolYearFactory(school=school) enrollment_1 = EnrollmentFactory(grade_level__school_year=school_year) enrollment_2 = EnrollmentFactory(grade_level__school_year=school_year) start = school_year.start_date school_break = SchoolBreakFactory( school_year=school_year, start_date=start, end_date=start ) school_break.students.add(enrollment_1.student) data = { "school_year": str(school_year.id), "start_date": str(start), "end_date": str(start), f"student-{enrollment_2.student.id}": str(enrollment_2.student.id), } form = SchoolBreakForm(user=school.admin, instance=school_break, data=data) form.is_valid() form.save() assert school_break.students.first() == enrollment_2.student
def test_get_week_with_breaks(self): """Next week starts with the correct task when the current week has breaks.""" today = timezone.now().date() next_week = Week(today + datetime.timedelta(days=7)) enrollment = EnrollmentFactory() school_year = enrollment.grade_level.school_year student = enrollment.student course = CourseFactory(grade_levels=[enrollment.grade_level]) CourseworkFactory(student=student, course_task__course=course, completed_date=today) task = CourseTaskFactory(course=course) CourseTaskFactory(course=course) SchoolBreakFactory( school_year=school_year, start_date=today + datetime.timedelta(days=1), end_date=today + datetime.timedelta(days=2), ) week_schedule = student.get_week_schedule(school_year, today, next_week) assert week_schedule["courses"][0]["days"][0]["task"] == task
def test_str(self): school_break = SchoolBreakFactory() assert str(school_break) == f"School Break {school_break.start_date}"
def test_unauthenticated_access(self): grade_level = SchoolBreakFactory() self.assertLoginRequired("schools:grade_level_edit", uuid=grade_level.uuid)
def test_has_schedules(self, timezone): """The schedules are in the context.""" now = datetime.datetime(2020, 1, 24, tzinfo=pytz.utc) friday = now.date() monday = friday - datetime.timedelta(days=4) timezone.localdate.return_value = now.date() user = self.make_user() student, grade_level = self.make_student_enrolled_in_grade_level(user, friday) course = CourseFactory( grade_levels=[grade_level], days_of_week=Course.MONDAY + Course.WEDNESDAY + Course.THURSDAY + Course.FRIDAY, ) task_1 = CourseTaskFactory(course=course) task_2 = CourseTaskFactory(course=course) task_3 = CourseTaskFactory(course=course) coursework = CourseworkFactory( student=student, course_task=task_1, completed_date=monday ) school_break = SchoolBreakFactory( school_year=grade_level.school_year, start_date=monday + datetime.timedelta(days=4), end_date=monday + datetime.timedelta(days=4), ) with self.login(user), self.assertNumQueries(16): self.get("core:app") expected_schedule = { "student": student, "courses": [ { "course": course, "days": [ { "week_date": monday, "coursework": [coursework], "school_break": None, }, { "week_date": monday + datetime.timedelta(days=1), "school_break": None, }, { "week_date": monday + datetime.timedelta(days=2), "task": task_2, "school_break": None, }, { "week_date": monday + datetime.timedelta(days=3), "task": task_3, "school_break": None, }, { "week_date": monday + datetime.timedelta(days=4), "school_break": school_break, "date_type": SchoolBreak.DateType.SINGLE, }, ], } ], } schedules = self.get_context("schedules") assert schedules == [expected_schedule]
def test_unauthenticated_access(self): school_break = SchoolBreakFactory() self.assertLoginRequired("schools:school_break_edit", uuid=school_break.uuid)
def test_unauthenticated_access(self): school_break = SchoolBreakFactory() self.assertLoginRequired("schools:school_break_delete", pk=school_break.id)