Skip to content

Commit 93642d4

Browse files
authored
Merge pull request #963 from openedx/pwnage101/ENT-9569
Data model for explicit storage of restricted runs and their courses
2 parents 53317a1 + 98c3353 commit 93642d4

13 files changed

+1062
-79
lines changed

.annotation_safe_list.yml

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ catalog.HistoricalContentMetadata:
2121
".. no_pii": "This model has no PII"
2222
catalog.HistoricalEnterpriseCatalog:
2323
".. no_pii": "This model has no PII"
24+
catalog.HistoricalRestrictedCourseMetadata:
25+
".. no_pii": "This model has no PII"
2426
contenttypes.ContentType:
2527
".. no_pii:": "This model has no PII"
2628
curation.HistoricalEnterpriseCurationConfig:

enterprise_catalog/apps/api/tasks.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def _update_full_content_metadata_course(content_keys, dry_run=False):
287287
else:
288288
ContentMetadata.objects.bulk_update(
289289
modified_content_metadata_records,
290-
['json_metadata'],
290+
['_json_metadata'],
291291
batch_size=10,
292292
)
293293

@@ -326,13 +326,14 @@ def _update_single_full_course_record(course_metadata_dict, metadata_record, cou
326326
for that course.
327327
"""
328328
# Merge the full metadata from discovery's /api/v1/courses into the local metadata object.
329-
metadata_record.json_metadata.update(course_metadata_dict)
329+
metadata_record._json_metadata.update(course_metadata_dict) # pylint: disable=protected-access
330330

331331
_normalize_metadata_record(metadata_record)
332332

333333
if course_review:
334-
metadata_record.json_metadata['reviews_count'] = course_review.get('reviews_count')
335-
metadata_record.json_metadata['avg_course_rating'] = course_review.get('avg_course_rating')
334+
# pylint: disable=protected-access
335+
metadata_record._json_metadata['reviews_count'] = course_review.get('reviews_count')
336+
metadata_record._json_metadata['avg_course_rating'] = course_review.get('avg_course_rating')
336337

337338
if metadata_record.json_metadata.get(FORCE_INCLUSION_METADATA_TAG_KEY):
338339
metadata_record.json_metadata = transform_course_metadata_to_visible(metadata_record.json_metadata)
@@ -358,11 +359,12 @@ def _normalize_metadata_record(course_metadata_record):
358359
normalized_metadata_input = {
359360
'course_metadata': course_metadata_record.json_metadata,
360361
}
361-
course_metadata_record.json_metadata['normalized_metadata'] =\
362+
# pylint: disable=protected-access
363+
course_metadata_record._json_metadata['normalized_metadata'] =\
362364
NormalizedContentMetadataSerializer(normalized_metadata_input).data
363-
course_metadata_record.json_metadata['normalized_metadata_by_run'] = {}
365+
course_metadata_record._json_metadata['normalized_metadata_by_run'] = {}
364366
for run in course_metadata_record.json_metadata.get('course_runs', []):
365-
course_metadata_record.json_metadata['normalized_metadata_by_run'].update({
367+
course_metadata_record._json_metadata['normalized_metadata_by_run'].update({
366368
run['key']: NormalizedContentMetadataSerializer({
367369
'course_run_metadata': run,
368370
'course_metadata': course_metadata_record.json_metadata,
@@ -413,15 +415,15 @@ def _update_full_content_metadata_program(content_keys, dry_run=False):
413415
logger.error('Could not find ContentMetadata record for content_key %s.', content_key)
414416
continue
415417

416-
metadata_record.json_metadata.update(program_metadata_dict)
418+
metadata_record._json_metadata.update(program_metadata_dict) # pylint: disable=protected-access
417419
modified_content_metadata_records.append(metadata_record)
418420

419421
if dry_run:
420422
logger.info('dry_run=true, not updating program metadata')
421423
else:
422424
ContentMetadata.objects.bulk_update(
423425
modified_content_metadata_records,
424-
['json_metadata'],
426+
['_json_metadata'],
425427
batch_size=10,
426428
)
427429

@@ -1200,7 +1202,12 @@ def fetch_missing_course_metadata_task(self, force=False, dry_run=False): # pyl
12001202
that are embedded inside a program.
12011203
"""
12021204
logger.info('[FETCH_MISSING_METADATA] fetch_missing_course_metadata_task task started.')
1203-
program_metadata_list = ContentMetadata.objects.filter(content_type=PROGRAM).values_list('json_metadata', flat=True)
1205+
program_metadata_list = ContentMetadata.objects.filter(
1206+
content_type=PROGRAM
1207+
).values_list(
1208+
'_json_metadata',
1209+
flat=True,
1210+
)
12041211
course_keys = set()
12051212
for program_metadata in program_metadata_list:
12061213
if program_metadata is not None:
@@ -1266,7 +1273,7 @@ def fetch_missing_pathway_metadata_task(self, force=False, dry_run=False): # py
12661273
)
12671274

