예제 #1
0
 def get_feature_flow(self, request, slug):
     """
     Overrides :obj:`FeatureFlowView` *get_feature_flow* method. Takes the slug,
     decrypts the hashed database id, and either retrieves the corresponding
     timetable or hits a 404.
     """
     timetable_id = hashids.decrypt(slug)[0]
     shared_timetable = get_object_or_404(SharedTimetable,
                                          id=timetable_id,
                                          school=request.subdomain)
     context = {
         "semester": shared_timetable.semester,
         "school": request.subdomain,
         "student": get_student(request),
     }
     return {
         "semester":
         shared_timetable.semester,
         "courses":
         CourseSerializer(shared_timetable.courses,
                          context=context,
                          many=True).data,
         "sharedTimetable":
         DisplayTimetableSerializer.from_model(shared_timetable).data,
     }
예제 #2
0
def get_student_dict(school, student, semester):
    """ Return serialized representation of a student. """
    user_dict = {
        'timeAcceptedTos': None,
        'isLoggedIn': False,
        'timetables': [],
        'courses': []
    }
    if student is not None:
        user_dict = dict(user_dict, **StudentSerializer(student).data)
        user_dict['isLoggedIn'] = True

        timetables = student.personaltimetable_set.filter(
            school=school, semester=semester).order_by('-last_updated')
        courses = {
            course
            for timetable in timetables for course in timetable.courses.all()
        }
        context = {'semester': semester, 'school': school, 'student': student}
        user_dict['timetables'] = DisplayTimetableSerializer.from_model(
            timetables, many=True).data
        user_dict['courses'] = CourseSerializer(courses,
                                                context=context,
                                                many=True).data
    return user_dict
예제 #3
0
    def create_or_update_timetable(self, request):
        school = request.subdomain
        has_conflict = request.data["has_conflict"]
        name = request.data["name"]
        semester, _ = Semester.objects.get_or_create(
            **request.data["semester"])
        student = Student.objects.get(user=request.user)
        params = {
            "school": school,
            "name": name,
            "semester": semester,
            "student": student,
        }
        tt_id = request.data.get("id")  # id is None if this is a new timetable
        if PersonalTimetable.objects.filter(~Q(id=tt_id), **params):
            return Response(status=status.HTTP_409_CONFLICT)

        personal_timetable = (PersonalTimetable.objects.create(
            **params) if tt_id is None else PersonalTimetable.objects.get(
                id=tt_id))
        slots = request.data["slots"]
        self.update_tt(personal_timetable, name, has_conflict, slots)
        self.update_events(personal_timetable, request.data["events"])

        response = {
            "timetables":
            get_student_tts(student, school, semester),
            "saved_timetable":
            DisplayTimetableSerializer.from_model(personal_timetable).data,
        }
        response_status = (status.HTTP_201_CREATED
                           if tt_id is None else status.HTTP_200_OK)
        return Response(response, status=response_status)
예제 #4
0
 def get(self, request, sem_name, year):
     """Returns student's personal timetables"""
     sem, _ = Semester.objects.get_or_create(name=sem_name, year=year)
     student = Student.objects.get(user=request.user)
     timetables = student.personaltimetable_set.filter(
         school=request.subdomain, semester=sem).order_by("-last_updated")
     courses = {
         course
         for timetable in timetables for course in timetable.courses.all()
     }
     context = {
         "semester": sem,
         "school": request.subdomain,
         "student": student
     }
     return Response(
         {
             "timetables":
             DisplayTimetableSerializer.from_model(timetables,
                                                   many=True).data,
             "courses":
             CourseSerializer(courses, context=context, many=True).data,
         },
         status=status.HTTP_200_OK,
     )
예제 #5
0
 def create_response(self, courses, locked_sections, timetables, context):
     return {
         "timetables": DisplayTimetableSerializer(timetables,
                                                  many=True).data,
         "new_c_to_s": locked_sections,
         "courses": CourseSerializer(courses, context=context,
                                     many=True).data,
     }
