diff --git a/csm_web/scheduler/urls.py b/csm_web/scheduler/urls.py index b997222f..e7f3f2b0 100644 --- a/csm_web/scheduler/urls.py +++ b/csm_web/scheduler/urls.py @@ -23,4 +23,5 @@ path("matcher//mentors/", views.matcher.mentors), path("matcher//configure/", views.matcher.configure), path("matcher//create/", views.matcher.create), + path("search/sections/", views.get_sections_of_user, name="get_sections_of_user"), ] diff --git a/csm_web/scheduler/views/__init__.py b/csm_web/scheduler/views/__init__.py index 8002052a..31c6cbf5 100644 --- a/csm_web/scheduler/views/__init__.py +++ b/csm_web/scheduler/views/__init__.py @@ -1,7 +1,8 @@ -from .course import CourseViewSet from . import matcher +from .course import CourseViewSet from .profile import ProfileViewSet from .resource import ResourceViewSet +from .search import get_sections_of_user from .section import SectionViewSet from .spacetime import SpacetimeViewSet from .student import StudentViewSet diff --git a/csm_web/scheduler/views/search.py b/csm_web/scheduler/views/search.py new file mode 100644 index 00000000..6bf62f5a --- /dev/null +++ b/csm_web/scheduler/views/search.py @@ -0,0 +1,154 @@ +from django.db.models import Count, Q +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.exceptions import PermissionDenied +from rest_framework.response import Response +from scheduler.models import Section, Student +from scheduler.serializers import SectionSerializer, StudentSerializer + + +@api_view(["GET"]) +def get_sections_of_user(request): + """ + Gets sections and associated students based on query parameters. + + Query Parameters: + - `query`: (optional) Search query to filter sections and students by name or email. + - `student_absences`: (optional) Filter students by num absences (exact int or int range). + + Returns: + - JSON response containing sections matching the query, along with associated student details. + + Raises: + - PermissionDenied: If user lacks permission to search sections. + - HTTP 400 Bad Request: If neither query parameters provided, + or invalid value for `student_absences`. + + Note: + - Only `query` provided: returns sections & students matching the query. + - Only `student_absences` provided: returns sections & students filtered by absences. + - Both `query` and `student_absences` provided: returns sections and students + filtered by both query and absences. + """ + + is_coordinator = bool( + request.user.coordinator_set.filter(user=request.user).count() + ) + if not is_coordinator: + raise PermissionDenied( + "You are not authorized to search through sections of this course." + ) + + query = request.query_params.get("query", "") + student_absences = request.query_params.get("student_absences", None) + + if not query and student_absences is None: + return Response( + {"error": "Please provide a query"}, status=status.HTTP_400_BAD_REQUEST + ) + + sections = Section.objects.all() + + # Fetch courses associated with the user's coordinator role + courses = request.user.coordinator_set.values_list("course", flat=True) + + # Filter sections based on the courses associated with the user's coordinator role + sections = sections.filter(mentor__course__in=courses) + + if query: + sections = sections.filter( + # pylint: disable-next=unsupported-binary-operation + Q(students__user__first_name__icontains=query) + | Q(students__user__last_name__icontains=query) + | Q(students__user__email__icontains=query) + | Q(mentor__user__first_name__icontains=query) + | Q(mentor__user__last_name__icontains=query) + | Q(mentor__user__email__icontains=query) + ).distinct() + + students = ( + Student.objects.filter( + # pylint: disable-next=unsupported-binary-operation + Q(user__first_name__icontains=query) + | Q(user__last_name__icontains=query) + | Q(user__email__icontains=query), + section__in=sections, + ) + .distinct() + .annotate( + num_absences=Count("attendance", filter=Q(attendance__presence="UN")) + ) + ) + student_query_results = students + + if student_absences is not None: + try: + # Check if the query is a range or single number + if "-" in student_absences: + start, end = student_absences.split("-") + students = ( + Student.objects.annotate( + num_absences=Count( + "attendance", filter=Q(attendance__presence="UN") + ) + ) + .filter( + num_absences__gte=start, + num_absences__lte=end, + section__in=sections, + ) + .distinct() + ) + else: + num_absences = int(student_absences) + students = ( + Student.objects.annotate( + num_absences=Count( + "attendance", filter=Q(attendance__presence="UN") + ) + ) + .filter(num_absences=num_absences, section__in=sections) + .distinct() + ) + sections = sections.filter(students__in=students).distinct() + except ValueError: + return Response( + { + "error": ( + "Invalid value for student_absences. Please provide an integer" + " or an integer range of format start-end." + ) + }, + status=status.HTTP_400_BAD_REQUEST, + ) + student_absences_results = students + + if query and student_absences: + # Filter students based on query + students = student_query_results.intersection(student_absences_results) + + section_serializer = SectionSerializer( + sections, many=True, context={"request": request} + ) + + student_data = [] + for student in students: + # attendance_data = student.attendance.filter(presence="UN") + student_serializer = StudentSerializer(student) + student_data.append( + { + "id": student_serializer.data["id"], + "name": student_serializer.data["name"], + "email": student_serializer.data["email"], + "section": student_serializer.data["section"], + "mentor": student.section.mentor.user.get_full_name(), + "num_absences": student.num_absences, + } + ) + + combined_data = { + "sections": section_serializer.data, + "students": student_data, + } + + return Response(combined_data, status=status.HTTP_200_OK) diff --git a/csm_web/scheduler/views/section.py b/csm_web/scheduler/views/section.py index 1a063140..1098fd01 100644 --- a/csm_web/scheduler/views/section.py +++ b/csm_web/scheduler/views/section.py @@ -216,7 +216,7 @@ def students(self, request, pk=None): Error status format: - 'OK': student is ready to be enrolled (but no action has been taken) - 'CONFLICT': student is already enrolled in another section - - if there is a cnoflicting section: + - if there is a conflicting section: detail = { 'section': serialized section } - if user can't enroll: detail = { 'reason': reason } @@ -356,10 +356,8 @@ class RestrictedAction: if student_queryset.count() > 1: # something bad happened, return immediately with error logger.error( - ( - " Multiple student objects exist in the" - " database (Students %s)!" - ), + " Multiple student objects exist in the" + " database (Students %s)!", student_queryset.all(), ) return Response( @@ -536,10 +534,8 @@ class RestrictedAction: ) student.save() logger.info( - ( - " User %s swapped into Section %s from" - " Section %s" - ), + " User %s swapped into Section %s from" + " Section %s", log_str(student.user), log_str(section), log_str(old_section), @@ -564,26 +560,20 @@ def _student_add(self, request, section): """ if not request.user.can_enroll_in_course(section.mentor.course): logger.warning( - ( - " User %s was unable to enroll in Section %s" - " because they are already involved in this course" - ), + " User %s was unable to enroll in Section %s" + " because they are already involved in this course", log_str(request.user), log_str(section), ) raise PermissionDenied( - ( - "You are already either mentoring for this course or enrolled in a" - " section, or the course is closed for enrollment" - ), + "You are already either mentoring for this course or enrolled in a" + " section, or the course is closed for enrollment", status.HTTP_422_UNPROCESSABLE_ENTITY, ) if section.current_student_count >= section.capacity: logger.warning( - ( - " User %s was unable to enroll in Section %s" - " because it was full" - ), + " User %s was unable to enroll in Section %s" + " because it was full", log_str(request.user), log_str(section), ) @@ -596,18 +586,14 @@ def _student_add(self, request, section): ) if student_queryset.count() > 1: logger.error( - ( - " Multiple student objects exist in the" - " database (Students %s)!" - ), + " Multiple student objects exist in the" + " database (Students %s)!", student_queryset.all(), ) return PermissionDenied( - ( - "An internal error occurred; email mentors@berkeley.edu" - " immediately. (Duplicate students exist in the database (Students" - f" {student_queryset.all()}))" - ), + "An internal error occurred; email mentors@berkeley.edu" + " immediately. (Duplicate students exist in the database (Students" + f" {student_queryset.all()}))", code=status.HTTP_500_INTERNAL_SERVER_ERROR, ) if student_queryset.count() == 1: diff --git a/models.dot b/models.dot new file mode 100644 index 00000000..e61051bc --- /dev/null +++ b/models.dot @@ -0,0 +1,1148 @@ +digraph model_graph { + // Dotfile by Django-Extensions graph_models + // Created: 2024-02-28 21:12 + // Cli Options: scheduler + + fontname = "Roboto" + fontsize = 8 + splines = true + rankdir = "TB" + + node [ + fontname = "Roboto" + fontsize = 8 + shape = "plaintext" + ] + + edge [ + fontname = "Roboto" + fontsize = 8 + ] + + // Labels + + + scheduler_models_Profile [label=< + + + + + + + + + + + +
+ + Profile
<ValidatingModel> +
+ course + + ForeignKey (id) +
+ user + + ForeignKey (None) +
+ >] + + django_contrib_auth_models_AbstractUser [label=< + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + AbstractUser
<AbstractBaseUser,PermissionsMixin> +
+ date_joined + + DateTimeField +
+ email + + EmailField +
+ first_name + + CharField +
+ is_active + + BooleanField +
+ is_staff + + BooleanField +
+ is_superuser + + BooleanField +
+ last_login + + DateTimeField +
+ last_name + + CharField +
+ password + + CharField +
+ username + + CharField +
+ >] + + scheduler_models_ValidatingModel [label=< + + + +
+ + ValidatingModel +
+ >] + + scheduler_models_User [label=< + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + User
<AbstractUser> +
+ id + + AutoField +
+ date_joined + + DateTimeField +
+ email + + EmailField +
+ first_name + + CharField +
+ is_active + + BooleanField +
+ is_staff + + BooleanField +
+ is_superuser + + BooleanField +
+ last_login + + DateTimeField +
+ last_name + + CharField +
+ password + + CharField +
+ priority_enrollment + + DateTimeField +
+ username + + CharField +
+ >] + + scheduler_models_Attendance [label=< + + + + + + + + + + + + + + + + + + + +
+ + Attendance
<ValidatingModel> +
+ id + + AutoField +
+ sectionOccurrence + + ForeignKey (id) +
+ student + + ForeignKey (id) +
+ presence + + CharField +
+ >] + + scheduler_models_SectionOccurrence [label=< + + + + + + + + + + + + + + + + + + + +
+ + SectionOccurrence
<ValidatingModel> +
+ id + + AutoField +
+ section + + ForeignKey (id) +
+ date + + DateField +
+ word_of_the_day + + CharField +
+ >] + + scheduler_models_Course [label=< + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Course
<ValidatingModel> +
+ id + + AutoField +
+ enrollment_end + + DateTimeField +
+ enrollment_start + + DateTimeField +
+ is_restricted + + BooleanField +
+ name + + SlugField +
+ permitted_absences + + PositiveSmallIntegerField +
+ section_start + + DateField +
+ title + + CharField +
+ valid_until + + DateField +
+ word_of_the_day_limit + + DurationField +
+ >] + + scheduler_models_Student [label=< + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Student
<Profile> +
+ id + + AutoField +
+ course + + ForeignKey (id) +
+ section + + ForeignKey (id) +
+ user + + ForeignKey (id) +
+ active + + BooleanField +
+ banned + + BooleanField +
+ >] + + scheduler_models_Mentor [label=< + + + + + + + + + + + + + + + +
+ + Mentor
<Profile> +
+ id + + AutoField +
+ course + + ForeignKey (id) +
+ user + + ForeignKey (id) +
+ >] + + scheduler_models_Coordinator [label=< + + + + + + + + + + + + + + + +
+ + Coordinator
<Profile> +
+ id + + AutoField +
+ course + + ForeignKey (id) +
+ user + + ForeignKey (id) +
+ >] + + scheduler_models_Section [label=< + + + + + + + + + + + + + + + + + + + +
+ + Section
<ValidatingModel> +
+ id + + AutoField +
+ mentor + + OneToOneOrNoneField (id) +
+ capacity + + PositiveSmallIntegerField +
+ description + + CharField +
+ >] + + scheduler_models_Resource [label=< + + + + + + + + + + + + + + + + + + + + + + + +
+ + Resource
<ValidatingModel> +
+ id + + AutoField +
+ course + + ForeignKey (id) +
+ date + + DateField +
+ topics + + CharField +
+ week_num + + PositiveSmallIntegerField +
+ >] + + scheduler_models_Link [label=< + + + + + + + + + + + + + + + + + + + +
+ + Link
<ValidatingModel> +
+ id + + AutoField +
+ resource + + ForeignKey (id) +
+ name + + CharField +
+ url + + URLField +
+ >] + + scheduler_models_Worksheet [label=< + + + + + + + + + + + + + + + + + + + + + + + +
+ + Worksheet
<ValidatingModel> +
+ id + + AutoField +
+ resource + + ForeignKey (id) +
+ name + + CharField +
+ solution_file + + FileField +
+ worksheet_file + + FileField +
+ >] + + scheduler_models_Spacetime [label=< + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Spacetime
<ValidatingModel> +
+ id + + AutoField +
+ section + + ForeignKey (id) +
+ day_of_week + + DayOfWeekField +
+ duration + + DurationField +
+ location + + CharField +
+ start_time + + TimeField +
+ >] + + scheduler_models_Override [label=< + + + + + + + + + + + + + + + + + + + +
+ + Override
<ValidatingModel> +
+ id + + AutoField +
+ overriden_spacetime + + OneToOneField (id) +
+ spacetime + + OneToOneField (id) +
+ date + + DateField +
+ >] + + scheduler_models_Matcher [label=< + + + + + + + + + + + + + + + + + + + + + + + +
+ + Matcher
<ValidatingModel> +
+ id + + AutoField +
+ course + + OneToOneOrNoneField (id) +
+ active + + BooleanField +
+ assignment + + JSONField +
+ is_open + + BooleanField +
+ >] + + scheduler_models_MatcherSlot [label=< + + + + + + + + + + + + + + + + + + + + + + + +
+ + MatcherSlot
<ValidatingModel> +
+ id + + AutoField +
+ matcher + + ForeignKey (id) +
+ max_mentors + + PositiveSmallIntegerField +
+ min_mentors + + PositiveSmallIntegerField +
+ times + + JSONField +
+ >] + + scheduler_models_MatcherPreference [label=< + + + + + + + + + + + + + + + + + + + +
+ + MatcherPreference
<ValidatingModel> +
+ id + + AutoField +
+ mentor + + ForeignKey (id) +
+ slot + + ForeignKey (id) +
+ preference + + PositiveSmallIntegerField +
+ >] + + + + + // Relations + + scheduler_models_Profile -> scheduler_models_User + [label=" user (profile)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Profile -> scheduler_models_Course + [label=" course (profile)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Profile -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + django_contrib_auth_base_user_AbstractBaseUser [label=< + + +
+ AbstractBaseUser +
+ >] + django_contrib_auth_models_AbstractUser -> django_contrib_auth_base_user_AbstractBaseUser + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + django_contrib_auth_models_PermissionsMixin [label=< + + +
+ PermissionsMixin +
+ >] + django_contrib_auth_models_AbstractUser -> django_contrib_auth_models_PermissionsMixin + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + django_contrib_auth_models_Group [label=< + + +
+ Group +
+ >] + scheduler_models_User -> django_contrib_auth_models_Group + [label=" groups (user)"] [arrowhead=dot arrowtail=dot, dir=both]; + django_contrib_auth_models_Permission [label=< + + +
+ Permission +
+ >] + scheduler_models_User -> django_contrib_auth_models_Permission + [label=" user_permissions (user)"] [arrowhead=dot arrowtail=dot, dir=both]; + + scheduler_models_User -> django_contrib_auth_models_AbstractUser + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Attendance -> scheduler_models_Student + [label=" student (attendance)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Attendance -> scheduler_models_SectionOccurrence + [label=" sectionOccurrence (attendance)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Attendance -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_SectionOccurrence -> scheduler_models_Section + [label=" section (sectionoccurrence)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_SectionOccurrence -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Course -> scheduler_models_User + [label=" whitelist (whitelist)"] [arrowhead=dot arrowtail=dot, dir=both]; + + scheduler_models_Course -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Student -> scheduler_models_User + [label=" user (student)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Student -> scheduler_models_Course + [label=" course (student)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Student -> scheduler_models_Section + [label=" section (students)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Student -> scheduler_models_Profile + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Mentor -> scheduler_models_User + [label=" user (mentor)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Mentor -> scheduler_models_Course + [label=" course (mentor)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Mentor -> scheduler_models_Profile + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Coordinator -> scheduler_models_User + [label=" user (coordinator)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Coordinator -> scheduler_models_Course + [label=" course (coordinator)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Coordinator -> scheduler_models_Profile + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Section -> scheduler_models_Mentor + [label=" mentor (section)"] [arrowhead=none, arrowtail=none, dir=both]; + + scheduler_models_Section -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Resource -> scheduler_models_Course + [label=" course (resource)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Resource -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Link -> scheduler_models_Resource + [label=" resource (link)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Link -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Worksheet -> scheduler_models_Resource + [label=" resource (worksheet)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Worksheet -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Spacetime -> scheduler_models_Section + [label=" section (spacetimes)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_Spacetime -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Override -> scheduler_models_Spacetime + [label=" spacetime (+)"] [arrowhead=none, arrowtail=none, dir=both]; + + scheduler_models_Override -> scheduler_models_Spacetime + [label=" overriden_spacetime (_override)"] [arrowhead=none, arrowtail=none, dir=both]; + + scheduler_models_Override -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_Matcher -> scheduler_models_Course + [label=" course (matcher)"] [arrowhead=none, arrowtail=none, dir=both]; + + scheduler_models_Matcher -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_MatcherSlot -> scheduler_models_Matcher + [label=" matcher (matcherslot)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_MatcherSlot -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + scheduler_models_MatcherPreference -> scheduler_models_MatcherSlot + [label=" slot (matcherpreference)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_MatcherPreference -> scheduler_models_Mentor + [label=" mentor (matcherpreference)"] [arrowhead=none, arrowtail=dot, dir=both]; + + scheduler_models_MatcherPreference -> scheduler_models_ValidatingModel + [label=" abstract\ninheritance"] [arrowhead=empty, arrowtail=none, dir=both]; + + +} diff --git a/models.svg b/models.svg new file mode 100644 index 00000000..5486cecd --- /dev/null +++ b/models.svg @@ -0,0 +1,1183 @@ + + + + + + +model_graph + + + +scheduler_models_Profile + + +     +    Profile +< +ValidatingModel +>     +     +course +     +     +ForeignKey (id) +     +     +user +     +     +ForeignKey (None) +     + + + + +scheduler_models_ValidatingModel + + +     +    ValidatingModel     + + + + +scheduler_models_Profile->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_User + + +     +    User +< +AbstractUser +>     +     +id +     +     +AutoField +     +     +date_joined +     +     +DateTimeField +     +     +email +     +     +EmailField +     +     +first_name +     +     +CharField +     +     +is_active +     +     +BooleanField +     +     +is_staff +     +     +BooleanField +     +     +is_superuser +     +     +BooleanField +     +     +last_login +     +     +DateTimeField +     +     +last_name +     +     +CharField +     +     +password +     +     +CharField +     +     +priority_enrollment +     +     +DateTimeField +     +     +username +     +     +CharField +     + + + + +scheduler_models_Profile->scheduler_models_User + + + user (profile) + + + +scheduler_models_Course + + +     +    Course +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +enrollment_end +     +     +DateTimeField +     +     +enrollment_start +     +     +DateTimeField +     +     +is_restricted +     +     +BooleanField +     +     +name +     +     +SlugField +     +     +permitted_absences +     +     +PositiveSmallIntegerField +     +     +section_start +     +     +DateField +     +     +title +     +     +CharField +     +     +valid_until +     +     +DateField +     +     +word_of_the_day_limit +     +     +DurationField +     + + + + +scheduler_models_Profile->scheduler_models_Course + + + course (profile) + + + +django_contrib_auth_models_AbstractUser + + +     +    AbstractUser +< +AbstractBaseUser,PermissionsMixin +>     +     +date_joined +     +     +DateTimeField +     +     +email +     +     +EmailField +     +     +first_name +     +     +CharField +     +     +is_active +     +     +BooleanField +     +     +is_staff +     +     +BooleanField +     +     +is_superuser +     +     +BooleanField +     +     +last_login +     +     +DateTimeField +     +     +last_name +     +     +CharField +     +     +password +     +     +CharField +     +     +username +     +     +CharField +     + + + + +django_contrib_auth_base_user_AbstractBaseUser + + +   +AbstractBaseUser +   + + + +django_contrib_auth_models_AbstractUser->django_contrib_auth_base_user_AbstractBaseUser + + + abstract +inheritance + + + +django_contrib_auth_models_PermissionsMixin + + +   +PermissionsMixin +   + + + +django_contrib_auth_models_AbstractUser->django_contrib_auth_models_PermissionsMixin + + + abstract +inheritance + + + +scheduler_models_User->django_contrib_auth_models_AbstractUser + + + abstract +inheritance + + + +django_contrib_auth_models_Group + + +   +Group +   + + + +scheduler_models_User->django_contrib_auth_models_Group + + + + groups (user) + + + +django_contrib_auth_models_Permission + + +   +Permission +   + + + +scheduler_models_User->django_contrib_auth_models_Permission + + + + user_permissions (user) + + + +scheduler_models_Attendance + + +     +    Attendance +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +sectionOccurrence +     +     +ForeignKey (id) +     +     +student +     +     +ForeignKey (id) +     +     +presence +     +     +CharField +     + + + + +scheduler_models_Attendance->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_SectionOccurrence + + +     +    SectionOccurrence +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +section +     +     +ForeignKey (id) +     +     +date +     +     +DateField +     +     +word_of_the_day +     +     +CharField +     + + + + +scheduler_models_Attendance->scheduler_models_SectionOccurrence + + + sectionOccurrence (attendance) + + + +scheduler_models_Student + + +     +    Student +< +Profile +>     +     +id +     +     +AutoField +     +     +course +     +     +ForeignKey (id) +     +     +section +     +     +ForeignKey (id) +     +     +user +     +     +ForeignKey (id) +     +     +active +     +     +BooleanField +     +     +banned +     +     +BooleanField +     + + + + +scheduler_models_Attendance->scheduler_models_Student + + + student (attendance) + + + +scheduler_models_SectionOccurrence->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Section + + +     +    Section +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +mentor +     +     +OneToOneOrNoneField (id) +     +     +capacity +     +     +PositiveSmallIntegerField +     +     +description +     +     +CharField +     + + + + +scheduler_models_SectionOccurrence->scheduler_models_Section + + + section (sectionoccurrence) + + + +scheduler_models_Course->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Course->scheduler_models_User + + + + whitelist (whitelist) + + + +scheduler_models_Student->scheduler_models_Profile + + + abstract +inheritance + + + +scheduler_models_Student->scheduler_models_User + + + user (student) + + + +scheduler_models_Student->scheduler_models_Course + + + course (student) + + + +scheduler_models_Student->scheduler_models_Section + + + section (students) + + + +scheduler_models_Mentor + + +     +    Mentor +< +Profile +>     +     +id +     +     +AutoField +     +     +course +     +     +ForeignKey (id) +     +     +user +     +     +ForeignKey (id) +     + + + + +scheduler_models_Mentor->scheduler_models_Profile + + + abstract +inheritance + + + +scheduler_models_Mentor->scheduler_models_User + + + user (mentor) + + + +scheduler_models_Mentor->scheduler_models_Course + + + course (mentor) + + + +scheduler_models_Coordinator + + +     +    Coordinator +< +Profile +>     +     +id +     +     +AutoField +     +     +course +     +     +ForeignKey (id) +     +     +user +     +     +ForeignKey (id) +     + + + + +scheduler_models_Coordinator->scheduler_models_Profile + + + abstract +inheritance + + + +scheduler_models_Coordinator->scheduler_models_User + + + user (coordinator) + + + +scheduler_models_Coordinator->scheduler_models_Course + + + course (coordinator) + + + +scheduler_models_Section->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Section->scheduler_models_Mentor + + mentor (section) + + + +scheduler_models_Resource + + +     +    Resource +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +course +     +     +ForeignKey (id) +     +     +date +     +     +DateField +     +     +topics +     +     +CharField +     +     +week_num +     +     +PositiveSmallIntegerField +     + + + + +scheduler_models_Resource->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Resource->scheduler_models_Course + + + course (resource) + + + +scheduler_models_Link + + +     +    Link +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +resource +     +     +ForeignKey (id) +     +     +name +     +     +CharField +     +     +url +     +     +URLField +     + + + + +scheduler_models_Link->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Link->scheduler_models_Resource + + + resource (link) + + + +scheduler_models_Worksheet + + +     +    Worksheet +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +resource +     +     +ForeignKey (id) +     +     +name +     +     +CharField +     +     +solution_file +     +     +FileField +     +     +worksheet_file +     +     +FileField +     + + + + +scheduler_models_Worksheet->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Worksheet->scheduler_models_Resource + + + resource (worksheet) + + + +scheduler_models_Spacetime + + +     +    Spacetime +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +section +     +     +ForeignKey (id) +     +     +day_of_week +     +     +DayOfWeekField +     +     +duration +     +     +DurationField +     +     +location +     +     +CharField +     +     +start_time +     +     +TimeField +     + + + + +scheduler_models_Spacetime->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Spacetime->scheduler_models_Section + + + section (spacetimes) + + + +scheduler_models_Override + + +     +    Override +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +overriden_spacetime +     +     +OneToOneField (id) +     +     +spacetime +     +     +OneToOneField (id) +     +     +date +     +     +DateField +     + + + + +scheduler_models_Override->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Override->scheduler_models_Spacetime + + spacetime (+) + + + +scheduler_models_Override->scheduler_models_Spacetime + + overriden_spacetime (_override) + + + +scheduler_models_Matcher + + +     +    Matcher +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +course +     +     +OneToOneOrNoneField (id) +     +     +active +     +     +BooleanField +     +     +assignment +     +     +JSONField +     +     +is_open +     +     +BooleanField +     + + + + +scheduler_models_Matcher->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_Matcher->scheduler_models_Course + + course (matcher) + + + +scheduler_models_MatcherSlot + + +     +    MatcherSlot +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +matcher +     +     +ForeignKey (id) +     +     +max_mentors +     +     +PositiveSmallIntegerField +     +     +min_mentors +     +     +PositiveSmallIntegerField +     +     +times +     +     +JSONField +     + + + + +scheduler_models_MatcherSlot->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_MatcherSlot->scheduler_models_Matcher + + + matcher (matcherslot) + + + +scheduler_models_MatcherPreference + + +     +    MatcherPreference +< +ValidatingModel +>     +     +id +     +     +AutoField +     +     +mentor +     +     +ForeignKey (id) +     +     +slot +     +     +ForeignKey (id) +     +     +preference +     +     +PositiveSmallIntegerField +     + + + + +scheduler_models_MatcherPreference->scheduler_models_ValidatingModel + + + abstract +inheritance + + + +scheduler_models_MatcherPreference->scheduler_models_Mentor + + + mentor (matcherpreference) + + + +scheduler_models_MatcherPreference->scheduler_models_MatcherSlot + + + slot (matcherpreference) + + +