Skip to content

Commit

Permalink
Merge branch 'master' of github.com:openedx/enterprise-catalog into h…
Browse files Browse the repository at this point in the history
…u/ent-8870
  • Loading branch information
brobro10000 committed Aug 20, 2024
2 parents 90d87e1 + ac1a3a4 commit 90f7681
Show file tree
Hide file tree
Showing 8 changed files with 746 additions and 536 deletions.
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: "2.1"
services:
mysql:
image: mysql:8.0.28-oracle
Expand Down
600 changes: 600 additions & 0 deletions enterprise_catalog/apps/api/v1/tests/test_enterprise_customer_views.py

Large diffs are not rendered by default.

546 changes: 13 additions & 533 deletions enterprise_catalog/apps/api/v1/tests/test_views.py

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion enterprise_catalog/apps/api/v1/views/enterprise_customer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import uuid
from collections import defaultdict

from django.utils.decorators import method_decorator
from edx_rbac.utils import get_decoded_jwt
Expand All @@ -14,6 +15,10 @@
from enterprise_catalog.apps.api.v1.serializers import ContentMetadataSerializer
from enterprise_catalog.apps.api.v1.utils import unquote_course_keys
from enterprise_catalog.apps.api.v1.views.base import BaseViewSet
from enterprise_catalog.apps.catalog.constants import (
COURSE_RUN_KEY_PREFIX,
RESTRICTED_RUNS_ALLOWED_KEY,
)
from enterprise_catalog.apps.catalog.models import EnterpriseCatalog


Expand Down Expand Up @@ -101,7 +106,7 @@ def contains_content_items(self, request, enterprise_uuid, course_run_ids, progr
f'Error: invalid enterprice customer uuid: "{enterprise_uuid}" provided.',
status=HTTP_400_BAD_REQUEST
)
customer_catalogs = EnterpriseCatalog.objects.filter(enterprise_uuid=enterprise_uuid)
customer_catalogs = list(EnterpriseCatalog.objects.filter(enterprise_uuid=enterprise_uuid))

any_catalog_contains_content_items = False
catalogs_that_contain_course = []
Expand All @@ -119,8 +124,32 @@ def contains_content_items(self, request, enterprise_uuid, course_run_ids, progr
}
if (get_catalogs_containing_specified_content_ids or get_catalog_list):
response_data['catalog_list'] = catalogs_that_contain_course

response_data[RESTRICTED_RUNS_ALLOWED_KEY] = self._get_restricted_runs_allowed_for_query(
course_run_ids, customer_catalogs,
)
return Response(response_data)

def _get_restricted_runs_allowed_for_query(self, course_run_ids, customer_catalogs):
# filter the set of restricted course keys down to only
# those requested by the client, and only if those requested keys
# are top-level course keys (NOT course run keys).
requested_course_keys = {
key for key in course_run_ids if not key.startswith(COURSE_RUN_KEY_PREFIX)
}
serialized_data = defaultdict(lambda: defaultdict(lambda: {'catalog_uuids': set()}))
for catalog in customer_catalogs:
if not catalog.restricted_runs_allowed:
continue
for restricted_course_key, restricted_runs in catalog.restricted_runs_allowed.items():
if restricted_course_key not in requested_course_keys:
continue
course_dict = serialized_data[restricted_course_key]
for course_run_key in restricted_runs:
run_dict = course_dict[course_run_key]
run_dict['catalog_uuids'].add(str(catalog.uuid))
return serialized_data or None

