Exemple #1
0
    def test_start_before_end(self):
        """The start date must be before the end date."""
        school = SchoolFactory()
        school_year = SchoolYearFactory(school=school)
        data = {
            "school_year": str(school_year.id),
            "start_date":
            str(school_year.end_date + datetime.timedelta(days=3)),
            "end_date": str(school_year.start_date),
        }
        form = SchoolBreakForm(user=school.admin, data=data)

        is_valid = form.is_valid()

        assert not is_valid
        assert "The start date must be before the end date." in form.non_field_errors(
        )
Exemple #2
0
    def test_school_year_uuid(self):
        """A school year is fetched from the querystring."""
        user = self.make_user()
        today = user.get_local_today()
        # Use dates in the past so the school year won't be a "current" school year.
        school_year = SchoolYearFactory(
            school__admin=user,
            start_date=today - datetime.timedelta(days=365),
            end_date=today - datetime.timedelta(days=1),
        )

        with self.login(user):
            self.get_check_200("courses:create",
                               data={"school_year": str(school_year.uuid)})

        form = self.get_context("form")
        assert form.school_year == school_year
    def test_post(self):
        """A user can create a grade level for their school year."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        data = {"school_year": str(school_year.id), "name": "3rd Grade"}

        with self.login(user):
            response = self.post(
                "schools:grade_level_create", uuid=school_year.uuid, data=data
            )

        grade_level = GradeLevel.objects.get(school_year=school_year)
        assert grade_level.name == "3rd Grade"
        self.response_302(response)
        assert response.get("Location") == self.reverse(
            "schools:school_year_detail", uuid=school_year.uuid
        )
Exemple #4
0
    def test_schedules_in_grade_level_order(self):
        """The schedules are in the same order as the grade levels."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        grade_level = GradeLevelFactory(school_year=school_year)
        grade_level_2 = GradeLevelFactory(school_year=school_year)
        enrollment = EnrollmentFactory(
            grade_level=grade_level_2, student__school=user.school
        )
        enrollment_2 = EnrollmentFactory(grade_level=grade_level)

        with self.login(user):
            self.get("core:dashboard")

        schedules = self.get_context("schedules")
        assert schedules[0]["student"] == enrollment_2.student
        assert schedules[1]["student"] == enrollment.student
Exemple #5
0
    def test_grade_level_info(self):
        """The context has the grade level structure expected by the template."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        grade_level = GradeLevelFactory(school_year=school_year)
        course = CourseFactory(grade_levels=[grade_level])
        EnrollmentFactory(grade_level=grade_level)

        with self.login(user):
            self.get_check_200("schools:school_year_detail",
                               uuid=school_year.uuid)

        assert self.get_context("grade_levels") == [{
            "grade_level": grade_level,
            "courses": [course],
            "has_students": True
        }]
Exemple #6
0
    def test_no_enrollable_students(self):
        """When all students are enrolled, redirect back to the school year."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        grade_level = GradeLevelFactory(school_year=school_year)
        EnrollmentFactory(student__school=user.school, grade_level=grade_level)

        with self.login(user):
            response = self.get(
                "students:enrollment_create", school_year_uuid=school_year.uuid
            )

        assert response.get("Location") == self.reverse(
            "schools:school_year_detail", uuid=school_year.uuid
        )
        message = list(get_messages(response.wsgi_request))[0]
        assert str(message) == "All students are enrolled in the school year."
Exemple #7
0
    def test_schedules_in_grade_level_order(self, mock_timezone):
        """The schedules are in the same order as the grade levels."""
        wednesday = timezone.localdate() + relativedelta(weekday=WE)
        mock_timezone.localdate.return_value = wednesday
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        grade_level = GradeLevelFactory(school_year=school_year)
        grade_level_2 = GradeLevelFactory(school_year=school_year)
        enrollment = EnrollmentFactory(grade_level=grade_level_2)
        enrollment_2 = EnrollmentFactory(grade_level=grade_level)

        with self.login(user):
            self.get("core:daily")

        schedules = self.get_context("schedules")
        assert schedules[0]["student"] == enrollment_2.student
        assert schedules[1]["student"] == enrollment.student