예제 #6
0
def get_student_tts(student, school, semester):
    """
    Returns serialized list of a student's :obj:`PersonalTimetable` objects
    ordered by last updated for passing to the frontend.
    """
    timetables = student.personaltimetable_set.filter(
        school=school, semester=semester).order_by('-last_updated')
    return DisplayTimetableSerializer.from_model(timetables, many=True).data
예제 #7
0
def get_student_tts(student, school, semester):
    """
    Returns serialized list of a student's :obj:`PersonalTimetable` objects
    ordered by last updated for passing to the frontend.
    """
    timetables = student.personaltimetable_set.filter(
        school=school, semester=semester).order_by('-last_updated')
    return DisplayTimetableSerializer.from_model(timetables, many=True).data
예제 #8
0
 def get(self, request, sem_name, year):
     """ Returns student's personal timetables """
     sem, _ = Semester.objects.get_or_create(name=sem_name, year=year)
     student = Student.objects.get(user=request.user)
     timetables = student.personaltimetable_set.filter(
         school=request.subdomain, semester=sem).order_by('-last_updated')
     courses = {course for timetable in timetables for course in timetable.courses.all()}
     context = {'semester': sem, 'school': request.subdomain, 'student': student}
     return Response({
         'timetables': DisplayTimetableSerializer.from_model(timetables, many=True).data,
         'courses': CourseSerializer(courses, context=context, many=True).data
     }, status=status.HTTP_200_OK)
예제 #9
0
def get_student_dict(school, student, semester):
    """ Return serialized representation of a student. """
    user_dict = {'timeAcceptedTos': None, 'isLoggedIn': False, 'timetables': [], 'courses': []}
    if student is not None:
        user_dict = dict(user_dict, **StudentSerializer(student).data)
        user_dict['isLoggedIn'] = True

        timetables = student.personaltimetable_set.filter(
            school=school, semester=semester).order_by('-last_updated')
        courses = {course for timetable in timetables for course in timetable.courses.all()}
        context = {'semester': semester, 'school': school, 'student': student}
        user_dict['timetables'] = DisplayTimetableSerializer.from_model(timetables, many=True).data
        user_dict['courses'] = CourseSerializer(courses, context=context, many=True).data
    return user_dict
예제 #10
0
    def test_shared_timetable_serialization(self):
        timetable = SharedTimetable.objects.create(semester=self.sem, school='uoft',
                                                   has_conflict=False)
        timetable.courses.add(self.course)
        timetable.sections.add(self.section)

        display = DisplayTimetable.from_model(timetable)
        self.assertEqual(len(display.slots), 1)
        self.assertIsInstance(display.slots[0].course, Course)

        serialized = DisplayTimetableSerializer(display).data
        self.assertEqual(len(serialized['slots']), 1)
        self.assertIsInstance(serialized['slots'][0]['course'], int)
        self.assertEqual(len(serialized['events']), 0)
예제 #11
0
    def test_personal_timetable_serialization(self):
        timetable = PersonalTimetable.objects.create(semester=self.sem,
                                                     school="uoft",
                                                     has_conflict=False,
                                                     student=self.student)
        timetable.courses.add(self.course)
        timetable.sections.add(self.section)
        timetable.events.add(self.event)

        display = DisplayTimetable.from_model(timetable)
        self.assertEqual(len(display.slots), 1)
        self.assertIsInstance(display.slots[0].course, Course)

        serialized = DisplayTimetableSerializer(display).data
        self.assertEqual(len(serialized["slots"]), 1)
        self.assertIsInstance(serialized["slots"][0]["course"], int)
        self.assertEqual(len(serialized["events"]), 1)
예제 #12
0
 def get_feature_flow(self, request, slug):
     """
     Overrides :obj:`FeatureFlowView` *get_feature_flow* method. Takes the slug,
     decrypts the hashed database id, and either retrieves the corresponding
     timetable or hits a 404.
     """
     timetable_id = hashids.decrypt(slug)[0]
     shared_timetable = get_object_or_404(SharedTimetable,
                                          id=timetable_id,
                                          school=request.subdomain)
     context = {'semester': shared_timetable.semester, 'school': request.subdomain,
                'student': get_student(request)}
     return {
         'semester': shared_timetable.semester,
         'courses': CourseSerializer(shared_timetable.courses, context=context, many=True).data,
         'sharedTimetable': DisplayTimetableSerializer.from_model(shared_timetable).data
     }
