Source code for courses.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 collections
import json
from datetime import datetime

from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.template import RequestContext
from pytz import timezone
from regex import E
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from analytics.models import SharedCourseView
from courses.serializers import CourseSerializer
from student.models import Student
from student.utils import get_classmates_from_course_id
from timetable.models import Semester, Course
from timetable.school_mappers import SCHOOLS_MAP
from helpers.mixins import ValidateSubdomainMixin, FeatureFlowView
from helpers.decorators import validate_subdomain
from parsing.models import DataUpdate


[docs]@validate_subdomain def all_courses(request): """ Generates the full course directory page. Includes links to all courses and is sorted by department. """ school = request.subdomain school_name = SCHOOLS_MAP[school].name dep_to_courses = collections.OrderedDict() departments = ( Course.objects.filter(school=school) .order_by("department") .values_list("department", flat=True) .distinct() ) for department in departments: dep_to_courses[department] = Course.objects.filter( school=school, department=department ).all() context = { "course_map": dep_to_courses, "school": school, "school_name": school_name, } return render(request, "all_courses.html", context)
[docs]def get_classmates_in_course(request, school, sem_name, year, course_id): """ Finds all classmates for the authenticated user who also have a timetable with the given course. """ school = school.lower() sem, _ = Semester.objects.get_or_create(name=sem_name, year=year) json_data = {"current": [], "past": []} course = Course.objects.get(school=school, id=course_id) student = None is_logged_in = request.user.is_authenticated if is_logged_in and Student.objects.filter(user=request.user).exists(): student = Student.objects.get(user=request.user) if student and student.user.is_authenticated and student.social_courses: json_data = get_classmates_from_course_id(school, student, course.id, sem) return HttpResponse(json.dumps(json_data), content_type="application/json")
[docs]@validate_subdomain def course_page(request, code): """ Generates a static course page for the provided course code and school (via subdomain). Completely outside of the React framework purely via Django templates. """ school = request.subdomain school_name = SCHOOLS_MAP[school].name course = Course.objects.get(code__iexact=code) semester, _ = Semester.objects.get_or_create(name="Fall", year=datetime.now().year) course_dict = CourseSerializer( course, context={"semester": semester, "school": school} ).data sections = course_dict["sections"] lectures = [s for s in sections if s["section_type"] == "L"] tutorials = [s for s in sections if s["section_type"] == "T"] practicals = [s for s in sections if s["section_type"] == "P"] avg = round(course.get_avg_rating(), 2) clean_evals = get_clean_evals(course_dict) course_url = f"/course/{course_dict['code']}/{semester.name}/{semester.year}" context = { "school": school, "school_name": school_name, "course": course_dict, "lectures": lectures, "tutorials": tutorials, "practicals": practicals, "url": course_url, "evals": clean_evals, "avg": avg, } return render(request, "course_page.html", context)
def get_clean_evals(course_dict): evals = course_dict["evals"] for i, v in enumerate(evals): for k in v: if isinstance(evals[i][k], str): evals[i][k] = evals[i][k].replace("\xa0", " ") if k == "year": evals[i][k] = evals[i][k].replace(":", " ") return evals
[docs]class CourseModal(FeatureFlowView): """ A :obj:`FeatureFlowView` for loading a course share link which directly opens the course modal on the frontend. Therefore, this view overrides the *get_feature_flow* method to fill intData with the detailed course json for the modal.abs Saves a :obj:`SharedCourseView` for analytics purposes. """ feature_name = "SHARE_COURSE"
[docs] def get_feature_flow(self, request, code, sem_name, year): semester, _ = Semester.objects.get_or_create(name=sem_name, year=year) code = str(code).upper() course = get_object_or_404(Course, school=self.school, code=code) course_json = CourseSerializer( course, context={ "semester": semester, "school": self.school, "student": self.student, }, ) # analytics SharedCourseView.objects.create( student=self.student, shared_course=course, ).save() return {"sharedCourse": course_json.data, "semester": semester}
[docs]class CourseDetail(ValidateSubdomainMixin, APIView): """View that handles individual course entities."""
[docs] def get(self, request, sem_name, year, course_id): """Return detailed data about a single course. Currently used for course modals.""" school = request.subdomain sem, _ = Semester.objects.get_or_create(name=sem_name, year=year) course = get_object_or_404(Course, school=school, id=course_id) student = None is_logged_in = request.user.is_authenticated if is_logged_in and Student.objects.filter(user=request.user).exists(): student = Student.objects.get(user=request.user) json_data = CourseSerializer( course, context={"semester": sem, "student": student, "school": request.subdomain}, ) return Response(json_data.data, status=status.HTTP_200_OK)
[docs]class SchoolList(APIView):
[docs] def get(self, request, school): """ Provides the basic school information including the schools areas, departments, levels, and the time the data was last updated """ last_updated = ( DataUpdate.objects.filter(school=school, update_type=DataUpdate.COURSES) .order_by("timestamp") .last() ) if last_updated is not None: last_updated = "{} {}".format( last_updated.timestamp.strftime("%Y-%m-%d %H:%M"), last_updated.timestamp.tzname(), ) json_data = { "areas": get_distinct_areas( sorted( list( Course.objects.filter(school=school) .exclude(areas__exact=[]) .values_list("areas", flat=True) .distinct() ) ) ), "departments": sorted( list( Course.objects.filter(school=school) .exclude(department__exact="") .values_list("department", flat=True) .distinct() ) ), "levels": sorted( list( Course.objects.filter(school=school) .exclude(level__exact="") .values_list("level", flat=True) .distinct() ) ), "last_updated": last_updated, } return Response(json_data, status=status.HTTP_200_OK)
def get_distinct_areas(areas_group): distinct_areas = set() for group in areas_group: if group != list("None"): for area in group: distinct_areas.add(area) return distinct_areas