Skip to content

Commit 6f84c89

Browse files
7278 b2b provisioning add course runcontract filtering (#2651)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 5831b37 commit 6f84c89

File tree

2 files changed

+80
-9
lines changed

2 files changed

+80
-9
lines changed

courses/views/v2/__init__.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,24 @@ class CourseFilterSet(django_filters.FilterSet):
7171
)
7272
id = IdInFilter(field_name="id", lookup_expr="in", label="Course ID")
7373

74-
def filter_courserun_is_enrollable(self, queryset, _, value):
75-
"""
76-
courserun_is_enrollable filter to narrow down runs that are open for
77-
enrollments
74+
def filter_queryset(self, queryset):
75+
request = self.request
76+
user = request.user if request else None
77+
org_id = request.query_params.get("org_id") if request else None
78+
79+
if not user or user.is_anonymous:
80+
queryset = queryset.filter(courseruns__b2b_contract__isnull=True)
81+
elif org_id:
82+
queryset = queryset.filter(
83+
courseruns__b2b_contract__organization__id=org_id,
84+
courseruns__b2b_contract__active=True,
85+
)
86+
else:
87+
queryset = queryset.filter(courseruns__b2b_contract__isnull=True)
88+
89+
return super().filter_queryset(queryset.distinct())
7890

79-
Uses utility functions that are shared wtih other parts of the application
80-
to keep the logic consistent
81-
"""
91+
def filter_courserun_is_enrollable(self, queryset, _, value):
8292
if value:
8393
return get_enrollable_courses(queryset)
8494
return get_unenrollable_courses(queryset)

courses/views/v2/views_test.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
import random
77

88
import pytest
9+
from django.contrib.auth.models import AnonymousUser
910
from django.db import connection
11+
from django.test.client import RequestFactory
1012
from django.urls import reverse
1113
from rest_framework import status
14+
from rest_framework.request import Request
1215

13-
from courses.factories import DepartmentFactory
16+
from b2b.api import create_contract_run
17+
from b2b.factories import ContractPageFactory, OrganizationPageFactory
18+
from courses.factories import CourseFactory, CourseRunFactory, DepartmentFactory
1419
from courses.models import Course, Program
1520
from courses.serializers.v2.courses import CourseWithCourseRunsSerializer
1621
from courses.serializers.v2.departments import (
@@ -22,7 +27,7 @@
2227
num_queries_from_department,
2328
num_queries_from_programs,
2429
)
25-
from courses.views.v2 import Pagination
30+
from courses.views.v2 import CourseFilterSet, Pagination
2631
from main.test_utils import assert_drf_json_equal, duplicate_queries_check
2732

2833
pytestmark = [pytest.mark.django_db, pytest.mark.usefixtures("raise_nplusone")]
@@ -251,3 +256,59 @@ def test_get_course(
251256
CourseWithCourseRunsSerializer(instance=course, context=mock_context).data
252257
)
253258
assert_drf_json_equal(course_data, course_from_fixture, ignore_order=True)
259+
260+
261+
@pytest.mark.django_db
262+
def test_filter_with_org_id_returns_contracted_course(user_drf_client):
263+
org = OrganizationPageFactory(name="Test Org")
264+
contract = ContractPageFactory(organization=org, active=True)
265+
course = CourseFactory(title="Contracted Course")
266+
create_contract_run(contract, course)
267+
268+
unrelated_course = Course.objects.create(title="Other Course")
269+
CourseRunFactory(course=unrelated_course)
270+
271+
url = reverse("v2:courses_api-list")
272+
response = user_drf_client.get(url, {"org_id": org.id})
273+
274+
titles = [result["title"] for result in response.data["results"]]
275+
assert course.title in titles
276+
assert unrelated_course.title not in titles
277+
278+
279+
@pytest.mark.django_db
280+
def test_filter_without_org_id_authenticated_user(user_drf_client):
281+
course_with_contract = CourseFactory(title="Contract Course")
282+
contract = ContractPageFactory(active=True)
283+
CourseRunFactory(course=course_with_contract, b2b_contract=contract)
284+
285+
course_no_contract = CourseFactory(title="No Contract Course")
286+
CourseRunFactory(course=course_no_contract, b2b_contract=None)
287+
288+
url = reverse("v2:courses_api-list")
289+
response = user_drf_client.get(url)
290+
291+
titles = [result["title"] for result in response.data["results"]]
292+
293+
assert course_no_contract.title in titles
294+
assert course_with_contract.title in titles
295+
296+
297+
def test_filter_anonymous_user_sees_no_contracted_runs():
298+
course_with_contract = CourseFactory(title="Hidden Course")
299+
contract = ContractPageFactory(active=True)
300+
CourseRunFactory(course=course_with_contract, b2b_contract=contract)
301+
302+
course_no_contract = CourseFactory(title="Visible Course")
303+
CourseRunFactory(course=course_no_contract)
304+
rf = RequestFactory()
305+
request = rf.get(reverse("v2:courses_api-list"))
306+
request.user = AnonymousUser()
307+
drf_request = Request(request)
308+
queryset = Course.objects.all()
309+
filtered = CourseFilterSet(
310+
data=drf_request.query_params, request=drf_request, queryset=queryset
311+
).qs
312+
313+
assert course_no_contract in filtered
314+
assert course_with_contract not in filtered

0 commit comments

Comments
 (0)