예제 #13
0
 def duplicate_timetable(self, request):
     school = request.subdomain
     name = request.data["source"]
     semester = Semester.objects.get(**request.data["semester"])
     student = Student.objects.get(user=request.user)
     new_name = request.data["name"]
     duplicate = get_object_or_404(
         PersonalTimetable,
         student=student,
         name=name,
         school=school,
         semester=semester,
     )
     courses, sections, events = self.get_duplicate_m2m_fields(duplicate)
     self.create_duplicated_timetable(new_name, duplicate, courses,
                                      sections, events)
     response = {
         "timetables":
         get_student_tts(student, school, semester),
         "saved_timetable":
         DisplayTimetableSerializer.from_model(duplicate).data,
     }
     return Response(response, status=status.HTTP_201_CREATED)
예제 #14
0
    def post(self, request):
        """
        Duplicates a personal timetable if a 'source' is provided. Else, creates
        a personal timetable based on the courses, custom events, preferences, etc.
        which are provided.
        """
        if 'source' in request.data:  # duplicate existing timetable
            school = request.subdomain
            name = request.data['source']
            semester = Semester.objects.get(**request.data['semester'])
            student = Student.objects.get(user=request.user)
            new_name = request.data['name']

            duplicate = get_object_or_404(PersonalTimetable, student=student, name=name,
                                          school=school, semester=semester)
            # save manytomany relationships before copying
            courses, sections = duplicate.courses.all(), duplicate.sections.all()
            events = duplicate.events.all()
            for event in events:  # create duplicates of each event to allow for safe delete
                event.pk = None
                event.save()

            duplicate.pk = None  # creates duplicate of object
            duplicate.name = new_name
            duplicate.save()
            duplicate.courses = courses
            duplicate.sections = sections
            duplicate.events = events

            response = {
                'timetables': get_student_tts(student, school, semester),
                'saved_timetable': DisplayTimetableSerializer.from_model(duplicate).data
            }
            return Response(response, status=status.HTTP_201_CREATED)
        else:
            school = request.subdomain
            has_conflict = request.data['has_conflict']
            name = request.data['name']
            semester, _ = Semester.objects.get_or_create(**request.data['semester'])
            student = Student.objects.get(user=request.user)
            params = {
                'school': school,
                'name': name,
                'semester': semester,
                'student': student
            }

            slots = request.data['slots']
            # id is None if this is a new timetable
            tt_id = request.data.get('id')

            if PersonalTimetable.objects.filter(~Q(id=tt_id), **params):
                return Response(status=status.HTTP_409_CONFLICT)

            personal_timetable = PersonalTimetable.objects.create(**params) if tt_id is None else \
                PersonalTimetable.objects.get(id=tt_id)
            self.update_tt(personal_timetable, name, has_conflict, slots)
            self.update_events(personal_timetable, request.data['events'])

            response = {
                'timetables': get_student_tts(student, school, semester),
                'saved_timetable': DisplayTimetableSerializer.from_model(personal_timetable).data
            }
            response_status = status.HTTP_201_CREATED if tt_id is None else status.HTTP_200_OK
            return Response(response, status=response_status)
