Source code for searches.views

# Copyright (C) 2017 Semester.ly Technologies, LLC
#
# Semester.ly is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Semester.ly is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

import operator

from django.core.paginator import Paginator
from django.db.models import Q

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from analytics.views import save_analytics_course_search
from courses.serializers import CourseSerializer
from searches.utils import search
from student.utils import get_student
from timetable.models import Semester
from helpers.mixins import ValidateSubdomainMixin, CsrfExemptMixin
from functools import reduce


[docs]class CourseSearchList(CsrfExemptMixin, ValidateSubdomainMixin, APIView): """Course Search List."""
[docs] def get(self, request, query, sem_name, year): """Return search results.""" school = request.subdomain sem = Semester.objects.get_or_create(name=sem_name, year=year)[0] course_matches = search(request.subdomain, query, sem).distinct()[:4] self.save_analytic(request, query, course_matches, sem) course_match_data = [ CourseSerializer(course, context={"semester": sem, "school": school}).data for course in course_matches ] return Response(course_match_data, status=status.HTTP_200_OK)
def save_analytic(self, request, query, course_matches, sem, advanced=False): save_analytics_course_search( query[:200], course_matches[:2], sem, request.subdomain, get_student(request), advanced, )
[docs] def post(self, request, query, sem_name, year): """Return advanced search results.""" school = request.subdomain sem, _ = Semester.objects.get_or_create(name=sem_name, year=year) filters = request.data.get("filters", {}) course_matches = search(school, query, sem) course_matches = self.filter_course_matches(course_matches, filters, sem) course_matches = course_matches.distinct()[:100] # prevent timeout self.save_analytic(request, query, course_matches, sem, True) student = get_student(request) serializer_context = { "semester": sem, "student": student, "school": request.subdomain, } cur_page = int(request.GET.get("page", 1)) courses_per_page = int(request.GET.get("limit", 10)) paginator = Paginator(course_matches, courses_per_page) if cur_page > paginator.num_pages: course_match_data = [] else: paginated_data = paginator.page(cur_page) course_match_data = CourseSerializer( paginated_data, context=serializer_context, many=True ).data return Response( {"data": course_match_data, "page": cur_page}, status=status.HTTP_200_OK )
def filter_course_matches(self, course_matches, filters, sem): course_matches = self.filter_by_areas(course_matches, filters) course_matches = self.filter_by_departments(course_matches, filters) course_matches = self.filter_by_levels(course_matches, filters) course_matches = self.filter_by_times(sem, course_matches, filters) course_matches = course_matches.order_by("id") return course_matches def filter_by_areas(self, course_matches, filters): if filters.get("areas"): course_matches = course_matches.filter(areas__contains=filters.get("areas")) return course_matches def filter_by_departments(self, course_matches, filters): if filters.get("departments"): course_matches = course_matches.filter( department__in=filters.get("departments") ) return course_matches def filter_by_levels(self, course_matches, filters): if filters.get("levels"): course_matches = course_matches.filter(level__in=filters.get("levels")) return course_matches def filter_by_times(self, sem, course_matches, filters): if filters.get("times"): day_map = { "Monday": "M", "Tuesday": "T", "Wednesday": "W", "Thursday": "R", "Friday": "F", "Saturday": "S", "Sunday": "U", } course_matches = course_matches.filter( reduce( operator.or_, ( Q( section__offering__time_start__gte="{0:0=2d}:00".format( min_max["min"] ), section__offering__time_end__lte="{0:0=2d}:00".format( min_max["max"] ), section__offering__day=day_map[min_max["day"]], section__semester=sem, section__section_type="L", ) for min_max in filters.get("times") ), ) ) return course_matches