Exemple #1
0
    def test_working_hours_multidate(self):
        """
        Test checking working hours when lesson starts in one day, and ends on
        another. This will be frequent situations, because our teachers are
        in different timezones.
        """
        mixer.blend(WorkingHours, teacher=self.teacher, start='23:00', end='23:59', weekday=0)
        mixer.blend(WorkingHours, teacher=self.teacher, start='00:00', end='02:00', weekday=1)

        entry_besides_hours = TimelineEntry(
            teacher=self.teacher,
            start=self.tzdatetime(2032, 5, 3, 22, 0),  # does not fit
            end=self.tzdatetime(2032, 5, 4, 0, 30)
        )
        self.assertFalse(entry_besides_hours.is_fitting_working_hours())

        entry_besides_hours.start = self.tzdatetime(2032, 5, 3, 22, 30)  # does fit
        entry_besides_hours.end = self.tzdatetime(2016, 7, 26, 2, 30)    # does not fit

        self.assertFalse(entry_besides_hours.is_fitting_working_hours())

        entry_within_hours = TimelineEntry(
            teacher=self.teacher,
            start=self.tzdatetime(2032, 5, 3, 23, 30),
            end=self.tzdatetime(2032, 5, 4, 0, 30)
        )
        self.assertTrue(entry_within_hours.is_fitting_working_hours())
Exemple #2
0
    def test_free_slots_for_lesson(self):
        """
        Test for getting free time slots for a particular teacher with particular
        lesson
        """
        other_teacher = create_teacher()

        master_class = mixer.blend(lessons.MasterClass, host=self.teacher)
        other_master_class = mixer.blend(lessons.MasterClass,
                                         host=other_teacher)

        entry = TimelineEntry(teacher=self.teacher,
                              lesson=master_class,
                              start=self.tzdatetime(2032, 5, 3, 14, 10),
                              end=self.tzdatetime(2032, 5, 3, 14, 40))
        entry.save()
        other_entry = TimelineEntry(teacher=other_teacher,
                                    lesson=other_master_class,
                                    start=self.tzdatetime(2032, 5, 3, 14, 10),
                                    end=self.tzdatetime(2032, 5, 3, 14, 40))
        other_entry.save()
        slots = self.teacher.find_free_slots(self.tzdatetime(2032, 5, 3),
                                             lesson_id=master_class.pk)
        self.assertEquals(len(slots), 1)
        slots = self.teacher.find_free_slots(self.tzdatetime(2032, 5, 3),
                                             lesson_id=other_master_class.pk)
        self.assertEquals(len(slots), 0)
Exemple #3
0
    def test_working_hours(self):
        mixer.blend(WorkingHours, teacher=self.teacher, start='12:00', end='13:00', weekday=0)
        entry_besides_hours = TimelineEntry(
            teacher=self.teacher,
            start=self.tzdatetime(2032, 5, 3, 4, 0),
            end=self.tzdatetime(2032, 5, 3, 4, 30),
        )
        self.assertFalse(entry_besides_hours.is_fitting_working_hours())

        entry_within_hours = TimelineEntry(
            teacher=self.teacher,
            start=self.tzdatetime(2032, 5, 3, 12, 30),
            end=self.tzdatetime(2032, 5, 3, 13, 0),
        )
        self.assertTrue(entry_within_hours.is_fitting_working_hours())
Exemple #4
0
 def test_working_hours_nonexistant(self):
     entry = TimelineEntry(
         teacher=self.teacher,
         start=self.tzdatetime(2032, 5, 3, 22, 0),  # does not fit
         end=self.tzdatetime(2032, 5, 3, 22, 30),
     )
     self.assertFalse(entry.is_fitting_working_hours())  # should not throw anything
Exemple #5
0
 def test_cant_save_due_to_overlap(self):
     overlapping_entry = TimelineEntry(
         teacher=self.teacher,
         lesson=self.lesson,
         start=self.tzdatetime(2016, 1, 3, 4, 0),
         end=self.tzdatetime(2016, 1, 3, 4, 30),
     )
     with self.assertRaises(AutoScheduleExpcetion):  # should conflict with self.big_entry
         overlapping_entry.clean()
