ENT-3367 | Show at most 1 Enterprise Learner Portal dashboard link in the header user-menu. Also, guard against no branding config when getting learner portal data.

This commit is contained in:
Alex Dusenbery
2020-09-16 11:35:52 -04:00
committed by Alex Dusenbery
parent ba9ee4e151
commit 4bb1914ec6
6 changed files with 284 additions and 91 deletions

View File

@@ -8,15 +8,14 @@ from django.urls import reverse
from django.utils.translation import ugettext as _
from lms.djangoapps.ccx.overrides import get_current_ccx
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name, get_enterprise_learner_portals
from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name, get_enterprise_learner_portal
# App that handles subdomain specific branding
from branding import api as branding_api
%>
<%
enterprise_customer_links = get_enterprise_learner_portals(request)
enterprise_customer_link = enterprise_customer_links[0] if enterprise_customer_links else None # Only want to show the first link
enterprise_customer_link = get_enterprise_learner_portal(request)
%>
<h1 class="header-logo">

View File

@@ -12,7 +12,7 @@ from django.utils.translation import ugettext as _
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_urls_for_user
from openedx.core.djangoapps.user_api.accounts.toggles import should_redirect_to_order_history_microfrontend
from openedx.core.djangoapps.user_api.accounts.utils import retrieve_last_sitewide_block_completed
from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name, get_enterprise_learner_portals
from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name, get_enterprise_learner_portal
%>
<%
@@ -22,10 +22,10 @@ profile_image_url = get_profile_image_urls_for_user(self.real_user)['medium']
username = self.real_user.username
resume_block = retrieve_last_sitewide_block_completed(self.real_user)
displayname = get_enterprise_learner_generic_name(request) or username
enterprise_customer_portals = get_enterprise_learner_portals(request)
enterprise_customer_portal = get_enterprise_learner_portal(request)
## Enterprises with the learner portal enabled should not show order history, as it does
## not apply to the learner's method of purchasing content.
should_show_order_history = should_redirect_to_order_history_microfrontend() and not enterprise_customer_portals
should_show_order_history = should_redirect_to_order_history_microfrontend() and not enterprise_customer_portal
%>
<div class="nav-item hidden-mobile">
@@ -43,12 +43,10 @@ should_show_order_history = should_redirect_to_order_history_microfrontend() and
% if resume_block:
<div class="mobile-nav-item dropdown-item dropdown-nav-item"><a href="${resume_block}" role="menuitem">${_("Resume your last course")}</a></div>
% endif
% if not enterprise_customer_portals:
% if not enterprise_customer_portal:
<div class="mobile-nav-item dropdown-item dropdown-nav-item"><a href="${reverse('dashboard')}" role="menuitem">${_("Dashboard")}</a></div>
% else:
% for portal in enterprise_customer_portals:
<div class="mobile-nav-item dropdown-item dropdown-nav-item"><a href="${settings.ENTERPRISE_LEARNER_PORTAL_BASE_URL}/${portal.get('slug')}" role="menuitem">${_("{name} Dashboard").format(name=portal.get('name'))}</a></div>
% endfor
<div class="mobile-nav-item dropdown-item dropdown-nav-item"><a href="${settings.ENTERPRISE_LEARNER_PORTAL_BASE_URL}/${enterprise_customer_portal.get('slug')}" role="menuitem">${_("Dashboard")}</a></div>
% endif
<div class="mobile-nav-item dropdown-item dropdown-nav-item"><a href="${reverse('learner_profile', kwargs={'username': username})}" role="menuitem">${_("Profile")}</a></div>

View File

