Skip to content

Commit

Permalink
feat(FFI-3): adds openedx-filter hook to the course enrollments site
Browse files Browse the repository at this point in the history
fix: change filter name by CourseEnrollmentQuerysetRequested

feat: catch filter exception

feat: add filter test

fix: change import class to run test

test: fix filter output test

test: complete test for queryset
  • Loading branch information
JuanDavidBuitrago committed Jul 19, 2023
1 parent 8111d5b commit 653dc3a
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
67 changes: 66 additions & 1 deletion common/djangoapps/student/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
from django.http import HttpResponse
from django.test import override_settings
from django.urls import reverse
from openedx.core.djangoapps.enrollments.data import get_course_enrollments
from openedx_filters import PipelineStep
from openedx_filters.learning.filters import DashboardRenderStarted, CourseEnrollmentStarted, CourseUnenrollmentStarted
from openedx_filters.learning.filters import (
DashboardRenderStarted,
CourseEnrollmentStarted,
CourseUnenrollmentStarted,
)
from rest_framework import status
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
Expand Down Expand Up @@ -110,6 +115,20 @@ def run_filter(self, context, template_name): # pylint: disable=arguments-diffe
)


class TestCourseEnrollmentsPipelineStep(PipelineStep):
"""
Utility function used when getting steps for pipeline.
"""

def run_filter(self, enrollments): # pylint: disable=arguments-differ
"""Pipeline steps that modifies course enrollments when make a queryset request."""

enrollments = [enrollment for enrollment in enrollments if enrollment.course_id.org == "demo"]
return {
"enrollments": enrollments,
}


@skip_unless_lms
class EnrollmentFiltersTest(ModuleStoreTestCase):
"""
Expand All @@ -118,17 +137,23 @@ class EnrollmentFiltersTest(ModuleStoreTestCase):
This class guarantees that the following filters are triggered during the user's enrollment:
- CourseEnrollmentStarted
- CourseEnrollmentQuerysetRequested
"""

def setUp(self): # pylint: disable=arguments-differ
super().setUp()
self.course = CourseFactory.create()
demo_course = CourseFactory.create(org='demo')
test_course = CourseFactory.create(org='test')
self.user = UserFactory.create(
username="test",
email="test@example.com",
password="password",
)
self.user_profile = UserProfileFactory.create(user=self.user, name="Test Example")
CourseEnrollment.enroll(self.user, demo_course.id, mode='audit')
CourseEnrollment.enroll(self.user, test_course.id, mode='audit')
self.enrollment = get_course_enrollments(self.user)

@override_settings(
OPEN_EDX_FILTERS_CONFIG={
Expand Down Expand Up @@ -189,6 +214,46 @@ def test_enrollment_without_filter_configuration(self):
self.assertEqual('audit', enrollment.mode)
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))

@override_settings()
def test_enrollment_queryset_filter_unexecuted_data(self):
"""
Test filter enrollment queryset when a request is made.
Expected result:
- CourseEnrollmentQuerysetRequested is triggered and executes TestCourseEnrollmentsPipelineStep.
- The result is a list of course enrollments queryset filter by org
"""
enrollments = get_course_enrollments(self.user)

self.assertListEqual(self.enrollment, enrollments)

@override_settings(
OPEN_EDX_FILTERS_CONFIG={
"org.openedx.learning.course_enrollment_queryset.requested.v1": {
"pipeline": [
"common.djangoapps.student.tests.test_filters.TestCourseEnrollmentsPipelineStep",
],
"fail_silently": False,
},
},
)
def test_enrollment_queryset_filter_executed_data(self):
"""
Test filter enrollment queryset when a request is made.
Expected result:
- CourseEnrollmentQuerysetRequested is triggered and executes TestCourseEnrollmentsPipelineStep.
- The result is a list of course enrollments queryset filter by org
"""
expected_enrollment = self.enrollment
expected_enrollment = expected_enrollment[0]['course_details']['course_id']

enrollments = get_course_enrollments(self.user)
enrollments = enrollments[0]['course_details']['course_id']

self.assertEqual(expected_enrollment, enrollments)
self.assertAlmostEqual(len(enrollments), len(expected_enrollment), 1)


@skip_unless_lms
class UnenrollmentFiltersTest(ModuleStoreTestCase):
Expand Down
16 changes: 16 additions & 0 deletions lms/djangoapps/mobile_api/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.utils.decorators import method_decorator
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
from openedx_filters.learning.filters import CourseEnrollmentQuerysetRequested
from rest_framework import generics, views
from rest_framework.decorators import api_view
from rest_framework.permissions import SAFE_METHODS
Expand Down Expand Up @@ -341,6 +342,13 @@ def get_queryset(self):
).order_by('created').reverse()
org = self.request.query_params.get('org', None)

try:
## .. filter_implemented_name: CourseEnrollmentQuerysetRequested
## .. filter_type: org.openedx.learning.course_enrollment_queryset.requested.v1
enrollments = CourseEnrollmentQuerysetRequested.run_filter(enrollments=enrollments)
except CourseEnrollmentQuerysetRequested.PreventEnrollmentQuerysetRequest as exc:
raise EnrollmentRequestNotAllowed(str(exc)) from exc

same_org = (
enrollment for enrollment in enrollments
if enrollment.course_overview and self.is_org(org, enrollment.course_overview.org)
Expand Down Expand Up @@ -375,6 +383,14 @@ def list(self, request, *args, **kwargs):
return response


class EnrollmentRequestException(Exception):
pass


class EnrollmentRequestNotAllowed(EnrollmentRequestException):
pass


@api_view(["GET"])
@mobile_view()
def my_user_info(request, api_version):
Expand Down
16 changes: 16 additions & 0 deletions openedx/core/djangoapps/enrollments/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.db import transaction
from opaque_keys.edx.keys import CourseKey
from openedx_filters.learning.filters import CourseEnrollmentQuerysetRequested

from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.enrollments.errors import (
Expand Down Expand Up @@ -54,6 +55,13 @@ def get_course_enrollments(username, include_inactive=False):
if not include_inactive:
qset = qset.filter(is_active=True)

try:
## .. filter_implemented_name: CourseEnrollmentQuerysetRequested
## .. filter_type: org.openedx.learning.course_enrollment_queryset.requested.v1
qset = CourseEnrollmentQuerysetRequested.run_filter(enrollments=qset)
except CourseEnrollmentQuerysetRequested.PreventEnrollmentQuerysetRequest as exc:
raise EnrollmentRequestNotAllowed(str(exc)) from exc

enrollments = CourseEnrollmentSerializer(qset, many=True).data

# Find deleted courses and filter them out of the results
Expand All @@ -76,6 +84,14 @@ def get_course_enrollments(username, include_inactive=False):
return valid


class EnrollmentRequestException(Exception):
pass


class EnrollmentRequestNotAllowed(EnrollmentRequestException):
pass


def get_course_enrollment(username, course_id):
"""Retrieve an object representing all aggregated data for a user's course enrollment.
Expand Down

0 comments on commit 653dc3a

Please sign in to comment.