Exemple #6
0
 def _create_entry(self, clean):
     entry = TimelineEntry(
         slots=1,
         lesson=self.lesson,
         teacher=self.host,
         start=self.tzdatetime(2032, 9, 13, 12, 0),
     )
     self.assertFalse(entry.is_finished)
     return entry
Exemple #7
0
    def test_filter_by_lesson_type(self):
        first_master_class = mixer.blend(lessons.MasterClass,
                                         host=self.first_teacher)
        second_master_class = mixer.blend(lessons.MasterClass,
                                          host=self.second_teacher)
        entry = TimelineEntry(
            teacher=self.first_teacher,
            lesson=first_master_class,
            start=datetime(2032, 5, 6, 14, 10),
            end=datetime(2032, 5, 6, 14, 40),
        )
        entry.save()
        entry = TimelineEntry(
            teacher=self.second_teacher,
            lesson=second_master_class,
            start=datetime(2032, 5, 6, 14, 15),
            end=datetime(2032, 5, 6, 14, 45),
        )
        entry.save()
        master_class_type = ContentType.objects.get_for_model(
            first_master_class)

        response = self.c.get('/market/2032-05-06/type/%d/lessons.json' %
                              master_class_type.pk)
        self.assertEquals(response.status_code, 200)

        records = json.loads(response.content.decode('utf-8'))
        self.assertEquals(len(records), 2)
        self.assertEquals(len(records[0]['slots']), 1)

        self.assertIsTime(
            records[0]['slots'][0]['server']
        )  # assert that returned slots carry some time (we dont care about timezones here)
        self.assertIsTime(records[1]['slots'][0]['server'])

        self.assertEquals(records[0]['name'], first_master_class.name)
        self.assertEquals(records[1]['name'], second_master_class.name)

        self.assertEquals(records[0]['host'],
                          self.first_teacher.user.crm.full_name)
        self.assertEquals(records[1]['host'],
                          self.second_teacher.user.crm.full_name)
Exemple #8
0
 def test_no_validation_when_more_then_one_student_has_signed(self):
     """
     There is no need to validate a timeline entry when it has students
     """
     overlapping_entry = TimelineEntry(
         teacher=self.teacher,
         lesson=self.lesson,
         start=self.tzdatetime(2016, 1, 3, 4, 0),
         end=self.tzdatetime(2016, 1, 3, 4, 30),
         taken_slots=1,
     )
     overlapping_entry.clean()  # should not throw anything
     self.assertTrue(True)
Exemple #9
0
 def test_cant_save_due_to_not_fitting_working_hours(self):
     """
     Create an entry that does not fit into teachers working hours
     """
     entry = TimelineEntry(
         teacher=self.teacher,
         lesson=self.lesson,
         start=self.tzdatetime(2032, 5, 3, 13, 30),  # monday
         end=self.tzdatetime(2032, 5, 3, 14, 0),
         allow_besides_working_hours=False
     )
     with self.assertRaises(DoesNotFitWorkingHours, msg='Entry does not fit teachers working hours'):
         entry.clean()
Exemple #10
0
    def test_free_slots_for_lesson_type_validates_with_auto_schedule(self):
        master_class = mixer.blend(lessons.MasterClass, host=self.teacher)
        entry = TimelineEntry(teacher=self.teacher,
                              lesson=master_class,
                              start=self.tzdatetime(2032, 5, 3, 14, 10),
                              end=self.tzdatetime(2032, 5, 3, 14, 40))
        entry.save()
        lesson_type = ContentType.objects.get_for_model(master_class)
        with patch('timeline.models.Entry.clean') as clean:
            clean.side_effect = AutoScheduleExpcetion(message='testing')

            slots = self.teacher.find_free_slots(date=self.tzdatetime(
                2032, 5, 3),
                                                 lesson_type=lesson_type.pk)
            self.assertEqual(len(slots), 0)