Exemple #8
0
    def test_school_year_for_user_only(self, timezone):
        now = datetime.datetime(2020, 1, 26, tzinfo=pytz.utc)
        sunday = now.date()
        timezone.localdate.return_value = now.date()
        user = self.make_user()
        student, grade_level = self.make_student_enrolled_in_grade_level(
            user, sunday)
        SchoolYearFactory(
            start_date=sunday - datetime.timedelta(days=90),
            end_date=sunday + datetime.timedelta(days=90),
        )
        CourseFactory(grade_levels=[grade_level])

        with self.login(user):
            self.get("core:app")

        schedules = self.get_context("schedules")
        assert len(schedules[0]["courses"]) > 0
Exemple #9
0
    def test_no_grade_levels(self):
        """When no grade levels are available, redirect to create a grade level."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)

        with self.login(user):
            response = self.get(
                "students:enrollment_create", school_year_id=school_year.id
            )

        assert response.get("Location") == self.reverse(
            "schools:grade_level_create", pk=school_year.id
        )
        message = list(get_messages(response.wsgi_request))[0]
        assert (
            str(message)
            == "You need to create a grade level for a student to enroll in."
        )
Exemple #10
0
    def test_surrounding_dates_no_current_school_year(self):
        """When there is no current school year, pull the dates from today."""
        user = self.make_user()
        today = timezone.localdate()
        SchoolYearFactory(
            school=user.school,
            start_date=today - datetime.timedelta(days=5),
            end_date=today - datetime.timedelta(days=1),
        )
        StudentFactory(school=user.school)

        with self.login(user):
            self.get("core:daily")

        self.assertContext("ereyesterday", today - datetime.timedelta(days=2))
        self.assertContext("yesterday", today - datetime.timedelta(days=1))
        self.assertContext("tomorrow", today + datetime.timedelta(days=1))
        self.assertContext("overmorrow", today + datetime.timedelta(days=2))
Exemple #11
0
    def test_not_graded_work_from_other_school(self):
        user = self.make_user()
        student = StudentFactory(school=user.school)
        school_year = SchoolYearFactory(school=user.school)
        graded_work_1 = GradedWorkFactory(
            course_task__course__grade_level__school_year=school_year)
        CourseworkFactory(student=student,
                          course_task=graded_work_1.course_task)
        graded_work_2 = GradedWorkFactory()
        CourseworkFactory(course_task=graded_work_2.course_task)

        with self.login(user):
            self.get("students:grade")

        self.assertContext("work_to_grade", [{
            "student": student,
            "graded_work": [graded_work_1]
        }])
    def test_no_overlapping_school_years(self):
        """A school year's dates may not overlap with another."""
        school = SchoolFactory()
        school_year = SchoolYearFactory(school=school)
        cases = [
            (
                "surround",
                str(school_year.start_date - datetime.timedelta(days=1)),
                str(school_year.end_date + datetime.timedelta(days=1)),
            ),
            (
                "inside",
                str(school_year.start_date + datetime.timedelta(days=1)),
                str(school_year.end_date - datetime.timedelta(days=1)),
            ),
            (
                "overlap_start",
                str(school_year.start_date - datetime.timedelta(days=1)),
                str(school_year.end_date - datetime.timedelta(days=1)),
            ),
            (
                "overlap_end",
                str(school_year.start_date + datetime.timedelta(days=1)),
                str(school_year.end_date + datetime.timedelta(days=1)),
            ),
        ]

        for case in cases:
            with self.subTest(case[0]):
                data = {
                    "school": str(school.id),
                    "start_date": case[1],
                    "end_date": case[2],
                    "monday": True,
                }
                form = SchoolYearForm(user=school.admin, data=data)

                is_valid = form.is_valid()

                assert not is_valid
                assert (
                    "School years may not have overlapping dates."
                    in form.non_field_errors()[0]
                )
    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"