12681275
learner_pathway_metadata_list = ContentMetadata.objects.filter(content_type=LEARNER_PATHWAY).values_list(
1269-
'json_metadata', flat=True,
1276+
'_json_metadata', flat=True,
12701277
)
12711278
program_uuids = set()
12721279
course_keys = set()

enterprise_catalog/apps/api/tests/test_tasks.py

+12-11
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def test_fetch_missing_course_metadata_task(self, mock_update_data_from_discover
223223
"""
224224
test_course = 'course:edX+testX'
225225
course_content_metadata = ContentMetadataFactory.create(content_type=COURSE)
226-
ContentMetadataFactory.create(content_type=PROGRAM, json_metadata={
226+
ContentMetadataFactory.create(content_type=PROGRAM, _json_metadata={
227227
'courses': [
228228
course_content_metadata.json_metadata,
229229
{
@@ -489,7 +489,7 @@ def test_update_full_metadata(self, mock_oauth_client, mock_partition_course_key
489489
# the normalized metadata serializer.
490490
metadata_4 = ContentMetadataFactory(content_type=COURSE, content_key=course_key_4)
491491
metadata_4.catalog_queries.set([self.catalog_query])
492-
metadata_4.json_metadata['advertised_course_run_uuid'] = None
492+
metadata_4._json_metadata['advertised_course_run_uuid'] = None # pylint: disable=protected-access
493493
metadata_4.save()
494494
non_course_metadata = ContentMetadataFactory(content_type=COURSE_RUN, content_key=non_course_key)
495495
non_course_metadata.catalog_queries.set([self.catalog_query])
@@ -714,7 +714,7 @@ def test_update_full_metadata_exec_ed(self, mock_oauth_client, mock_partition_co
714714

715715
# Simulate a pre-existing ContentMetadata object freshly seeded using the response from /api/v1/search/all/
716716
course_metadata = ContentMetadataFactory.create(
717-
content_type=COURSE, content_key=course_key, json_metadata={
717+
content_type=COURSE, content_key=course_key, _json_metadata={
718718
'aggregation_key': 'course:edX+testX',
719719
'key': 'edX+testX',
720720
'course_type': EXEC_ED_2U_COURSE_TYPE,
@@ -801,7 +801,8 @@ def setUp(self):
801801
self.course_metadata_published.catalog_queries.set([self.enterprise_catalog_query])
802802
self.course_metadata_published.tags.set([self.tag1])
803803
self.course_metadata_unpublished = ContentMetadataFactory(content_type=COURSE, content_key='course-2')
804-
self.course_metadata_unpublished.json_metadata.get('course_runs')[0].update({
804+
# pylint: disable=protected-access
805+
self.course_metadata_unpublished._json_metadata.get('course_runs')[0].update({
805806
'status': 'unpublished',
806807
})
807808
self.course_metadata_unpublished.catalog_queries.set([self.enterprise_catalog_query])
@@ -821,7 +822,7 @@ def setUp(self):
821822
content_type=COURSE_RUN,
822823
parent_content_key='course-2',
823824
)
824-
self.course_run_metadata_unpublished.json_metadata.update({
825+
self.course_run_metadata_unpublished._json_metadata.update({
825826
'status': 'unpublished',
826827
})
827828
self.course_run_metadata_unpublished.catalog_queries.set([course_run_catalog_query])
@@ -874,7 +875,7 @@ def test_add_metadata_to_algolia_objects_skips_metadata_records_over_max_size(se
874875
short_description_string = "ayylmao".join(["" for x in range(50000)])
875876
full_description_string = "foobar".join(["" for x in range(50000)])
876877
too_big_sized_course = ContentMetadataFactory(content_type=COURSE, content_key='test-course-2')
877-
too_big_sized_course.json_metadata.update(
878+
too_big_sized_course._json_metadata.update( # pylint: disable=protected-access
878879
{"short_description": short_description_string, "full_description": full_description_string}
879880
)
880881
too_big_sized_course.save()
@@ -1216,7 +1217,7 @@ def test_index_algolia_published_course_to_program(self, mock_search_client):
12161217
program_2 = ContentMetadataFactory(content_type=PROGRAM, content_key='program-2')
12171218

12181219
# Make program-2 hidden to make it "non-indexable". Later we will assert that it will not get indexed.
1219-
program_2.json_metadata.update({
1220+
program_2._json_metadata.update({ # pylint: disable=protected-access
12201221
'hidden': True,
12211222
})
12221223
program_2.save()
@@ -1324,7 +1325,7 @@ def test_index_algolia_unpublished_course_to_program(self, mock_search_client):
13241325
program_2.catalog_queries.set([self.enterprise_catalog_query])
13251326

13261327
# Make program-2 hidden to make it "non-indexable". Later we will assert that it will not get indexed.
1327-
program_2.json_metadata.update({
1328+
program_2._json_metadata.update({ # pylint: disable=protected-access
13281329
'hidden': True,
13291330
})
13301331
program_2.save()
@@ -1635,7 +1636,7 @@ def test_index_algolia_program_to_pathway(self, mock_search_client):
16351636
program_1.catalog_queries.set([self.enterprise_catalog_query])
16361637

16371638
# Make program-2 hidden to make it "non-indexable". Later we will assert that it will not get indexed.
1638-
program_2.json_metadata.update({
1639+
program_2._json_metadata.update({ # pylint: disable=protected-access
16391640
'hidden': True,
16401641
})
16411642
program_2.save()
@@ -1772,15 +1773,15 @@ def test_index_algolia_all_uuids(self, mock_search_client):
17721773
program_for_main_course = ContentMetadataFactory(content_type=PROGRAM, content_key='program-1')
17731774
# Make the program hidden to make it "non-indexable", but ensure that it still gets indexed due to being related
17741775
# to an indexable course.
1775-
program_for_main_course.json_metadata.update({
1776+
program_for_main_course._json_metadata.update({ # pylint: disable=protected-access
17761777
'hidden': True,
17771778
})
17781779
program_for_main_course.save()
17791780
program_for_pathway = ContentMetadataFactory(content_type=PROGRAM, content_key='program-2')
17801781
program_for_pathway.catalog_queries.set([self.enterprise_catalog_query])
17811782
# Make the program hidden to make it "non-indexable", but ensure that it still gets indexed due to being related
17821783
# to an indexable pathway.
1783-
program_for_pathway.json_metadata.update({
1784+
program_for_pathway._json_metadata.update({ # pylint: disable=protected-access
17841785
'hidden': True,
17851786
})
17861787
program_for_pathway.save()

enterprise_catalog/apps/api/v1/tests/test_serializers.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@ def test_product_source_formatting(self):
2929
Test that the content metadata serializer will transform product source data within the json metadata field
3030
from a string to a dict.
3131
"""
32-
json_metadata = self.content_metadata_item.json_metadata
33-
json_metadata['product_source'] = '2u'
34-
self.content_metadata_item.json_metadata = json_metadata
32+
self.content_metadata_item._json_metadata.update({'product_source': '2u'}) # pylint: disable=protected-access
3533
self.content_metadata_item.save()
3634
serialized_data = ContentMetadataSerializer(self.content_metadata_item)
3735
assert serialized_data.data.get('product_source') == {

enterprise_catalog/apps/api/v1/tests/test_utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ def test_get_archived_content_count(self):
6969
"""
7070
Test that archived content will increment the count.
7171
"""
72-
content_1 = ContentMetadataFactory.create(json_metadata={'course_run_statuses': ['archived']})
73-
content_2 = ContentMetadataFactory.create(json_metadata={'course_run_statuses': ['unpublished', 'archived']})
72+
content_1 = ContentMetadataFactory.create(_json_metadata={'course_run_statuses': ['archived']})
73+
content_2 = ContentMetadataFactory.create(_json_metadata={'course_run_statuses': ['unpublished', 'archived']})
7474
# if there's at least one published course run, the content should not be considered archived
75-
content_3 = ContentMetadataFactory.create(json_metadata={'course_run_statuses': ['published', 'archived']})
75+
content_3 = ContentMetadataFactory.create(_json_metadata={'course_run_statuses': ['published', 'archived']})
7676

7777
highlighted_content_1 = HighlightedContentFactory(content_metadata=content_1)
7878
highlighted_content_2 = HighlightedContentFactory(content_metadata=content_2)

enterprise_catalog/apps/catalog/management/commands/tests/test_set_global_average_course_rating_value.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ def test_command_averages_course_reviews(
2525
"""
2626
ContentMetadataFactory(
2727
content_type='course',
28-
json_metadata={'avg_course_rating': 5, 'reviews_count': 20}
28+
_json_metadata={'avg_course_rating': 5, 'reviews_count': 20}
2929
)
3030
ContentMetadataFactory(
3131
content_type='course',
32-
json_metadata={'avg_course_rating': 4, 'reviews_count': 10}
32+
_json_metadata={'avg_course_rating': 4, 'reviews_count': 10}
3333
)
34-
ContentMetadataFactory(json_metadata={})
34+
ContentMetadataFactory(_json_metadata={})
3535
call_command(self.command_name)
3636
expected_total_average = ((5 * 20) + (4 * 10)) / 30
3737

0 commit comments

Comments
 (0)