Exemple #11
0
 def test_get_teachers_by_lesson(self):
     """
     Find teachers for a particular lesson
     """
     first_master_class = mixer.blend(lessons.MasterClass,
                                      host=self.teacher)
     first_entry = TimelineEntry(teacher=self.teacher,
                                 lesson=first_master_class,
                                 start=self.tzdatetime(2032, 5, 3, 14, 10),
                                 end=self.tzdatetime(2032, 5, 3, 14, 40))
     first_entry.save()
     free_teachers = list(
         Teacher.objects.find_free(date=self.tzdatetime(2032, 5, 3),
                                   lesson_id=first_master_class.pk))
     self.assertEquals(len(free_teachers), 1)
Exemple #12
0
 def test_cant_save_due_to_teacher_has_events(self):
     entry = TimelineEntry(
         teacher=self.teacher,
         lesson=self.lesson,
         start=self.tzdatetime(2016, 5, 3, 13, 30),
         end=self.tzdatetime(2016, 5, 3, 14, 00),
     )
     mixer.blend(
         ExternalEvent,
         teacher=self.teacher,
         start=self.tzdatetime(2016, 5, 2, 00, 00),
         end=self.tzdatetime(2016, 5, 5, 23, 59),
     )
     with self.assertRaises(AutoScheduleExpcetion):
         entry.clean()
Exemple #13
0
 def test_cant_save_due_to_teacher_absence(self):
     entry = TimelineEntry(
         teacher=self.teacher,
         lesson=self.lesson,
         start=self.tzdatetime(2016, 5, 3, 13, 30),
         end=self.tzdatetime(2016, 5, 3, 14, 00),
     )
     vacation = Absence(
         type='vacation',
         teacher=self.teacher,
         start=self.tzdatetime(2016, 5, 2, 00, 00),
         end=self.tzdatetime(2016, 5, 5, 23, 59),
     )
     vacation.save()
     with self.assertRaises(AutoScheduleExpcetion):
         entry.clean()
Exemple #14
0
    def test_two_teachers_for_single_slot(self):
        """
        Check if find_free_slots returns only slots of selected teacher
        """
        other_teacher = create_teacher()
        master_class = mixer.blend(lessons.MasterClass, host=other_teacher)
        entry = TimelineEntry(teacher=other_teacher,
                              lesson=master_class,
                              start=self.tzdatetime(2032, 5, 3, 14, 10),
                              end=self.tzdatetime(2032, 5, 3, 14, 40))
        entry.save()
        lesson_type = ContentType.objects.get_for_model(master_class)

        slots = self.teacher.find_free_slots(date=self.tzdatetime(2032, 5, 3),
                                             lesson_type=lesson_type.pk)
        self.assertEquals(
            len(slots), 0
        )  # should not return anything — we are checking slots for self.teacher, not other_teacher
Exemple #15
0
    def test_free_slots_for_lesson_type(self):
        """
        Test for getting free time slots for a certain lesson type.
        """
        master_class = mixer.blend(lessons.MasterClass, host=self.teacher)
        entry = TimelineEntry(teacher=self.teacher,
                              lesson=master_class,
                              start=self.tzdatetime(2032, 5, 3, 14, 10),
                              end=self.tzdatetime(2032, 5, 3, 14, 40))
        entry.save()
        lesson_type = ContentType.objects.get_for_model(master_class)

        slots = self.teacher.find_free_slots(date=self.tzdatetime(2032, 5, 3),
                                             lesson_type=lesson_type.pk)
        self.assertEquals(len(slots), 1)

        slots = self.teacher.find_free_slots(date=self.tzdatetime(2032, 5, 5),
                                             lesson_type=lesson_type.pk)
        self.assertEquals(
            len(slots), 0)  # there is no master classes, planned on 2032-05-05
Exemple #16
0
 def test_schedule_existsing_entry(self):
     """
     Create a timeline entry, that class.__get_entry should return instead of
     creating a new one
     """
     lesson = products.OrdinaryLesson.get_default()
     c = self._buy_a_lesson(lesson)
     date = self.tzdatetime(2016, 8, 17, 10, 1)
     entry = TimelineEntry(
         teacher=self.host,
         start=date,
         lesson=lesson
     )
     entry.save()
     c.schedule(
         teacher=self.host,
         date=self.tzdatetime(2016, 8, 17, 10, 1),
         allow_besides_working_hours=True,
     )
     c.save()
     self.assertEquals(c.timeline, entry)