예제 #15
0
    def post(self, request):
        """Generate best timetables given the user's selected courses"""
        school = request.subdomain
        params = request.data
        student = get_student(request)

        try:
            params['semester'] = Semester.objects.get_or_create(**params['semester'])[0]
        except TypeError: # handle deprecated cached semesters from frontend
            params['semester'] = Semester.objects.get(name="Fall", year="2016") \
                if params['semester'] == "F" \
                else Semester.objects.get(name="Spring", year="2017")

        course_ids = params['courseSections'].keys()
        courses = [Course.objects.get(id=cid) for cid in course_ids]
        locked_sections = params['courseSections']

        save_analytics_timetable(courses, params['semester'], school, get_student(request))

        for updated_course in params.get('updated_courses', []):
            cid = str(updated_course['course_id'])
            locked_sections[cid] = locked_sections.get(cid, {})
            if cid not in course_ids:
                courses.append(Course.objects.get(id=int(cid)))

            for locked_section in filter(bool, updated_course['section_codes']):
                update_locked_sections(locked_sections, cid, locked_section, params['semester'])

        # temp optional course implementation
        opt_course_ids = params.get('optionCourses', [])
        max_optional = params.get('numOptionCourses', len(opt_course_ids))
        optional_courses = [Course.objects.get(id=cid) for cid in opt_course_ids]
        optional_course_subsets = [subset for subset_size in range(max_optional, -1, -1)
                                   for subset in itertools.combinations(optional_courses,
                                                                        subset_size)]

        custom_events = params.get('customSlots', [])
        preferences = params['preferences']
        with_conflicts = preferences.get('try_with_conflicts', False)
        sort_metrics = [(m['metric'], m['order']) for m in preferences.get('sort_metrics', [])
                        if m['selected']]

        # TODO move sorting to view level so that result is sorted
        timetables = [timetable for opt_courses in optional_course_subsets
                                for timetable in courses_to_timetables(courses + list(opt_courses),
                                                                       locked_sections,
                                                                       params['semester'],
                                                                       sort_metrics,
                                                                       params['school'],
                                                                       custom_events,
                                                                       with_conflicts,
                                                                       opt_course_ids)]

        context = {'semester': params['semester'], 'school': request.subdomain, 'student': student}
        courses = [course for course in courses + optional_courses]
        response = {
            'timetables': DisplayTimetableSerializer(timetables, many=True).data,
            'new_c_to_s': locked_sections,
            'courses': CourseSerializer(courses, context=context, many=True).data
        }
        return Response(response, status=status.HTTP_200_OK)
예제 #16
0
    def post(self, request):
        """
        Duplicates a personal timetable if a 'source' is provided. Else, creates
        a personal timetable based on the courses, custom events, preferences, etc.
        which are provided.
        """
        if 'source' in request.data:  # duplicate existing timetable
            school = request.subdomain
            name = request.data['source']
            semester = Semester.objects.get(**request.data['semester'])
            student = Student.objects.get(user=request.user)
            new_name = request.data['name']

            duplicate = get_object_or_404(PersonalTimetable, student=student, name=name,
                                          school=school, semester=semester)
            # save manytomany relationships before copying
            courses, sections = duplicate.courses.all(), duplicate.sections.all()
            events = duplicate.events.all()
            for event in events:  # create duplicates of each event to allow for safe delete
                event.pk = None
                event.save()

            duplicate.pk = None  # creates duplicate of object
            duplicate.name = new_name
            duplicate.save()
            duplicate.courses = courses
            duplicate.sections = sections
            duplicate.events = events

            response = {
                'timetables': get_student_tts(student, school, semester),
                'saved_timetable': DisplayTimetableSerializer.from_model(duplicate).data
            }
            return Response(response, status=status.HTTP_201_CREATED)
        else:
            school = request.subdomain
            has_conflict = request.data['has_conflict']
            name = request.data['name']
            semester, _ = Semester.objects.get_or_create(**request.data['semester'])
            student = Student.objects.get(user=request.user)
            params = {
                'school': school,
                'name': name,
                'semester': semester,
                'student': student
            }

            slots = request.data['slots']
            # id is None if this is a new timetable
            tt_id = request.data.get('id')

            if PersonalTimetable.objects.filter(~Q(id=tt_id), **params):
                return Response(status=status.HTTP_409_CONFLICT)

            personal_timetable = PersonalTimetable.objects.create(**params) if tt_id is None else \
                PersonalTimetable.objects.get(id=tt_id)
            self.update_tt(personal_timetable, name, has_conflict, slots)
            self.update_events(personal_timetable, request.data['events'])

            response = {
                'timetables': get_student_tts(student, school, semester),
                'saved_timetable': DisplayTimetableSerializer.from_model(personal_timetable).data
            }
            response_status = status.HTTP_201_CREATED if tt_id is None else status.HTTP_200_OK
            return Response(response, status=response_status)