@action(detail=True, methods=['post'])
def filter_content_items(self, request, enterprise_uuid, **kwargs):
"""
Expand Down
3 changes: 2 additions & 1 deletion enterprise_catalog/apps/catalog/algolia_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
LEARNER_PATHWAY,
PROGRAM,
PROGRAM_TYPES_MAP,
VIDEO, LATE_ENROLLMENT_THRESHOLD_DAYS,
VIDEO,
LATE_ENROLLMENT_THRESHOLD_DAYS,
)
from enterprise_catalog.apps.catalog.models import ContentMetadata
from enterprise_catalog.apps.catalog.utils import batch_by_pk, localized_utcnow, to_timestamp, offset_localized_utcnow
Expand Down
7 changes: 7 additions & 0 deletions enterprise_catalog/apps/catalog/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@
# Late enrollment threshold
LATE_ENROLLMENT_THRESHOLD_DAYS = 30

RESTRICTED_RUNS_ALLOWED_KEY = 'restricted_runs_allowed'

AGGREGATION_KEY_PREFIX = 'course:'

COURSE_RUN_KEY_PREFIX = 'course-v1:'


def json_serialized_course_modes():
"""
:return: serialized course modes.
Expand Down
28 changes: 28 additions & 0 deletions enterprise_catalog/apps/catalog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)
from enterprise_catalog.apps.catalog.constants import (
ACCESS_TO_ALL_ENTERPRISES_TOKEN,
AGGREGATION_KEY_PREFIX,
CONTENT_COURSE_TYPE_ALLOW_LIST,
CONTENT_PRODUCT_SOURCE_ALLOW_LIST,
CONTENT_TYPE_CHOICES,
Expand All @@ -34,6 +35,7 @@
EXEC_ED_2U_COURSE_TYPE,
EXEC_ED_2U_ENTITLEMENT_MODE,
PROGRAM,
RESTRICTED_RUNS_ALLOWED_KEY,
json_serialized_course_modes,
)
from enterprise_catalog.apps.catalog.utils import (
Expand Down Expand Up @@ -116,6 +118,28 @@ def pretty_print_content_filter(self):
"""
return json.dumps(self.content_filter, indent=4)

@cached_property
def restricted_runs_allowed(self):
"""
Return a dict of restricted course <-> run mappings by
course key, e.g.
```
"edX+FUN": [
"course-v1:edX+FUN+3T2024"
]
```
"""
mapping = self.content_filter.get(RESTRICTED_RUNS_ALLOWED_KEY) # pylint: disable=no-member
if not mapping:
return None
if not isinstance(mapping, dict):
LOGGER.error('%s restricted runs value is not a dict', self)
return None
return {
course_key.removeprefix(AGGREGATION_KEY_PREFIX): course_run_list
for course_key, course_run_list in mapping.items()
}

@classmethod
def get_by_uuid(cls, uuid):
try:
Expand Down Expand Up @@ -211,6 +235,10 @@ def content_metadata(self):
return ContentMetadata.objects.none()
return self.catalog_query.contentmetadata_set.all()

@cached_property
def restricted_runs_allowed(self):
return self.catalog_query.restricted_runs_allowed

@cached_property
def enterprise_customer(self):
"""
Expand Down
66 changes: 66 additions & 0 deletions enterprise_catalog/apps/catalog/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
EXEC_ED_2U_COURSE_TYPE,
EXEC_ED_2U_ENTITLEMENT_MODE,
PROGRAM,
RESTRICTED_RUNS_ALLOWED_KEY,
)
from enterprise_catalog.apps.catalog.models import (
ContentMetadata,
Expand Down Expand Up @@ -590,3 +591,68 @@ def test_bulk_update_changes_modified_time(self):
for record in records:
record.refresh_from_db()
self.assertGreater(record.modified, original_modified_time)

def test_restricted_runs_allowed_happy_path(self):
"""
Test the happy path for computing a CatalogQuery's `restricted_runs_allowed`.
"""
restricted_runs_dict = {
"course:edX+FUN": [
"course-v1:edX+FUN+3T2024",
"course-v1:edX+FUN+4T2024",
],
"course:edX+GAMES": [
"course-v1:edX+GAMES+3T2024",
"course-v1:edX+GAMES+4T2024",
]
}
content_filter = {
'other': 'stuff',
RESTRICTED_RUNS_ALLOWED_KEY: restricted_runs_dict
}
catalog_query = factories.CatalogQueryFactory(content_filter=content_filter)
catalog = factories.EnterpriseCatalogFactory(
catalog_query=catalog_query,
)

expected_restricted_runs_dict = {
"edX+FUN": [
"course-v1:edX+FUN+3T2024",
"course-v1:edX+FUN+4T2024",
],
"edX+GAMES": [
"course-v1:edX+GAMES+3T2024",
"course-v1:edX+GAMES+4T2024",
]
}
self.assertEqual(
expected_restricted_runs_dict,
catalog_query.restricted_runs_allowed,
)
self.assertEqual(
expected_restricted_runs_dict,
catalog.restricted_runs_allowed,
)

@ddt.data(
['some+course+run'],
'some+course+run',
{},
[],
'',
)
def test_restricted_runs_are_none(self, restricted_runs_dict):
"""
Tests all the cases that should result in a restricted_runs_allowed of None.
"""
content_filter = {
'other': 'stuff',
RESTRICTED_RUNS_ALLOWED_KEY: restricted_runs_dict
}
catalog_query = factories.CatalogQueryFactory(content_filter=content_filter)
catalog = factories.EnterpriseCatalogFactory(
catalog_query=catalog_query,
)

self.assertIsNone(catalog_query.restricted_runs_allowed)
self.assertIsNone(catalog.restricted_runs_allowed)

0 comments on commit 90f7681

Please sign in to comment.