Adding email opt in to create entitlement
This commit is contained in:
@@ -20,6 +20,7 @@ from course_modes.tests.factories import CourseModeFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
|
||||
from openedx.core.djangoapps.user_api.models import UserOrgTag
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import (TEST_PASSWORD, CourseEnrollmentFactory, UserFactory)
|
||||
|
||||
@@ -309,6 +310,47 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
|
||||
)
|
||||
assert course_entitlement.policy == policy
|
||||
|
||||
@patch("entitlements.api.v1.views.get_owners_for_course")
|
||||
def test_email_opt_in_single_org(self, mock_get_owners):
|
||||
course_uuid = uuid.uuid4()
|
||||
entitlement_data = self._get_data_set(self.user, str(course_uuid))
|
||||
entitlement_data['email_opt_in'] = True
|
||||
|
||||
org = u'particularly'
|
||||
mock_get_owners.return_value = [{'key': org}]
|
||||
|
||||
response = self.client.post(
|
||||
self.entitlements_list_url,
|
||||
data=json.dumps(entitlement_data),
|
||||
content_type='application/json',
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
result_obj = UserOrgTag.objects.get(user=self.user, org=org, key='email-optin')
|
||||
self.assertEqual(result_obj.value, u"True")
|
||||
|
||||
@patch("entitlements.api.v1.views.get_owners_for_course")
|
||||
def test_email_opt_in_multiple_orgs(self, mock_get_owners):
|
||||
course_uuid = uuid.uuid4()
|
||||
entitlement_data = self._get_data_set(self.user, str(course_uuid))
|
||||
entitlement_data['email_opt_in'] = True
|
||||
|
||||
org_1 = u'particularly'
|
||||
org_2 = u'underwood'
|
||||
mock_get_owners.return_value = [{'key': org_1}, {'key': org_2}]
|
||||
|
||||
response = self.client.post(
|
||||
self.entitlements_list_url,
|
||||
data=json.dumps(entitlement_data),
|
||||
content_type='application/json',
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
result_obj = UserOrgTag.objects.get(user=self.user, org=org_1, key='email-optin')
|
||||
self.assertEqual(result_obj.value, u"True")
|
||||
result_obj = UserOrgTag.objects.get(user=self.user, org=org_2, key='email-optin')
|
||||
self.assertEqual(result_obj.value, u"True")
|
||||
|
||||
def test_add_entitlement_with_support_detail(self):
|
||||
"""
|
||||
Verify that an EntitlementSupportDetail entry is made when the request includes support interaction information.
|
||||
|
||||
@@ -19,9 +19,10 @@ from entitlements.api.v1.permissions import IsAdminOrSupportOrAuthenticatedReadO
|
||||
from entitlements.api.v1.serializers import CourseEntitlementSerializer
|
||||
from entitlements.models import CourseEntitlement, CourseEntitlementPolicy, CourseEntitlementSupportDetail
|
||||
from entitlements.utils import is_course_run_entitlement_fulfillable
|
||||
from openedx.core.djangoapps.catalog.utils import get_course_runs_for_course
|
||||
from openedx.core.djangoapps.catalog.utils import get_course_runs_for_course, get_owners_for_course
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
|
||||
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
|
||||
from student.models import AlreadyEnrolledError, CourseEnrollment, CourseEnrollmentException
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -158,6 +159,8 @@ class EntitlementViewSet(viewsets.ModelViewSet):
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
support_details = request.data.pop('support_details', [])
|
||||
email_opt_in = request.data.pop('email_opt_in', False)
|
||||
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
self.perform_create(serializer)
|
||||
@@ -165,6 +168,12 @@ class EntitlementViewSet(viewsets.ModelViewSet):
|
||||
entitlement = serializer.instance
|
||||
set_entitlement_policy(entitlement, request.site)
|
||||
|
||||
# The owners for a course are the organizations that own the course. By taking owner.key,
|
||||
# we are able to pass in the organization key for email_opt_in
|
||||
owners = get_owners_for_course(entitlement.course_uuid)
|
||||
for owner in owners:
|
||||
update_email_opt_in(entitlement.user, owner['key'], email_opt_in)
|
||||
|
||||
if support_details:
|
||||
for support_detail in support_details:
|
||||
support_detail['entitlement'] = entitlement
|
||||
|
||||
@@ -35,9 +35,10 @@ from openedx.core.djangoapps.catalog.utils import (
|
||||
get_course_runs,
|
||||
get_course_runs_for_course,
|
||||
get_course_run_details,
|
||||
get_pathways,
|
||||
get_currency_data,
|
||||
get_localized_price_text,
|
||||
get_owners_for_course,
|
||||
get_pathways,
|
||||
get_program_types,
|
||||
get_programs,
|
||||
get_visible_sessions_for_entitlement
|
||||
@@ -464,6 +465,31 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase):
|
||||
self.assertEqual(data, catalog_course_runs)
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
|
||||
class TestGetCourseOwners(CatalogIntegrationMixin, TestCase):
|
||||
"""
|
||||
Tests covering retrieval of course runs from the catalog service.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestGetCourseOwners, self).setUp()
|
||||
|
||||
self.catalog_integration = self.create_catalog_integration(cache_ttl=1)
|
||||
self.user = UserFactory(username=self.catalog_integration.service_username)
|
||||
|
||||
def test_get_course_owners_by_course(self, mock_get_edx_api_data):
|
||||
"""
|
||||
Test retrieval of course runs.
|
||||
"""
|
||||
catalog_course_runs = CourseRunFactory.create_batch(10)
|
||||
catalog_course = CourseFactory(course_runs=catalog_course_runs)
|
||||
mock_get_edx_api_data.return_value = catalog_course
|
||||
|
||||
data = get_owners_for_course(course_uuid=str(catalog_course['uuid']))
|
||||
self.assertTrue(mock_get_edx_api_data.called)
|
||||
self.assertEqual(data, catalog_course['owners'])
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
|
||||
class TestSessionEntitlement(CatalogIntegrationMixin, TestCase):
|
||||
|
||||
@@ -37,6 +37,45 @@ def create_catalog_api_client(user, site=None):
|
||||
return EdxRestApiClient(url, jwt=jwt)
|
||||
|
||||
|
||||
def check_catalog_integration_and_get_user(error_message_field):
|
||||
"""
|
||||
Checks that catalog integration is enabled, and if so, attempts to get and
|
||||
return the service user.
|
||||
|
||||
Parameters:
|
||||
error_message_field (str): The field that will be attempted to be
|
||||
retrieved when calling the api client. Used for the error message.
|
||||
|
||||
Returns:
|
||||
Tuple of:
|
||||
The catalog integration service user if it exists, else None
|
||||
The catalog integration Object
|
||||
Note: (This is necessary for future calls of functions using this method)
|
||||
"""
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
|
||||
if catalog_integration.is_enabled():
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
logger.error(
|
||||
'Catalog service user with username [{username}] does not exist. '
|
||||
'{field} will not be retrieved.'.format(
|
||||
username=catalog_integration.service_username,
|
||||
field=error_message_field,
|
||||
)
|
||||
)
|
||||
return None, catalog_integration
|
||||
return user, catalog_integration
|
||||
else:
|
||||
logger.error(
|
||||
'Unable to retrieve details about {field} because Catalog Integration is not enabled'.format(
|
||||
field=error_message_field,
|
||||
)
|
||||
)
|
||||
return None, catalog_integration
|
||||
|
||||
|
||||
def get_programs(site, uuid=None):
|
||||
"""Read programs from the cache.
|
||||
|
||||
@@ -101,13 +140,8 @@ def get_program_types(name=None):
|
||||
list of dict, representing program types.
|
||||
dict, if a specific program type is requested.
|
||||
"""
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
if catalog_integration.enabled:
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
return []
|
||||
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Program types')
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
cache_key = '{base}.program_types'.format(base=catalog_integration.CACHE_KEY)
|
||||
|
||||
@@ -186,13 +220,8 @@ def get_currency_data():
|
||||
list of dict, representing program types.
|
||||
dict, if a specific program type is requested.
|
||||
"""
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
if catalog_integration.enabled:
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
return []
|
||||
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Currency data')
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
cache_key = '{base}.currency'.format(base=catalog_integration.CACHE_KEY)
|
||||
|
||||
@@ -289,18 +318,9 @@ def get_course_runs():
|
||||
Returns:
|
||||
list of dict with each record representing a course run.
|
||||
"""
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
course_runs = []
|
||||
if catalog_integration.enabled:
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
logger.error(
|
||||
'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
|
||||
catalog_integration.service_username,
|
||||
)
|
||||
return course_runs
|
||||
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Course runs')
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
|
||||
querystring = {
|
||||
@@ -314,18 +334,8 @@ def get_course_runs():
|
||||
|
||||
|
||||
def get_course_runs_for_course(course_uuid):
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
|
||||
if catalog_integration.is_enabled():
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
logger.error(
|
||||
'Catalog service user with username [%s] does not exist. Course runs will not be retrieved.',
|
||||
catalog_integration.service_username,
|
||||
)
|
||||
return []
|
||||
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Course runs')
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
cache_key = '{base}.course.{uuid}.course_runs'.format(
|
||||
base=catalog_integration.CACHE_KEY,
|
||||
@@ -345,6 +355,28 @@ def get_course_runs_for_course(course_uuid):
|
||||
return []
|
||||
|
||||
|
||||
def get_owners_for_course(course_uuid):
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Owners')
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
cache_key = '{base}.course.{uuid}.course_runs'.format(
|
||||
base=catalog_integration.CACHE_KEY,
|
||||
uuid=course_uuid
|
||||
)
|
||||
data = get_edx_api_data(
|
||||
catalog_integration,
|
||||
'courses',
|
||||
resource_id=course_uuid,
|
||||
api=api,
|
||||
cache_key=cache_key if catalog_integration.is_cache_enabled else None,
|
||||
long_term_cache=True,
|
||||
many=False
|
||||
)
|
||||
return data.get('owners', [])
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_course_uuid_for_course(course_run_key):
|
||||
"""
|
||||
Retrieve the Course UUID for a given course key
|
||||
@@ -356,18 +388,8 @@ def get_course_uuid_for_course(course_run_key):
|
||||
Returns:
|
||||
UUID: Course UUID and None if it was not retrieved.
|
||||
"""
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
|
||||
if catalog_integration.is_enabled():
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
logger.error(
|
||||
'Catalog service user with username [%s] does not exist. Course UUID will not be retrieved.',
|
||||
catalog_integration.service_username,
|
||||
)
|
||||
return []
|
||||
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(error_message_field='Course UUID')
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
|
||||
run_cache_key = '{base}.course_run.{course_run_key}'.format(
|
||||
@@ -483,27 +505,15 @@ def get_course_run_details(course_run_key, fields):
|
||||
Returns:
|
||||
dict with language, start date, end date, and max_effort details about specified course run
|
||||
"""
|
||||
catalog_integration = CatalogIntegration.current()
|
||||
course_run_details = dict()
|
||||
if catalog_integration.enabled:
|
||||
try:
|
||||
user = catalog_integration.get_service_user()
|
||||
except ObjectDoesNotExist:
|
||||
msg = 'Catalog service user {} does not exist. Data for course_run {} will not be retrieved'.format(
|
||||
catalog_integration.service_username,
|
||||
course_run_key
|
||||
)
|
||||
logger.error(msg)
|
||||
return course_run_details
|
||||
user, catalog_integration = check_catalog_integration_and_get_user(
|
||||
error_message_field='Data for course_run {}'.format(course_run_key)
|
||||
)
|
||||
if user:
|
||||
api = create_catalog_api_client(user)
|
||||
|
||||
cache_key = '{base}.course_runs'.format(base=catalog_integration.CACHE_KEY)
|
||||
|
||||
course_run_details = get_edx_api_data(catalog_integration, 'course_runs', api, resource_id=course_run_key,
|
||||
cache_key=cache_key, many=False, traverse_pagination=False, fields=fields)
|
||||
else:
|
||||
msg = 'Unable to retrieve details about course_run {} because Catalog Integration is not enabled'.format(
|
||||
course_run_key
|
||||
)
|
||||
logger.error(msg)
|
||||
return course_run_details
|
||||
|
||||
@@ -332,9 +332,9 @@ class UpdateEmailOptInTests(ModuleStoreTestCase):
|
||||
"""
|
||||
Test cases to cover API-driven email list opt-in update workflows
|
||||
"""
|
||||
USERNAME = u'frank-underwood'
|
||||
USERNAME = u'claire-underwood'
|
||||
PASSWORD = u'ṕáśśẃőŕd'
|
||||
EMAIL = u'frank+underwood@example.com'
|
||||
EMAIL = u'claire+underwood@example.com'
|
||||
shard = 2
|
||||
|
||||
@ddt.data(
|
||||
|
||||
Reference in New Issue
Block a user