Refactor program type enrollment checks
Checking if a user was enrolled in a program type was using the `name` field which is subject to be translated. This change allows for us to check by the type's slug which will be constant. This also includes the addition of api.py files for the course_modes, catalog, and programs apps.
This commit is contained in:
17
common/djangoapps/course_modes/api.py
Normal file
17
common/djangoapps/course_modes/api.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Python APIs exposed by the course_modes app to other in-process apps.
|
||||
"""
|
||||
|
||||
from course_modes.models import CourseMode as _CourseMode
|
||||
|
||||
|
||||
def get_paid_modes_for_course(course_run_id):
|
||||
"""
|
||||
Returns a list of non-expired mode slugs for a course run ID that have a set minimum price.
|
||||
|
||||
Params:
|
||||
course_run_id (CourseKey): The course run you want to get the paid modes for
|
||||
Returns:
|
||||
A list of paid modes (strings) that the course has attached to it.
|
||||
"""
|
||||
return _CourseMode.paid_modes_for_course(course_run_id)
|
||||
@@ -1,12 +0,0 @@
|
||||
"""
|
||||
URL definitions for the course_modes API.
|
||||
"""
|
||||
|
||||
|
||||
from django.conf.urls import include, url
|
||||
|
||||
app_name = 'common.djangoapps.course_modes.api'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^v1/', include('course_modes.api.v1.urls', namespace='v1')),
|
||||
]
|
||||
12
common/djangoapps/course_modes/rest_api/urls.py
Normal file
12
common/djangoapps/course_modes/rest_api/urls.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
URL definitions for the course_modes API.
|
||||
"""
|
||||
|
||||
|
||||
from django.conf.urls import include, url
|
||||
|
||||
app_name = 'common.djangoapps.course_modes.rest_api'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^v1/', include('course_modes.rest_api.v1.urls', namespace='v1')),
|
||||
]
|
||||
@@ -5,7 +5,6 @@ Tests for the course modes API.
|
||||
|
||||
import json
|
||||
import unittest
|
||||
from itertools import product
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
@@ -15,7 +14,7 @@ from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
from six import text_type
|
||||
|
||||
from course_modes.api.v1.views import CourseModesView
|
||||
from course_modes.rest_api.v1.views import CourseModesView
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
@@ -79,7 +78,7 @@ class CourseModesViewTestBase(AuthAndScopesTestMixin):
|
||||
"""
|
||||
Required method to implement AuthAndScopesTestMixin.
|
||||
"""
|
||||
pass
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
@ddt.data(*JWT_AUTH_TYPES)
|
||||
def test_jwt_on_behalf_of_user(self, auth_type):
|
||||
@@ -6,7 +6,7 @@ URL definitions for the course_modes v1 API.
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
|
||||
from course_modes.api.v1 import views
|
||||
from course_modes.rest_api.v1 import views
|
||||
|
||||
app_name = 'v1'
|
||||
|
||||
@@ -14,7 +14,7 @@ from rest_framework import status
|
||||
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from course_modes.api.serializers import CourseModeSerializer
|
||||
from course_modes.rest_api.serializers import CourseModeSerializer
|
||||
from course_modes.models import CourseMode
|
||||
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
|
||||
from openedx.core.lib.api.parsers import MergePatchParser
|
||||
@@ -156,8 +156,9 @@ urlpatterns = [
|
||||
|
||||
# Multiple course modes and identity verification
|
||||
url(r'^course_modes/', include('course_modes.urls')),
|
||||
url(r'^api/course_modes/', include(('course_modes.api.urls', 'common.djangoapps.course_mods'),
|
||||
url(r'^api/course_modes/', include(('course_modes.rest_api.urls', 'common.djangoapps.course_mods'),
|
||||
namespace='course_modes_api')),
|
||||
|
||||
url(r'^verify_student/', include('verify_student.urls')),
|
||||
|
||||
# URLs for managing dark launches of languages
|
||||
|
||||
21
openedx/core/djangoapps/catalog/api.py
Normal file
21
openedx/core/djangoapps/catalog/api.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Python APIs exposed by the catalog app to other in-process apps.
|
||||
"""
|
||||
|
||||
from .utils import get_programs_by_type_slug as _get_programs_by_type_slug
|
||||
|
||||
|
||||
def get_programs_by_type(site, program_type_slug):
|
||||
"""
|
||||
Retrieves a list of programs for the site whose type's slug matches the parameter.
|
||||
Slug is used is used as a consistent value to check since ProgramType.name is
|
||||
a field that gets translated.
|
||||
|
||||
Params:
|
||||
site (Site): The corresponding Site object to fetch programs for.
|
||||
program_type_slug (string): The type slug that matching programs must have.
|
||||
|
||||
Returns:
|
||||
A list of programs (dicts) for the given site with the given type slug
|
||||
"""
|
||||
return _get_programs_by_type_slug(site, program_type_slug)
|
||||
@@ -22,5 +22,9 @@ CATALOG_COURSE_PROGRAMS_CACHE_KEY_TPL = 'catalog-course-programs-{course_uuid}'
|
||||
# that live in the same environment).
|
||||
PROGRAMS_BY_TYPE_CACHE_KEY_TPL = 'programs-by-type-{site_id}-{program_type}'
|
||||
|
||||
# Site-aware cache key template used to locate an item containing
|
||||
# a list of all program UUIDs with a certain program slug
|
||||
PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL = 'programs-by-type-slug-{site_id}-{program_slug}'
|
||||
|
||||
# Template used to create cache keys for organization to program uuids.
|
||||
PROGRAMS_BY_ORGANIZATION_CACHE_KEY_TPL = 'organization-programs-{org_key}'
|
||||
|
||||
@@ -18,6 +18,7 @@ from openedx.core.djangoapps.catalog.cache import (
|
||||
PROGRAM_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_ORGANIZATION_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL,
|
||||
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
|
||||
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL
|
||||
)
|
||||
@@ -64,6 +65,7 @@ class Command(BaseCommand):
|
||||
courses = {}
|
||||
catalog_courses = {}
|
||||
programs_by_type = {}
|
||||
programs_by_type_slug = {}
|
||||
organizations = {}
|
||||
for site in Site.objects.all():
|
||||
site_config = getattr(site, 'configuration', None)
|
||||
@@ -93,6 +95,7 @@ class Command(BaseCommand):
|
||||
courses.update(self.get_courses(new_programs))
|
||||
catalog_courses.update(self.get_catalog_courses(new_programs))
|
||||
programs_by_type.update(self.get_programs_by_type(site, new_programs))
|
||||
programs_by_type_slug.update(self.get_programs_by_type_slug(site, new_programs))
|
||||
organizations.update(self.get_programs_by_organization(new_programs))
|
||||
|
||||
logger.info(u'Caching UUIDs for {total} programs for site {site_name}.'.format(
|
||||
@@ -123,6 +126,9 @@ class Command(BaseCommand):
|
||||
logger.info(text_type('Caching program UUIDs by {} program types.'.format(len(programs_by_type))))
|
||||
cache.set_many(programs_by_type, None)
|
||||
|
||||
logger.info(text_type('Caching program UUIDs by {} program type slugs.'.format(len(programs_by_type_slug))))
|
||||
cache.set_many(programs_by_type_slug, None)
|
||||
|
||||
logger.info(u'Caching programs uuids for {} organizations'.format(len(organizations)))
|
||||
cache.set_many(organizations, None)
|
||||
|
||||
@@ -263,6 +269,18 @@ class Command(BaseCommand):
|
||||
programs_by_type[cache_key].append(program['uuid'])
|
||||
return programs_by_type
|
||||
|
||||
def get_programs_by_type_slug(self, site, programs):
|
||||
"""
|
||||
Returns a dictionary mapping site-aware cache keys corresponding to program types
|
||||
to lists of program uuids with that type.
|
||||
"""
|
||||
programs_by_type_slug = defaultdict(list)
|
||||
for program in programs.values():
|
||||
program_slug = program.get('type_attrs', {}).get('slug')
|
||||
cache_key = PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL.format(site_id=site.id, program_slug=program_slug)
|
||||
programs_by_type_slug[cache_key].append(program['uuid'])
|
||||
return programs_by_type_slug
|
||||
|
||||
def get_programs_by_organization(self, programs):
|
||||
"""
|
||||
Returns a dictionary mapping organization keys to lists of program uuids authored by that org
|
||||
|
||||
@@ -14,6 +14,7 @@ from openedx.core.djangoapps.catalog.cache import (
|
||||
PATHWAY_CACHE_KEY_TPL,
|
||||
PROGRAM_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL,
|
||||
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
|
||||
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL
|
||||
)
|
||||
@@ -199,10 +200,15 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, SiteMix
|
||||
# program UUIDS by program type and a cached list of UUIDs by authoring organization
|
||||
for program in self.programs:
|
||||
program_type = normalize_program_type(program.get('type', 'None'))
|
||||
program_type_slug = program.get('type_attrs', {}).get('slug')
|
||||
program_type_cache_key = PROGRAMS_BY_TYPE_CACHE_KEY_TPL.format(
|
||||
site_id=self.site.id, program_type=program_type
|
||||
)
|
||||
program_type_slug_cache_key = PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL.format(
|
||||
site_id=self.site.id, program_slug=program_type_slug
|
||||
)
|
||||
self.assertIn(program['uuid'], cache.get(program_type_cache_key))
|
||||
self.assertIn(program['uuid'], cache.get(program_type_slug_cache_key))
|
||||
|
||||
for organization in program['authoring_organizations']:
|
||||
organization_cache_key = PROGRAMS_BY_ORGANIZATION_CACHE_KEY_TPL.format(
|
||||
|
||||
@@ -24,6 +24,7 @@ from openedx.core.djangoapps.catalog.cache import (
|
||||
PATHWAY_CACHE_KEY_TPL,
|
||||
PROGRAM_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL,
|
||||
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
|
||||
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL
|
||||
)
|
||||
@@ -33,7 +34,8 @@ from openedx.core.djangoapps.catalog.tests.factories import (
|
||||
CourseRunFactory,
|
||||
PathwayFactory,
|
||||
ProgramFactory,
|
||||
ProgramTypeFactory
|
||||
ProgramTypeFactory,
|
||||
ProgramTypeAttrsFactory
|
||||
)
|
||||
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
|
||||
from openedx.core.djangoapps.catalog.utils import (
|
||||
@@ -50,6 +52,7 @@ from openedx.core.djangoapps.catalog.utils import (
|
||||
get_program_types,
|
||||
get_programs,
|
||||
get_programs_by_type,
|
||||
get_programs_by_type_slug,
|
||||
get_visible_sessions_for_entitlement,
|
||||
normalize_program_type,
|
||||
)
|
||||
@@ -830,7 +833,7 @@ class TestProgramCourseRunCrawling(TestCase):
|
||||
|
||||
@skip_unless_lms
|
||||
class TestGetProgramsByType(CacheIsolationTestCase):
|
||||
""" Test for the ``get_programs_by_type()`` function. """
|
||||
""" Test for the ``get_programs_by_type()`` and the ``get_programs_by_type_slug()`` functions. """
|
||||
ENABLED_CACHES = ['default']
|
||||
|
||||
@classmethod
|
||||
@@ -839,11 +842,26 @@ class TestGetProgramsByType(CacheIsolationTestCase):
|
||||
super(TestGetProgramsByType, cls).setUpClass()
|
||||
cls.site = SiteFactory()
|
||||
cls.other_site = SiteFactory()
|
||||
cls.masters_program_1 = ProgramFactory.create(type='Masters')
|
||||
cls.masters_program_2 = ProgramFactory.create(type='Masters')
|
||||
cls.masters_program_other_site = ProgramFactory.create(type='Masters')
|
||||
cls.bachelors_program = ProgramFactory.create(type='Bachelors')
|
||||
cls.no_type_program = ProgramFactory.create(type=None)
|
||||
cls.masters_program_1 = ProgramFactory.create(
|
||||
type='Masters',
|
||||
type_attrs=ProgramTypeAttrsFactory.create(slug="masters")
|
||||
)
|
||||
cls.masters_program_2 = ProgramFactory.create(
|
||||
type='Masters',
|
||||
type_attrs=ProgramTypeAttrsFactory.create(slug="masters")
|
||||
)
|
||||
cls.masters_program_other_site = ProgramFactory.create(
|
||||
type='Masters',
|
||||
type_attrs=ProgramTypeAttrsFactory.create(slug="masters")
|
||||
)
|
||||
cls.bachelors_program = ProgramFactory.create(
|
||||
type='Bachelors',
|
||||
type_attrs=ProgramTypeAttrsFactory.create(slug="bachelors")
|
||||
)
|
||||
cls.no_type_program = ProgramFactory.create(
|
||||
type=None,
|
||||
type_attrs=None
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
""" Loads program data into the cache before each test function. """
|
||||
@@ -865,41 +883,58 @@ class TestGetProgramsByType(CacheIsolationTestCase):
|
||||
cache.set_many(cached_programs, None)
|
||||
|
||||
programs_by_type = defaultdict(list)
|
||||
programs_by_type_slug = defaultdict(list)
|
||||
for program in all_programs:
|
||||
program_type = normalize_program_type(program.get('type'))
|
||||
program_type_slug = (program.get('type_attrs') or {}).get('slug')
|
||||
site_id = self.site.id
|
||||
|
||||
if program == self.masters_program_other_site:
|
||||
site_id = self.other_site.id
|
||||
|
||||
cache_key = PROGRAMS_BY_TYPE_CACHE_KEY_TPL.format(site_id=site_id, program_type=program_type)
|
||||
programs_by_type[cache_key].append(program['uuid'])
|
||||
program_type_cache_key = PROGRAMS_BY_TYPE_CACHE_KEY_TPL.format(
|
||||
site_id=site_id,
|
||||
program_type=program_type
|
||||
)
|
||||
program_type_slug_cache_key = PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL.format(
|
||||
site_id=site_id,
|
||||
program_slug=program_type_slug
|
||||
)
|
||||
programs_by_type[program_type_cache_key].append(program['uuid'])
|
||||
programs_by_type_slug[program_type_slug_cache_key].append(program['uuid'])
|
||||
|
||||
cache.set_many(programs_by_type, None)
|
||||
cache.set_many(programs_by_type_slug, None)
|
||||
|
||||
def test_get_masters_programs(self):
|
||||
expected_programs = [self.masters_program_1, self.masters_program_2]
|
||||
six.assertCountEqual(self, expected_programs, get_programs_by_type(self.site, 'masters'))
|
||||
six.assertCountEqual(self, expected_programs, get_programs_by_type_slug(self.site, 'masters'))
|
||||
|
||||
def test_get_bachelors_programs(self):
|
||||
expected_programs = [self.bachelors_program]
|
||||
self.assertEqual(expected_programs, get_programs_by_type(self.site, 'bachelors'))
|
||||
self.assertEqual(expected_programs, get_programs_by_type_slug(self.site, 'bachelors'))
|
||||
|
||||
def test_get_no_such_type_programs(self):
|
||||
expected_programs = []
|
||||
self.assertEqual(expected_programs, get_programs_by_type(self.site, 'doctorate'))
|
||||
self.assertEqual(expected_programs, get_programs_by_type_slug(self.site, 'doctorate'))
|
||||
|
||||
def test_get_masters_programs_other_site(self):
|
||||
expected_programs = [self.masters_program_other_site]
|
||||
self.assertEqual(expected_programs, get_programs_by_type(self.other_site, 'masters'))
|
||||
self.assertEqual(expected_programs, get_programs_by_type_slug(self.other_site, 'masters'))
|
||||
|
||||
def test_get_programs_null_type(self):
|
||||
expected_programs = [self.no_type_program]
|
||||
self.assertEqual(expected_programs, get_programs_by_type(self.site, None))
|
||||
self.assertEqual(expected_programs, get_programs_by_type_slug(self.site, None))
|
||||
|
||||
def test_get_programs_false_type(self):
|
||||
expected_programs = []
|
||||
self.assertEqual(expected_programs, get_programs_by_type(self.site, False))
|
||||
self.assertEqual(expected_programs, get_programs_by_type_slug(self.site, False))
|
||||
|
||||
def test_normalize_program_type(self):
|
||||
self.assertEqual('none', normalize_program_type(None))
|
||||
|
||||
@@ -23,6 +23,7 @@ from openedx.core.djangoapps.catalog.cache import (
|
||||
PATHWAY_CACHE_KEY_TPL,
|
||||
PROGRAM_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_CACHE_KEY_TPL,
|
||||
PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL,
|
||||
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
|
||||
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL
|
||||
)
|
||||
@@ -163,6 +164,29 @@ def get_programs_by_type(site, program_type):
|
||||
return get_programs_by_uuids(uuids)
|
||||
|
||||
|
||||
def get_programs_by_type_slug(site, program_type_slug):
|
||||
"""
|
||||
Keyword Arguments:
|
||||
site (Site): The corresponding Site object to fetch programs for.
|
||||
program_type_slug (string): The type slug that matching programs must have.
|
||||
|
||||
Returns:
|
||||
A list of programs for the given site with the given type slug.
|
||||
|
||||
Slugs are a consistent identifier whereas type (used in `get_programs_by_type`)
|
||||
may be translated.
|
||||
"""
|
||||
program_type_slug_cache_key = PROGRAMS_BY_TYPE_SLUG_CACHE_KEY_TPL.format(
|
||||
site_id=site.id, program_slug=program_type_slug
|
||||
)
|
||||
uuids = cache.get(program_type_slug_cache_key, [])
|
||||
if not uuids:
|
||||
logger.warning(text_type(
|
||||
'Failed to get program UUIDs from cache for site {} and type slug {}'.format(site.id, program_type_slug)
|
||||
))
|
||||
return get_programs_by_uuids(uuids)
|
||||
|
||||
|
||||
def get_programs_by_uuids(uuids):
|
||||
"""
|
||||
Gets a list of programs for the provided uuids
|
||||
|
||||
30
openedx/core/djangoapps/programs/api.py
Normal file
30
openedx/core/djangoapps/programs/api.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Python APIs exposed by the Programs app to other in-process apps.
|
||||
"""
|
||||
|
||||
from .utils import is_user_enrolled_in_program_type as _is_user_enrolled_in_program_type
|
||||
|
||||
|
||||
def is_user_enrolled_in_program_type(user, program_type_slug, paid_modes=False, enrollments=None, entitlements=None):
|
||||
"""
|
||||
This method will look at the learners Enrollments and Entitlements to determine
|
||||
if a learner is enrolled in a Program of the given type.
|
||||
|
||||
NOTE: This method relies on the Program Cache right now. The goal is to move away from this
|
||||
in the future.
|
||||
|
||||
Arguments:
|
||||
user (User): The user we are looking for.
|
||||
program_type_slug (str): The slug of the Program type we are looking for.
|
||||
paid_modes (bool): Request if the user is enrolled in a Program in a paid mode, False by default.
|
||||
enrollments (List[Dict]): Takes a serialized list of CourseEnrollments linked to the user
|
||||
entitlements (List[CourseEntitlement]): Take a list of CourseEntitlement objects linked to the user
|
||||
|
||||
NOTE: Both enrollments and entitlements will be collected if they are not passed in. They are available
|
||||
as parameters in case they were already collected, to save duplicate queries in high traffic areas.
|
||||
|
||||
Returns:
|
||||
bool: True is the user is enrolled in programs of the requested type
|
||||
"""
|
||||
|
||||
return _is_user_enrolled_in_program_type(user, program_type_slug, paid_modes, enrollments, entitlements)
|
||||
@@ -1587,23 +1587,23 @@ class TestProgramEnrollment(SharedModuleStoreTestCase):
|
||||
|
||||
def test_user_not_in_program(self, mock_get_programs_by_type):
|
||||
mock_get_programs_by_type.return_value = [self.program]
|
||||
self.assertFalse(is_user_enrolled_in_program_type(user=self.user, program_type=self.MICROBACHELORS))
|
||||
self.assertFalse(is_user_enrolled_in_program_type(user=self.user, program_type_slug=self.MICROBACHELORS))
|
||||
|
||||
def test_user_enrolled_in_mb_program(self, mock_get_programs_by_type):
|
||||
CourseEnrollmentFactory.create(user=self.user, course_id=self.course_run.id, mode=CourseMode.VERIFIED)
|
||||
mock_get_programs_by_type.return_value = [self.program]
|
||||
self.assertTrue(is_user_enrolled_in_program_type(user=self.user, program_type=self.MICROBACHELORS))
|
||||
self.assertTrue(is_user_enrolled_in_program_type(user=self.user, program_type_slug=self.MICROBACHELORS))
|
||||
|
||||
def test_user_enrolled_unpaid_in_program(self, mock_get_programs_by_type):
|
||||
CourseEnrollmentFactory.create(user=self.user, course_id=self.course_run.id, mode=CourseMode.AUDIT)
|
||||
mock_get_programs_by_type.return_value = [self.program]
|
||||
self.assertTrue(is_user_enrolled_in_program_type(user=self.user, program_type=self.MICROBACHELORS))
|
||||
self.assertTrue(is_user_enrolled_in_program_type(user=self.user, program_type_slug=self.MICROBACHELORS))
|
||||
|
||||
def test_user_enrolled_unpaid_in_program_paid_only_request(self, mock_get_programs_by_type):
|
||||
CourseEnrollmentFactory.create(user=self.user, course_id=self.course_run.id, mode=CourseMode.AUDIT)
|
||||
mock_get_programs_by_type.return_value = [self.program]
|
||||
self.assertFalse(
|
||||
is_user_enrolled_in_program_type(user=self.user, program_type=self.MICROBACHELORS, paid_modes=True)
|
||||
is_user_enrolled_in_program_type(user=self.user, program_type_slug=self.MICROBACHELORS, paid_modes=True)
|
||||
)
|
||||
|
||||
def test_user_with_entitlement_no_enrollment(self, mock_get_programs_by_type):
|
||||
@@ -1613,4 +1613,4 @@ class TestProgramEnrollment(SharedModuleStoreTestCase):
|
||||
course_uuid=self.program['courses'][0]['uuid']
|
||||
)
|
||||
mock_get_programs_by_type.return_value = [self.program]
|
||||
self.assertTrue(is_user_enrolled_in_program_type(user=self.user, program_type=self.MICROBACHELORS))
|
||||
self.assertTrue(is_user_enrolled_in_program_type(user=self.user, program_type_slug=self.MICROBACHELORS))
|
||||
|
||||
@@ -22,16 +22,17 @@ from pytz import utc
|
||||
from requests.exceptions import ConnectionError, Timeout
|
||||
from six.moves.urllib.parse import urljoin, urlparse, urlunparse # pylint: disable=import-error
|
||||
|
||||
from course_modes.api import get_paid_modes_for_course
|
||||
from course_modes.models import CourseMode
|
||||
from entitlements.api import get_active_entitlement_list_for_user
|
||||
from entitlements.models import CourseEntitlement
|
||||
from lms.djangoapps.certificates import api as certificate_api
|
||||
from lms.djangoapps.certificates.models import GeneratedCertificate
|
||||
from lms.djangoapps.commerce.utils import EcommerceService
|
||||
from openedx.core.djangoapps.catalog.api import get_programs_by_type
|
||||
from openedx.core.djangoapps.catalog.utils import (
|
||||
get_fulfillable_course_runs_for_entitlement,
|
||||
get_programs,
|
||||
get_programs_by_type
|
||||
)
|
||||
from openedx.core.djangoapps.certificates.api import available_date_for_certificate
|
||||
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
|
||||
@@ -901,9 +902,9 @@ class ProgramMarketingDataExtender(ProgramDataExtender):
|
||||
self.instructors.append(instructor)
|
||||
|
||||
|
||||
def is_user_enrolled_in_program_type(user, program_type, paid_modes=False, enrollments=None, entitlements=None):
|
||||
def is_user_enrolled_in_program_type(user, program_type_slug, paid_modes=False, enrollments=None, entitlements=None):
|
||||
"""
|
||||
This method will Look at the learners Enrollments and Entitlements to determine
|
||||
This method will look at the learners Enrollments and Entitlements to determine
|
||||
if a learner is enrolled in a Program of the given type.
|
||||
|
||||
NOTE: This method relies on the Program Cache right now. The goal is to move away from this
|
||||
@@ -911,15 +912,20 @@ def is_user_enrolled_in_program_type(user, program_type, paid_modes=False, enrol
|
||||
|
||||
Arguments:
|
||||
user (User): The user we are looking for.
|
||||
program_type (String): The Program type we are looking for.
|
||||
program_type_slug (str): The slug of the Program type we are looking for.
|
||||
paid_modes (bool): Request if the user is enrolled in a Program in a paid mode, False by default.
|
||||
enrollments (List[Dict]): Takes a serialized list of CourseEnrollments linked to the user
|
||||
entitlements (List[CourseEntitlement]): Take a list of CourseEntitlement objects linked to the user
|
||||
|
||||
NOTE: Both enrollments and entitlements will be collected if they are not passed in. They are available
|
||||
as parameters in case they were already collected, to save duplicate queries in high traffic areas.
|
||||
|
||||
Returns:
|
||||
bool: True is the user is enrolled in programs of the requested Type
|
||||
bool: True is the user is enrolled in programs of the requested type
|
||||
"""
|
||||
course_runs = set()
|
||||
course_uuids = set()
|
||||
programs = get_programs_by_type(Site.objects.get_current(), program_type)
|
||||
programs = get_programs_by_type(Site.objects.get_current(), program_type_slug)
|
||||
if not programs:
|
||||
return False
|
||||
|
||||
@@ -941,7 +947,7 @@ def is_user_enrolled_in_program_type(user, program_type, paid_modes=False, enrol
|
||||
course_run_id = enrollment['course_details']['course_id']
|
||||
if paid_modes:
|
||||
course_run_key = CourseKey.from_string(course_run_id)
|
||||
paid_modes = [mode.slug for mode in CourseMode.paid_modes_for_course(course_run_key)]
|
||||
paid_modes = [mode.slug for mode in get_paid_modes_for_course(course_run_key)]
|
||||
if enrollment['mode'] in paid_modes and course_run_id in course_runs:
|
||||
return True
|
||||
elif course_run_id in course_runs:
|
||||
|
||||
Reference in New Issue
Block a user