Exemple #14
0
    def test_has_days(self, timezone):
        """The context has the first and last day of the week."""
        user = self.make_user()
        now = datetime.datetime(2020, 1, 26, tzinfo=pytz.utc)
        first_day = now.date() + relativedelta(weekday=SU(-1))
        last_day = now.date() + relativedelta(weekday=SA(+1))
        timezone.localdate.return_value = now.date()
        SchoolYearFactory(
            school=user.school,
            start_date=now,
            end_date=now + datetime.timedelta(days=1),
        )
        StudentFactory(school=user.school)

        with self.login(user):
            self.get("core:dashboard")

        assert self.get_context("first_day") == first_day
        assert self.get_context("last_day") == last_day
Exemple #15
0
    def test_no_school(self):
        """A missing school is an error."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        data = {
            "school_day": "0",
            "description": "Christmas",
            "start_date": "2020-08-05",
            "end_date": "2020-08-05",
        }

        with self.login(user):
            response = self.post("schools:school_break_create",
                                 pk=school_year.id,
                                 data=data)

        form = self.get_context("form")
        assert form.non_field_errors() == ["Invalid school year."]
        self.response_200(response)
Exemple #16
0
    def test_course_days_on_school_year_days(self):
        """A course must run on days that are in the school year's set days."""
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school,
                                        days_of_week=SchoolYear.MONDAY)
        grade_level = GradeLevelFactory(school_year=school_year)
        data = {
            "name": "Will Not Work",
            "default_task_duration": "30",
            "grade_levels": [str(grade_level.id)],
            "tuesday": "on",
        }
        form = CourseForm(school_year, data=data)

        is_valid = form.is_valid()

        assert not is_valid
        assert ("The course must run within school year days: Monday"
                in form.non_field_errors())
Exemple #17
0
    def test_surrounding_dates(self, mock_timezone):
        wednesday = timezone.localdate() + relativedelta(weekday=WE)
        mock_timezone.localdate.return_value = wednesday
        user = self.make_user()
        SchoolYearFactory(
            school=user.school,
            days_of_week=SchoolYear.TUESDAY + SchoolYear.WEDNESDAY +
            SchoolYear.THURSDAY,
        )

        with self.login(user):
            self.get("core:daily")

        previous_thursday = wednesday - datetime.timedelta(days=6)
        self.assertContext("ereyesterday", previous_thursday)
        self.assertContext("yesterday", wednesday - datetime.timedelta(days=1))
        self.assertContext("tomorrow", wednesday + datetime.timedelta(days=1))
        next_tuesday = wednesday + datetime.timedelta(days=6)
        self.assertContext("overmorrow", next_tuesday)
Exemple #18
0
    def test_future_school_year(self):
        """Go to a future school year if there is no current one.

        The user may be new and have no currently active school year.
        It would be pretty lame to send them to the list
        when they may be building out their first school year.
        """
        user = self.make_user()
        today = user.get_local_today()
        school_year = SchoolYearFactory(
            school=user.school,
            start_date=today + datetime.timedelta(days=1),
            end_date=today + datetime.timedelta(days=200),
        )

        with self.login(user):
            self.get_check_200("schools:current_school_year")

        assert school_year == self.get_context("schoolyear")
Exemple #19
0
    def test_school_year_overlap_with_itself(self):
        """A school year is permitted to overlap with itself when updating."""
        school = SchoolFactory()
        school_year = SchoolYearFactory(school=school)

        data = {
            "school": str(school.id),
            "start_date":
            str(school_year.start_date - datetime.timedelta(days=1)),
            "end_date": str(school_year.end_date),
            "monday": True,
        }
        form = SchoolYearForm(user=school.admin,
                              instance=school_year,
                              data=data)

        is_valid = form.is_valid()

        assert is_valid