Exemple #17
0
    def setUp(self):
        self.teacher = create_teacher()
        self.lesson = mixer.blend(lessons.MasterClass, host=self.teacher)
        self.entry = TimelineEntry(
            teacher=self.teacher,
            lesson=self.lesson,
            start=self.tzdatetime('Europe/Moscow', 2016, 1, 18, 14, 10),
            end=self.tzdatetime('Europe/Moscow', 2016, 1, 18, 14, 40),
        )

        self.entry.save()

        mixer.blend(WorkingHours, teacher=self.teacher, weekday=0, start='13:00', end='15:00')

        self.absence = Absence(
            type='vacation',
            teacher=self.teacher,
            start=self.tzdatetime(2032, 5, 3, 0, 0),
            end=self.tzdatetime(2032, 5, 3, 23, 59),
        )
        self.absence.save()
class TestRevertChanges(LoggedInTestCase, ModuleTestCase):
    def setUp(self):
        super(TestRevertChanges, self).setUp()
        self.master = ModuleTeaching(
            **{
                "module": self.module,
                "teaching_lectures": 45,
                "teaching_tutorials": 3,
                "teaching_online": 45,
                "teaching_practical_workshops": 8,
                "teaching_supervised_time": 8,
                "teaching_fieldworks": 8,
                "teaching_external_visits": 8,
                "teaching_schedule_assessment": 8,
                "teaching_placement": 8,
                "archive_flag": False,
                "staging_flag": True,
                "current_flag": False,
                "version_number": 1,
                "copy_number": 3
            })
        self.master.save()

        self.prev = ModuleTeaching(
            **{
                "module": self.module,
                "teaching_lectures": 8,
                "teaching_tutorials": 8,
                "teaching_online": 8,
                "teaching_practical_workshops": 8,
                "teaching_supervised_time": 8,
                "teaching_fieldworks": 8,
                "teaching_external_visits": 8,
                "teaching_schedule_assessment": 8,
                "teaching_placement": 8,
                "archive_flag": True,
                "staging_flag": False,
                "current_flag": False,
                "version_number": 2,
                "copy_number": 1
            })
        self.prev.save()

        self.current = ModuleTeaching(
            **{
                "module": self.module,
                "teaching_lectures": 45,
                "teaching_tutorials": 84,
                "teaching_online": 8,
                "teaching_practical_workshops": 8,
                "teaching_supervised_time": 8,
                "teaching_fieldworks": 8,
                "teaching_external_visits": 8,
                "teaching_schedule_assessment": 8,
                "teaching_placement": 8,
                "archive_flag": True,
                "staging_flag": False,
                "current_flag": False,
                "version_number": 3,
                "copy_number": 2
            })

        self.current.save()

        self.parent_entry = TimelineEntry(
            **{
                "title": "Changes to tracking form",
                "changes":
                "Changes to tracking form:\n\n* There are 1 changes to Module Teaching\n* There are 2 changes to Assessment test4\n* There are 1 changes to Software tet\n",
                "created": "2018-04-11T16:58:40.999Z",
                "last_modified": "2018-04-11T16:58:40.999Z",
                "module_code": self.module.module_code,
                "parent_entry": None,
                "status": "Draft",
                "entry_type": "Tracking-Form",
                "content_type": None,
                "object_id": None,
                "revert_object_id": "0",
                "changes_by": self.admin,
                "approved_by": None
            })
        self.parent_entry.save()

        self.entry = TimelineEntry(
            **{
                "title": "Module Teaching",
                "changes": "* teaching online: 8 -> 45\n",
                "created": "2018-04-11T16:58:41.019Z",
                "last_modified": "2018-04-11T16:58:41.019Z",
                "module_code": self.module.module_code,
                "parent_entry": self.parent_entry,
                "status": "Draft",
                "entry_type": "Tracking-Form",
                "content_object": self.current,
                "revert_object_id": self.prev.pk,
                "changes_by": None,
                "approved_by": None
            })
        self.entry.save()

    def test_valid_revert_changes(self):
        """
        Test that the changes are reverted for tracking form data
        """
        # test the current staged changes
        master = ModuleTeaching.objects.filter(version_number=1).first()
        self.assertTrue(master.staging_flag)
        self.assertFalse(master.archive_flag)
        self.assertFalse(master.current_flag)
        self.assertEquals(master.copy_number, 3)

        result = revert_changes(self.parent_entry)
        self.assertTrue(result)

        # check timeline has been removed
        with self.assertRaises(TimelineEntry.DoesNotExist):
            TimelineEntry.objects.get(pk=self.parent_entry.pk)

        # check that it has been reverted
        master = ModuleTeaching.objects.filter(version_number=1).first()
        self.assertTrue(master.current_flag)
        self.assertFalse(master.archive_flag)
        self.assertFalse(master.staging_flag)
        self.assertEquals(master.copy_number, 2)

    def test_no_children_master(self):
        """
        Test when there are no child for parent entry
        """
        result = revert_changes(self.entry)
        self.assertFalse(result)

    def test_invalid_master_instance(self):
        """
        Test when the parent entry is not a timeline entry type
        """
        parents = [None, ModuleTeaching, self.user]

        for parent in parents:
            with self.assertRaises(ValueError):
                revert_changes(parent)
    def setUp(self):
        super(TestRevertChanges, self).setUp()
        self.master = ModuleTeaching(
            **{
                "module": self.module,
                "teaching_lectures": 45,
                "teaching_tutorials": 3,
                "teaching_online": 45,
                "teaching_practical_workshops": 8,
                "teaching_supervised_time": 8,
                "teaching_fieldworks": 8,
                "teaching_external_visits": 8,
                "teaching_schedule_assessment": 8,
                "teaching_placement": 8,
                "archive_flag": False,
                "staging_flag": True,
                "current_flag": False,
                "version_number": 1,
                "copy_number": 3
            })
        self.master.save()

        self.prev = ModuleTeaching(
            **{
                "module": self.module,
                "teaching_lectures": 8,
                "teaching_tutorials": 8,
                "teaching_online": 8,
                "teaching_practical_workshops": 8,
                "teaching_supervised_time": 8,
                "teaching_fieldworks": 8,
                "teaching_external_visits": 8,
                "teaching_schedule_assessment": 8,
                "teaching_placement": 8,
                "archive_flag": True,
                "staging_flag": False,
                "current_flag": False,
                "version_number": 2,
                "copy_number": 1
            })
        self.prev.save()

        self.current = ModuleTeaching(
            **{
                "module": self.module,
                "teaching_lectures": 45,
                "teaching_tutorials": 84,
                "teaching_online": 8,
                "teaching_practical_workshops": 8,
                "teaching_supervised_time": 8,
                "teaching_fieldworks": 8,
                "teaching_external_visits": 8,
                "teaching_schedule_assessment": 8,
                "teaching_placement": 8,
                "archive_flag": True,
                "staging_flag": False,
                "current_flag": False,
                "version_number": 3,
                "copy_number": 2
            })

        self.current.save()

        self.parent_entry = TimelineEntry(
            **{
                "title": "Changes to tracking form",
                "changes":
                "Changes to tracking form:\n\n* There are 1 changes to Module Teaching\n* There are 2 changes to Assessment test4\n* There are 1 changes to Software tet\n",
                "created": "2018-04-11T16:58:40.999Z",
                "last_modified": "2018-04-11T16:58:40.999Z",
                "module_code": self.module.module_code,
                "parent_entry": None,
                "status": "Draft",
                "entry_type": "Tracking-Form",
                "content_type": None,
                "object_id": None,
                "revert_object_id": "0",
                "changes_by": self.admin,
                "approved_by": None
            })
        self.parent_entry.save()

        self.entry = TimelineEntry(
            **{
                "title": "Module Teaching",
                "changes": "* teaching online: 8 -> 45\n",
                "created": "2018-04-11T16:58:41.019Z",
                "last_modified": "2018-04-11T16:58:41.019Z",
                "module_code": self.module.module_code,
                "parent_entry": self.parent_entry,
                "status": "Draft",
                "entry_type": "Tracking-Form",
                "content_object": self.current,
                "revert_object_id": self.prev.pk,
                "changes_by": None,
                "approved_by": None
            })
        self.entry.save()