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, }
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
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)
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, )
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, }
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
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)
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
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)
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)
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 }
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)
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)
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)