Exemple #20
0
    def test_has_schedules(self, mock_timezone):
        friday = timezone.localdate() + relativedelta(weekday=FR)
        mock_timezone.localdate.return_value = friday
        user = self.make_user()
        student = StudentFactory(school=user.school)
        school_year = SchoolYearFactory(
            school=user.school, days_of_week=SchoolYear.FRIDAY
        )
        grade_level = GradeLevelFactory(school_year=school_year)
        course = CourseFactory(grade_levels=[grade_level], days_of_week=Course.FRIDAY)
        task_1 = CourseTaskFactory(course=course)
        coursework = CourseworkFactory(
            student=student, course_task=task_1, completed_date=friday
        )
        course_2 = CourseFactory(grade_levels=[grade_level], days_of_week=Course.FRIDAY)
        task_2a = CourseTaskFactory(course=course_2)
        CourseworkFactory(
            student=student,
            course_task=task_2a,
            completed_date=friday - datetime.timedelta(days=1),
        )
        task_2b = CourseTaskFactory(course=course_2)
        course_3 = CourseFactory(
            grade_levels=[grade_level], days_of_week=Course.THURSDAY
        )
        CourseTaskFactory(course=course_3)
        course_4 = CourseFactory(grade_levels=[grade_level], days_of_week=Course.FRIDAY)
        EnrollmentFactory(student=student, grade_level=grade_level)

        with self.login(user):
            self.get("core:daily")

        expected_schedule = {
            "student": student,
            "courses": [
                {"course": course, "coursework": [coursework]},
                {"course": course_2, "task": task_2b},
                {"course": course_3},
                {"course": course_4, "no_scheduled_task": True},
            ],
        }
        self.assertContext("schedules", [expected_schedule])
Exemple #21
0
    def test_grade(self):
        user = self.make_user()
        student = StudentFactory(school=user.school)
        school_year = SchoolYearFactory(school=user.school)
        graded_work = GradedWorkFactory(
            course_task__course__grade_level__school_year=school_year)
        graded_work_2 = GradedWorkFactory(
            course_task__course__grade_level__school_year=school_year)
        data = {
            f"graded_work-{student.id}-{graded_work.id}": "100",
            f"graded_work-{student.id}-{graded_work_2.id}": "",
        }

        with self.login(user):
            response = self.post("students:grade", data=data)

        self.response_302(response)
        self.assertEqual(response.get("Location"), self.reverse("core:daily"))
        grade = Grade.objects.get(student=student, graded_work=graded_work)
        self.assertEqual(grade.score, 100)
    def test_incomplete_daily(self):
        today = timezone.now().date()
        user = self.make_user()
        student = StudentFactory(school=user.school)
        school_year = SchoolYearFactory(school=user.school)
        grade_level = GradeLevelFactory(school_year=school_year)
        course = CourseFactory(grade_level=grade_level)
        task = CourseTaskFactory(course=course)
        CourseworkFactory(student=student, course_task=task)
        data = {
            "completed_date": "{:%Y-%m-%d}".format(today),
            f"task-{student.id}-{task.id}": "off",
        }

        with self.login(user):
            self.post("core:daily", data=data)

        self.assertFalse(
            Coursework.objects.filter(student=student,
                                      course_task=task).exists())