@@ -43,6 +43,10 @@ except ImportError:
CONSENT_FAILED_PARAMETER = 'consent_failed'
LOGGER = logging.getLogger("edx.enterprise_helpers")
ENTERPRISE_CUSTOMER_KEY_NAME = 'enterprise_customer'
# See https://open-edx-proposals.readthedocs.io/en/latest/oep-0022-bp-django-caches.html#common-caching-defect-and-fix
_CACHE_MISS = '__CACHE_MISS__'
class EnterpriseApiException(Exception):
@@ -283,7 +287,7 @@ class EnterpriseApiServiceClient(EnterpriseServiceClientMixin, EnterpriseApiClie
API response`.
"""
enterprise_customer = enterprise_customer_from_cache(uuid=uuid)
if not enterprise_customer:
if enterprise_customer is _CACHE_MISS:
endpoint = getattr(self.client, 'enterprise-customer')
enterprise_customer = endpoint(uuid).get()
if enterprise_customer:
@@ -348,7 +352,6 @@ def enterprise_is_enabled(otherwise=None):
return decorator
@enterprise_is_enabled()
def get_enterprise_customer_cache_key(uuid, username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME):
"""The cache key used to get cached Enterprise Customer data."""
return get_cache_key(
@@ -358,28 +361,63 @@ def get_enterprise_customer_cache_key(uuid, username=settings.ENTERPRISE_SERVICE
)
@enterprise_is_enabled()
def cache_enterprise(enterprise_customer):
"""Cache this customer's data."""
"""Add this customer's data to the Django cache."""
cache_key = get_enterprise_customer_cache_key(enterprise_customer['uuid'])
cache.set(cache_key, enterprise_customer, settings.ENTERPRISE_API_CACHE_TIMEOUT)
@enterprise_is_enabled()
def enterprise_customer_from_cache(request=None, uuid=None):
"""Check all available caches for Enterprise Customer data."""
enterprise_customer = None
def enterprise_customer_from_cache(uuid):
"""
Retrieve enterprise customer data associated with the given ``uuid`` from the Django cache,
returning a ``__CACHE_MISS__`` if absent.
"""
cache_key = get_enterprise_customer_cache_key(uuid)
return cache.get(cache_key, _CACHE_MISS)
# Check if it's cached in the general cache storage.
if uuid:
cache_key = get_enterprise_customer_cache_key(uuid)
enterprise_customer = cache.get(cache_key)
# Check if it's cached in the session.
if not enterprise_customer and request:
enterprise_customer = request.session.get('enterprise_customer')
def add_enterprise_customer_to_session(request, enterprise_customer):
""" Add the given enterprise_customer data to the request's session. """
request.session[ENTERPRISE_CUSTOMER_KEY_NAME] = enterprise_customer
return enterprise_customer
def enterprise_customer_from_session(request):
"""
Retrieve enterprise_customer data from the request's session,
returning a ``__CACHE_MISS__`` if absent.
"""
return request.session.get(ENTERPRISE_CUSTOMER_KEY_NAME, _CACHE_MISS)
def enterprise_customer_uuid_from_session(request):
"""
Retrieve an enterprise customer UUID from the request's session,
returning a ``__CACHE_MISS__`` if absent. Note that this may
return ``None``, which indicates that we've previously looked
for an associated customer for this request's user, and
none was present.
"""
customer_data = enterprise_customer_from_session(request)
if customer_data is not _CACHE_MISS:
customer_data = customer_data or {}
return customer_data.get('uuid')
return _CACHE_MISS
def enterprise_customer_uuid_from_query_param(request):
"""
Returns an enterprise customer UUID from the given request's GET data,
or ``__CACHE_MISS__`` if not present.
"""
return request.GET.get(ENTERPRISE_CUSTOMER_KEY_NAME, _CACHE_MISS)
def enterprise_customer_uuid_from_cookie(request):
"""
Returns an enterprise customer UUID from the given request's cookies,
or ``__CACHE_MISS__`` if not present.
"""
return request.COOKIES.get(settings.ENTERPRISE_CUSTOMER_COOKIE_NAME, _CACHE_MISS)
@enterprise_is_enabled()
@@ -430,34 +468,56 @@ def enterprise_customer_uuid_for_request(request):
except EnterpriseCustomer.DoesNotExist:
enterprise_customer_uuid = None
else:
# Check if we got an Enterprise UUID passed directly as either a query
# parameter, or as a value in the Enterprise cookie.
enterprise_customer_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(
settings.ENTERPRISE_CUSTOMER_COOKIE_NAME
)
enterprise_customer_uuid = _customer_uuid_from_query_param_cookies_or_session(request)
if not enterprise_customer_uuid and request.user.is_authenticated:
if enterprise_customer_uuid is _CACHE_MISS and request.user.is_authenticated:
# If there's no way to get an Enterprise UUID for the request, check to see
# if there's already an Enterprise attached to the requesting user on the backend.
learner_data = get_enterprise_learner_data_from_db(request.user)
if learner_data:
enterprise_customer_uuid = learner_data[0]['enterprise_customer']['uuid']
enterprise_customer = learner_data[0]['enterprise_customer']
enterprise_customer_uuid = enterprise_customer['uuid']
cache_enterprise(enterprise_customer)
else:
enterprise_customer_uuid = None
# Now that we've asked the database for this users's enterprise customer data,
# add it to their session (even if it's null/empty, which indicates the user
# has no associated enterprise customer).
add_enterprise_customer_to_session(request, enterprise_customer)
return enterprise_customer_uuid
def _customer_uuid_from_query_param_cookies_or_session(request):
"""
Helper function that plucks a customer UUID out of the given requests's
query params, cookie, or session data.
Returns ``__CACHE_MISS__`` if none of those keys are present in the request.
"""
for function in (
enterprise_customer_uuid_from_query_param,
enterprise_customer_uuid_from_cookie,
enterprise_customer_uuid_from_session,
):
enterprise_customer_uuid = function(request)
if enterprise_customer_uuid is not _CACHE_MISS:
return enterprise_customer_uuid
return _CACHE_MISS
@enterprise_is_enabled()
def enterprise_customer_for_request(request):
"""
Check all the context clues of the request to determine if
the request being made is tied to a particular EnterpriseCustomer.
"""
if 'enterprise_customer' in request.session and request.session['enterprise_customer']:
return enterprise_customer_from_cache(request=request)
else:
enterprise_customer = enterprise_customer_from_session(request)
if enterprise_customer is _CACHE_MISS:
enterprise_customer = enterprise_customer_from_api(request)
request.session['enterprise_customer'] = enterprise_customer
return enterprise_customer
add_enterprise_customer_to_session(request, enterprise_customer)
return enterprise_customer
@enterprise_is_enabled(otherwise=False)
@@ -587,19 +647,21 @@ def get_enterprise_learner_portal_enabled_message(request):
"""
Returns message to be displayed in dashboard if the user is linked to an Enterprise with the Learner Portal enabled.
Note: request.session['enterprise_customer'] will be used in case the user is linked to
Note: request.session[ENTERPRISE_CUSTOMER_KEY_NAME] will be used in case the user is linked to
multiple Enterprises. Otherwise, it won't exist and the Enterprise Learner data
will be used. If that doesn't exist return None.
Args:
request: request made to the LMS dashboard
"""
if 'enterprise_customer' in request.session and request.session['enterprise_customer']:
enterprise_customer = request.session['enterprise_customer']
else:
enterprise_customer = enterprise_customer_from_session(request)
if enterprise_customer is _CACHE_MISS:
learner_data = get_enterprise_learner_data_from_db(request.user)
if learner_data:
enterprise_customer = learner_data[0]['enterprise_customer']
enterprise_customer = learner_data[0]['enterprise_customer'] if learner_data else None
# Add to session cache regardless of whether it is null
add_enterprise_customer_to_session(request, enterprise_customer)
if enterprise_customer:
cache_enterprise(enterprise_customer)
else:
return None

View File

@@ -172,7 +172,13 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, CacheIsolationTestCase):
@mock.patch('openedx.features.enterprise_support.api.get_enterprise_learner_data_from_db')
def test_consent_needed_for_course(self, mock_get_enterprise_learner_data):
user = UserFactory(username='janedoe')
request = mock.MagicMock(session={}, user=user, site=SiteFactory(domain="example.com"))
request = mock.MagicMock(
user=user,
site=SiteFactory(domain="example.com"),
session={},
COOKIES={},
GET={},
)
ec_uuid = 'cf246b88-d5f6-4908-a522-fc307e0b0c59'
course_id = 'fake-course'
mock_get_enterprise_learner_data.return_value = self.get_mock_enterprise_learner_results()
@@ -266,28 +272,55 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, CacheIsolationTestCase):
# the third-party auth pipeline has no `provider_id`.
mock_registry.get_from_pipeline.return_value.provider_id = None
self.mock_get_enterprise_customer('real-ent-uuid', {'real': 'enterprisecustomer'}, 200)
enterprise_customer = enterprise_customer_for_request(
mock.MagicMock(GET={'enterprise_customer': 'real-ent-uuid'}, user=self.user)
mock_request = mock.MagicMock(
GET={'enterprise_customer': 'real-ent-uuid'},
COOKIES={},
session={},
user=self.user
)
enterprise_customer = enterprise_customer_for_request(mock_request)
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
# Verify that the method `enterprise_customer_for_request` returns
# expected enterprise customer against the requesting user even if
# the third-party auth pipeline has no `provider_id` but there is
# enterprise customer UUID in the cookie.
enterprise_customer = enterprise_customer_for_request(
mock.MagicMock(GET={}, COOKIES={settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'}, user=self.user)
mock_request = mock.MagicMock(
GET={},
COOKIES={settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'},
session={},
user=self.user
)
enterprise_customer = enterprise_customer_for_request(mock_request)
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
# Verify that the method `enterprise_customer_for_request` returns
# expected enterprise customer against the requesting user if
# data is cached only in the request session
mock_registry.get_from_pipeline.return_value.provider_id = None
self.mock_get_enterprise_customer('real-ent-uuid', {'real': 'enterprisecustomer'}, 200)
mock_request = mock.MagicMock(
GET={},
COOKIES={},
session={'enterprise_customer': {'real': 'enterprisecustomer'}},
user=self.user
)
enterprise_customer = enterprise_customer_for_request(mock_request)
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
# Verify that we can still get enterprise customer from enterprise
# learner API even if we are unable to get it from preferred sources,
# e.g. url query parameters, third-party auth pipeline, enterprise
# cookie.
# cookie, or session.
mock_get_enterprise_learner_data.return_value = [{'enterprise_customer': {'uuid': 'real-ent-uuid'}}]
enterprise_customer = enterprise_customer_for_request(
mock.MagicMock(GET={}, COOKIES={}, user=self.user, site=1)
mock_request = mock.MagicMock(
GET={},
COOKIES={},
session={},
user=self.user,
site=1
)
enterprise_customer = enterprise_customer_for_request(mock_request)
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
def test_enterprise_customer_for_request_with_session(self):
@@ -312,13 +345,13 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, CacheIsolationTestCase):
'openedx.features.enterprise_support.api.enterprise_customer_from_api',
return_value=enterprise_data
) as mock_enterprise_customer_from_api, mock.patch(
'openedx.features.enterprise_support.api.enterprise_customer_from_cache',
'openedx.features.enterprise_support.api.enterprise_customer_from_session',
return_value=enterprise_data
) as mock_enterprise_customer_from_cache:
) as mock_enterprise_customer_from_session:
enterprise_customer = enterprise_customer_for_request(dummy_request)
self.assertEqual(enterprise_customer, enterprise_data)
self.assertEqual(mock_enterprise_customer_from_api.called, False)
self.assertEqual(mock_enterprise_customer_from_cache.called, True)
self.assertEqual(mock_enterprise_customer_from_session.called, True)
# Verify enterprise customer data fetched from session for subsequent calls
# with unauthenticated user in SAML case
@@ -328,13 +361,13 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, CacheIsolationTestCase):
'openedx.features.enterprise_support.api.enterprise_customer_from_api',
return_value=enterprise_data
) as mock_enterprise_customer_from_api, mock.patch(
'openedx.features.enterprise_support.api.enterprise_customer_from_cache',
'openedx.features.enterprise_support.api.enterprise_customer_from_session',
return_value=enterprise_data
) as mock_enterprise_customer_from_cache:
) as mock_enterprise_customer_from_session:
enterprise_customer = enterprise_customer_for_request(dummy_request)
self.assertEqual(enterprise_customer, enterprise_data)
self.assertEqual(mock_enterprise_customer_from_api.called, False)
self.assertEqual(mock_enterprise_customer_from_cache.called, True)
self.assertEqual(mock_enterprise_customer_from_session.called, True)
def check_data_sharing_consent(self, consent_required=False, consent_url=None):
"""

View File

@@ -11,7 +11,7 @@ from django.test.utils import override_settings
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from openedx.features.enterprise_support.utils import ENTERPRISE_HEADER_LINKS, get_enterprise_learner_portals
from openedx.features.enterprise_support.utils import ENTERPRISE_HEADER_LINKS, get_enterprise_learner_portal
from openedx.features.enterprise_support.tests import FEATURES_WITH_ENTERPRISE_ENABLED
from openedx.features.enterprise_support.tests.factories import (
EnterpriseCustomerBrandingConfigurationFactory, EnterpriseCustomerUserFactory,
@@ -49,9 +49,10 @@ class TestEnterpriseUtils(TestCase):
self.assertEqual(mock_customer_request.call_count, expected_calls)
@override_waffle_flag(ENTERPRISE_HEADER_LINKS, True)
def test_get_enterprise_learner_portals_uncached(self):
def test_get_enterprise_learner_portal_uncached(self):
"""
Test that only enabled enterprise portals are returned
Test that only an enabled enterprise portal is returned,
and that it matches the customer UUID provided in the request.
"""
enterprise_customer_user = EnterpriseCustomerUserFactory(active=True, user_id=self.user.id)
EnterpriseCustomerBrandingConfigurationFactory(
@@ -61,24 +62,88 @@ class TestEnterpriseUtils(TestCase):
enterprise_customer_user.enterprise_customer.save()
request = mock.MagicMock(session={}, user=self.user)
portals = get_enterprise_learner_portals(request)
self.assertEqual(len(portals), 1)
self.assertDictEqual(portals[0], {
# Indicate the "preferred" customer in the request
request.GET = {'enterprise_customer': enterprise_customer_user.enterprise_customer.uuid}
# Create another enterprise customer association for the same user.
# There should be no data returned for this customer's portal,
# because we filter for only the enterprise customer uuid found in the request.
other_enterprise_customer_user = EnterpriseCustomerUserFactory(active=True, user_id=self.user.id)
other_enterprise_customer_user.enable_learner_portal = True
other_enterprise_customer_user.save()
portal = get_enterprise_learner_portal(request)
self.assertDictEqual(portal, {
'name': enterprise_customer_user.enterprise_customer.name,
'slug': enterprise_customer_user.enterprise_customer.slug,
'logo': enterprise_customer_user.enterprise_customer.branding_configuration.logo.url,
})
@override_waffle_flag(ENTERPRISE_HEADER_LINKS, True)
def test_get_enterprise_learner_portals_cached(self):
def test_get_enterprise_learner_portal_no_branding_config(self):
"""
Test that only an enabled enterprise portal is returned,
and that it matches the customer UUID provided in the request,
even if no branding config is associated with the customer.
"""
enterprise_customer_user = EnterpriseCustomerUserFactory.create(active=True, user_id=self.user.id)
enterprise_customer_user.enterprise_customer.enable_learner_portal = True
enterprise_customer_user.enterprise_customer.save()
request = mock.MagicMock(session={}, user=self.user)
# Indicate the "preferred" customer in the request
request.GET = {'enterprise_customer': enterprise_customer_user.enterprise_customer.uuid}
portal = get_enterprise_learner_portal(request)
self.assertDictEqual(portal, {
'name': enterprise_customer_user.enterprise_customer.name,
'slug': enterprise_customer_user.enterprise_customer.slug,
'logo': None,
})
@override_waffle_flag(ENTERPRISE_HEADER_LINKS, True)
def test_get_enterprise_learner_portal_no_customer_from_request(self):
"""
Test that only one enabled enterprise portal is returned,
even if enterprise_customer_uuid_from_request() returns None.
"""
# Create another enterprise customer association for the same user.
# There should be no data returned for this customer's portal,
# because another customer is later created with a more recent active/modified time.
other_enterprise_customer_user = EnterpriseCustomerUserFactory(active=True, user_id=self.user.id)
other_enterprise_customer_user.enable_learner_portal = True
other_enterprise_customer_user.save()
enterprise_customer_user = EnterpriseCustomerUserFactory(active=True, user_id=self.user.id)
EnterpriseCustomerBrandingConfigurationFactory(
enterprise_customer=enterprise_customer_user.enterprise_customer,
)
enterprise_customer_user.enterprise_customer.enable_learner_portal = True
enterprise_customer_user.enterprise_customer.save()
request = mock.MagicMock(session={}, user=self.user)
with mock.patch(
'openedx.features.enterprise_support.api.enterprise_customer_uuid_for_request',
return_value=None,
):
portal = get_enterprise_learner_portal(request)
self.assertDictEqual(portal, {
'name': enterprise_customer_user.enterprise_customer.name,
'slug': enterprise_customer_user.enterprise_customer.slug,
'logo': enterprise_customer_user.enterprise_customer.branding_configuration.logo.url,
})
@override_waffle_flag(ENTERPRISE_HEADER_LINKS, True)
def test_get_enterprise_learner_portal_cached(self):
enterprise_customer_data = {
'name': 'Enabled Customer',
'slug': 'enabled_customer',
'logo': 'https://logo.url',
}
request = mock.MagicMock(session={
'enterprise_learner_portals': json.dumps([enterprise_customer_data])
'enterprise_learner_portal': json.dumps(enterprise_customer_data)
}, user=self.user)
portals = get_enterprise_learner_portals(request)
self.assertEqual(len(portals), 1)
self.assertDictEqual(portals[0], enterprise_customer_data)
portal = get_enterprise_learner_portal(request)
self.assertDictEqual(portal, enterprise_customer_data)

View File

@@ -10,6 +10,7 @@ from django.conf import settings
from django.urls import NoReverseMatch, reverse
from django.utils.translation import ugettext as _
from edx_django_utils.cache import TieredCache, get_cache_key
from enterprise.api.v1.serializers import EnterpriseCustomerBrandingConfigurationSerializer
from enterprise.models import EnterpriseCustomerUser, EnterpriseCustomer
from social_django.models import UserSocialAuth
@@ -293,42 +294,77 @@ def _get_sync_learner_profile_data(enterprise_customer):
return False
def get_enterprise_learner_portals(request):
def get_enterprise_learner_portal(request):
"""
Gets the formatted portal names and slugs that can be used
to generate links for enabled enterprise Learner Portals.
Gets the formatted portal name and slug that can be used
to generate a link for an enabled enterprise Learner Portal.
Caches and returns results in/from the user's request session if provided.
Caches and returns result in/from the user's request session if provided.
"""
# Prevent a circular import.
from openedx.features.enterprise_support.api import enterprise_enabled
from openedx.features.enterprise_support.api import enterprise_enabled, enterprise_customer_uuid_for_request
user = request.user
# Only cache this if a learner is authenticated (AnonymousUser exists and should not be tracked)
learner_portal_session_key = 'enterprise_learner_portal'
if enterprise_enabled() and ENTERPRISE_HEADER_LINKS.is_enabled() and user and user.id:
# If the key exists return that value
if 'enterprise_learner_portals' in request.session:
return json.loads(request.session['enterprise_learner_portals'])
# Ordering is important, this is consistent with how we decide on which
# enterprise_customer is the selected one for an enterprise_customer
enterprise_learner_portals = [{
'name': enterprise_customer_user.enterprise_customer.name,
'slug': enterprise_customer_user.enterprise_customer.slug,
'logo': enterprise_customer_user.enterprise_customer.branding_configuration.logo.url if
enterprise_customer_user.enterprise_customer.branding_configuration else None,
} for enterprise_customer_user in EnterpriseCustomerUser.objects.filter(
user_id=user.id, enterprise_customer__enable_learner_portal=True
).prefetch_related(
'enterprise_customer', 'enterprise_customer__branding_configuration'
).order_by('-enterprise_customer__active', '-modified')]
if learner_portal_session_key in request.session:
return json.loads(request.session[learner_portal_session_key])
kwargs = {
'user_id': user.id,
'enterprise_customer__enable_learner_portal': True,
}
enterprise_customer_uuid = enterprise_customer_uuid_for_request(request)
if enterprise_customer_uuid:
kwargs['enterprise_customer__uuid'] = enterprise_customer_uuid
queryset = EnterpriseCustomerUser.objects.filter(**kwargs).prefetch_related(
'enterprise_customer',
'enterprise_customer__branding_configuration',
)
if not enterprise_customer_uuid:
# If the request doesn't help us know which Enterprise Customer UUID to select with,
# order by the most recently activated/modified customers,
# so that when we select the first result of the query as the preferred
# customer, it's the most recently active one.
queryset = queryset.order_by('-enterprise_customer__active', '-modified')
preferred_enterprise_customer_user = queryset.first()
if not preferred_enterprise_customer_user:
return None
enterprise_customer = preferred_enterprise_customer_user.enterprise_customer
learner_portal_data = {
'name': enterprise_customer.name,
'slug': enterprise_customer.slug,
'logo': enterprise_branding_configuration(enterprise_customer).get('logo'),
}
# Cache the result in the user's request session
request.session['enterprise_learner_portals'] = json.dumps(enterprise_learner_portals)
return enterprise_learner_portals
request.session[learner_portal_session_key] = json.dumps(learner_portal_data)
return learner_portal_data
return None
def enterprise_branding_configuration(enterprise_customer_obj):
"""
Given an instance of ``EnterpriseCustomer``, returns a related
branding_configuration serialized dictionary if it exists, otherwise an empty dictionary.
"""
# We can use hasattr() on one-to-one relationships to avoid exception-catching:
# https://docs.djangoproject.com/en/2.2/topics/db/examples/one_to_one/
if not hasattr(enterprise_customer_obj, 'branding_configuration'):
return {}
branding_config = enterprise_customer_obj.branding_configuration
return EnterpriseCustomerBrandingConfigurationSerializer(branding_config).data
def get_enterprise_learner_generic_name(request):
"""
Get a generic name concatenating the Enterprise Customer name and 'Learner'.