Skip to content

Commit

Permalink
Add courses api filters to catalog page (#1892)
Browse files Browse the repository at this point in the history
* Add filters to courses API on catalog page

* add index to courserun.live

* remove uncomment code

* Add docstring, add back prefetch courseruns when courserun_is_enrollable isn't applied
  • Loading branch information
rachellougee authored Sep 18, 2023
1 parent 0c763d8 commit 5e6f5ee
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 5 deletions.
18 changes: 18 additions & 0 deletions courses/migrations/0044_alter_courserun_live.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.20 on 2023-09-15 12:13

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("courses", "0043_course_program_live_index"),
]

operations = [
migrations.AlterField(
model_name="courserun",
name="live",
field=models.BooleanField(db_index=True, default=False),
),
]
2 changes: 1 addition & 1 deletion courses/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ class CourseRun(TimestampedModel):
help_text="The date beyond which the learner can not enroll in paid course mode.",
)

live = models.BooleanField(default=False)
live = models.BooleanField(default=False, db_index=True)
is_self_paced = models.BooleanField(default=False)
products = GenericRelation(
"ecommerce.Product", related_query_name="courserunproducts"
Expand Down
55 changes: 53 additions & 2 deletions courses/views/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Course views verson 1"""
import logging
import django_filters
from typing import Optional, Tuple, Union

from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.db.models import Q, Count
from django.db.models import Q, Count, Prefetch
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django_filters.rest_framework import DjangoFilterBackend
Expand Down Expand Up @@ -58,6 +59,7 @@
USER_MSG_TYPE_ENROLLED,
)
from main.utils import encode_json_cookie_value
from mitol.common.utils import now_in_utc
from openedx.api import (
subscribe_to_edx_course_emails,
sync_enrollments_with_edx,
Expand Down Expand Up @@ -102,16 +104,65 @@ def paginate_queryset(self, queryset):
return super().paginate_queryset(queryset)


class CourseFilterSet(django_filters.FilterSet):

courserun_is_enrollable = django_filters.BooleanFilter(
field_name="courserun_is_enrollable",
method="filter_courserun_is_enrollable",
)

def filter_courserun_is_enrollable(self, queryset, _, value):
"""
courserun_is_enrollable filter to narrow down runs that are open for
enrollments
"""
now = now_in_utc()

if value is True:
enrollable_runs = CourseRun.objects.filter(
Q(live=True)
& Q(start_date__isnull=False)
& Q(enrollment_start__lt=now)
& (Q(enrollment_end=None) | Q(enrollment_end__gt=now))
)
return queryset.prefetch_related(
Prefetch("courseruns", queryset=enrollable_runs)
).filter(courseruns__id__in=enrollable_runs.values_list("id", flat=True))

else:
unenrollable_runs = CourseRun.objects.filter(
Q(live=False) | Q(start_date__isnull=True) | Q(enrollment_end__lte=now)
)
return queryset.prefetch_related(
Prefetch("courseruns", queryset=unenrollable_runs)
).filter(courseruns__id__in=unenrollable_runs.values_list("id", flat=True))

class Meta:
model = Course
fields = ["id", "live", "readable_id", "page__live", "courserun_is_enrollable"]


class CourseViewSet(viewsets.ReadOnlyModelViewSet):
"""API view set for Courses"""

pagination_class = Pagination
permission_classes = []
filter_backends = [DjangoFilterBackend]
serializer_class = CourseWithCourseRunsSerializer
filterset_fields = ["id", "live", "readable_id"]
filterset_class = CourseFilterSet

def get_queryset(self):
courserun_is_enrollable = self.request.query_params.get(
"courserun_is_enrollable", None
)

if courserun_is_enrollable:
return (
Course.objects.filter()
.select_related("page")
.prefetch_related("departments")
.all()
)
return (
Course.objects.filter()
.select_related("page")
Expand Down
2 changes: 1 addition & 1 deletion frontend/public/src/containers/pages/CatalogPage_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ describe("CatalogPage", function() {

sinon.assert.calledWith(
helper.handleRequestStub,
"/api/courses/?page=2&live=true",
"/api/courses/?page=2&live=true&page__live=true&courserun_is_enrollable=true",
"GET"
)

Expand Down
2 changes: 1 addition & 1 deletion frontend/public/src/lib/queries/courses.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const coursesQueryKey = "courses"

export const coursesQuery = page => ({
queryKey: coursesQueryKey,
url: `/api/courses/?page=${page}&live=true`,
url: `/api/courses/?page=${page}&live=true&page__live=true&courserun_is_enrollable=true`,
transform: json => ({
courses: json
}),
Expand Down

0 comments on commit 5e6f5ee

Please sign in to comment.