Exemple #23
0
    def test_has_surrounding_week_dates(self, timezone):
        """The context has the previous and next week dates."""
        user = self.make_user()
        now = datetime.datetime(2020, 1, 26, tzinfo=pytz.utc)
        sunday = now.date() + relativedelta(weekday=SU(-1))
        previous_sunday = sunday - datetime.timedelta(days=7)
        next_sunday = sunday + datetime.timedelta(days=7)
        timezone.localdate.return_value = now.date()
        SchoolYearFactory(
            school=user.school,
            start_date=now,
            end_date=now + datetime.timedelta(days=1),
        )
        StudentFactory(school=user.school)

        with self.login(user):
            self.get("core:dashboard")

        assert self.get_context("previous_week_date") == previous_sunday
        assert self.get_context("next_week_date") == next_sunday
    def test_school_break_create_applies_to_one_student(self):
        """A school break is specific to a single student."""
        school = SchoolFactory()
        school_year = SchoolYearFactory(school=school)
        enrollment = EnrollmentFactory(grade_level__school_year=school_year)
        EnrollmentFactory(grade_level__school_year=school_year)
        data = {
            "school_year": str(school_year.id),
            "start_date": str(school_year.start_date),
            "end_date": str(school_year.start_date),
            f"student-{enrollment.student.id}": str(enrollment.student.id),
        }
        form = SchoolBreakForm(user=school.admin, data=data)
        form.is_valid()

        form.save()

        school_break = school_year.breaks.first()
        student_on_break = school_break.students.first()
        assert enrollment.student == student_on_break
    def test_planned_dates_for_future_school_years(self):
        """The planned dates for a future school year match with the year."""
        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,
        )
        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)
        forecaster = Forecaster()

        items = forecaster.get_task_items(enrollment.student, course)

        task_item = items[0]
        assert task_item["planned_date"] == school_year.start_date
    def test_no_forecast_course_not_running(self):
        """A course that isn't running will not forecast dates."""
        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,
        )
        enrollment = EnrollmentFactory(student__school=user.school,
                                       grade_level__school_year=school_year)
        course = CourseFactory(grade_levels=[enrollment.grade_level],
                               days_of_week=Course.NO_DAYS)
        CourseTaskFactory(course=course)
        forecaster = Forecaster()

        items = forecaster.get_task_items(enrollment.student, course)

        task_item = items[0]
        assert "planned_date" not in task_item
Exemple #27
0
    def test_has_week_dates(self, mock_timezone):
        sunday = timezone.localdate() + relativedelta(weekday=SU)
        monday = sunday - datetime.timedelta(days=6)
        mock_timezone.localdate.return_value = sunday
        user = self.make_user()
        SchoolYearFactory(
            school=user.school,
            days_of_week=SchoolYear.MONDAY + SchoolYear.TUESDAY +
            SchoolYear.WEDNESDAY + SchoolYear.THURSDAY + SchoolYear.FRIDAY,
        )

        with self.login(user):
            self.get("core:app")

        assert self.get_context("week_dates") == [
            monday,
            monday + datetime.timedelta(days=1),
            monday + datetime.timedelta(days=2),
            monday + datetime.timedelta(days=3),
            monday + datetime.timedelta(days=4),
        ]
Exemple #28
0
    def test_get(self):
        user = self.make_user()
        school_year = SchoolYearFactory(school=user.school)
        enrollment = EnrollmentFactory(grade_level__school_year=school_year)
        course = CourseFactory(grade_levels=[enrollment.grade_level],
                               days_of_week=Course.ALL_DAYS)
        coursework = CourseworkFactory(course_task__course=course)

        with self.login(user):
            self.get_check_200("schools:school_year_forecast",
                               pk=school_year.id)

        assert self.get_context("schoolyear") == school_year
        assert self.get_context("students") == [{
            "student":
            enrollment.student,
            "courses": [{
                "course": course,
                "last_forecast_date": coursework.completed_date
            }],
        }]
    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_surrounding_dates(self, timezone):
        now = datetime.datetime(2020, 1, 22, tzinfo=pytz.utc)
        wednesday = now.date()
        timezone.localdate.return_value = wednesday
        user = self.make_user()
        SchoolYearFactory(
            school=user.school,
            start_date=wednesday - datetime.timedelta(days=90),
            end_date=wednesday + datetime.timedelta(days=90),
            days_of_week=SchoolYear.TUESDAY + SchoolYear.WEDNESDAY +
            SchoolYear.THURSDAY,
        )

        with self.login(user):
            self.get("core:daily")

        previous_thursday = wednesday - datetime.timedelta(days=6)
        self.assertContext("ereyesterday", previous_thursday)
        self.assertContext("yesterday", wednesday - datetime.timedelta(days=1))
        self.assertContext("tomorrow", wednesday + datetime.timedelta(days=1))
        next_tuesday = wednesday + datetime.timedelta(days=6)
        self.assertContext("overmorrow", next_tuesday)