22Signal handler for invalidating cached course overviews
33"""
44
5-
65import logging
76
87from django .db import transaction
98from django .db .models .signals import post_save
109from django .dispatch import Signal
1110from django .dispatch .dispatcher import receiver
1211
12+ from openedx_catalog import api as catalog_api
13+ from openedx_catalog .models_api import CourseRun
1314from openedx .core .djangoapps .signals .signals import COURSE_CERT_DATE_CHANGE
1415from xmodule .data import CertificatesDisplayBehaviors
1516from xmodule .modulestore .django import SignalHandler
@@ -33,6 +34,8 @@ def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable
3334 """
3435 Catches the signal that a course has been published in Studio and updates the corresponding CourseOverview cache
3536 entry.
37+
38+ Also sync course data to the openedx_catalog CourseRun model.
3639 """
3740 try :
3841 previous_course_overview = CourseOverview .objects .get (id = course_key )
@@ -41,6 +44,48 @@ def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable
4144 updated_course_overview = CourseOverview .load_from_module_store (course_key )
4245 _check_for_course_changes (previous_course_overview , updated_course_overview )
4346
47+ # Currently, SplitModulestoreCourseIndex is the ultimate source of truth for
48+ # which courses exist. When a course is published, we sync that data to
49+ # CourseOverview, and from CourseOverview to CourseRun.
50+
51+ # In the future, CourseRun will be the "source of truth" and each CourseRun
52+ # may optionally point to content and get synced to CourseOverview.
53+
54+ # Ensure a CourseRun exists for this course
55+ try :
56+ course_run = catalog_api .get_course_run (course_key )
57+ except CourseRun .DoesNotExist :
58+ # Presumably this is a newly-created course. Create the CourseRun.
59+ course_run = catalog_api .create_course_run_for_modulestore_course_with (
60+ course_id = course_key ,
61+ display_name = updated_course_overview .display_name ,
62+ language_short = updated_course_overview .language ,
63+ )
64+
65+ # Keep the CourseRun up to date as the course is edited:
66+ if updated_course_overview .display_name != course_run .display_name :
67+ catalog_api .sync_course_run_details (course_key , display_name = updated_course_overview .display_name )
68+
69+ if (
70+ updated_course_overview .language
71+ and updated_course_overview .language != course_run .catalog_course .language_short
72+ ):
73+ if course_run .catalog_course .runs .count () == 1 :
74+ # This is the only run in this CatalogCourse. Update the language of the CatalogCourse
75+ catalog_api .update_catalog_course (
76+ course_run .catalog_course ,
77+ language_short = updated_course_overview .language ,
78+ )
79+ else :
80+ LOG .warning (
81+ 'Course run "%s" language "%s" does not match its catalog course language, "%s"' ,
82+ str (course_key ),
83+ updated_course_overview .language ,
84+ course_run .catalog_course .language_short ,
85+ )
86+
87+ # In the future, this will also sync schedule and other metadata to the CourseRun's related models
88+
4489
4590@receiver (SignalHandler .course_deleted )
4691def _listen_for_course_delete (sender , course_key , ** kwargs ): # pylint: disable=unused-argument
0 commit comments