From de6f25b8e61028a2213e77ad7418798cd924af59 Mon Sep 17 00:00:00 2001 From: Muhammad Afaq Shuaib Date: Sat, 21 Dec 2024 01:33:49 +0500 Subject: [PATCH] temp: disable price update for published courses (#4520) --- course_discovery/apps/api/v1/views/courses.py | 53 +++++++++++-------- .../apps/course_metadata/models.py | 12 +++-- .../apps/course_metadata/toggles.py | 12 +++++ 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/course_discovery/apps/api/v1/views/courses.py b/course_discovery/apps/api/v1/views/courses.py index 9024ae510d..50a2a5ec1e 100644 --- a/course_discovery/apps/api/v1/views/courses.py +++ b/course_discovery/apps/api/v1/views/courses.py @@ -34,6 +34,7 @@ Collaborator, Course, CourseEditor, CourseEntitlement, CourseRun, CourseType, CourseUrlSlug, Organization, Program, Seat, Source, Video ) +from course_discovery.apps.course_metadata.toggles import IS_DISABLE_PRICE_UPDATES_FOR_PUBLISHED_RUNS from course_discovery.apps.course_metadata.utils import ( create_missing_entitlement, ensure_draft_world, validate_course_number, validate_slug_format ) @@ -338,28 +339,36 @@ def update_course(self, data, partial=False): # pylint: disable=too-many-statem self.log_request_subjects_and_prices(data, course) # First, update course entitlements - if data.get('type') or data.get('prices'): - entitlements = [] - prices = data.get('prices', {}) - course_type = CourseType.objects.get(uuid=data.get('type')) if data.get('type') else course.type - entitlement_types = course_type.entitlement_types.all() - for entitlement_type in entitlement_types: - price = prices.get(entitlement_type.slug) - if price is None: - continue - entitlement, did_change = self.update_entitlement(course, entitlement_type, price, partial=partial) - entitlements.append(entitlement) - changed = changed or did_change - # Deleting entitlements here since they would be orphaned otherwise. - # One example of how this situation can happen is if a course team is switching between - # "Verified and Audit" and "Audit Only" before actually publishing their course run. - course.entitlements.exclude(mode__in=entitlement_types).delete() - course.entitlements.set(entitlements) - - # If entitlement has changed, get updated course object from DB that has new value for - # data modified timestamp. - if changed: - course.refresh_from_db() + is_price_update_disabled = IS_DISABLE_PRICE_UPDATES_FOR_PUBLISHED_RUNS.is_enabled() + has_no_published_runs = not any( + course_run.status == CourseRunStatus.Published for course_run in course.active_course_runs + ) + if ( + course.is_external_course or not is_price_update_disabled or + (is_price_update_disabled and has_no_published_runs) + ): + if data.get('type') or data.get('prices'): + entitlements = [] + prices = data.get('prices', {}) + course_type = CourseType.objects.get(uuid=data.get('type')) if data.get('type') else course.type + entitlement_types = course_type.entitlement_types.all() + for entitlement_type in entitlement_types: + price = prices.get(entitlement_type.slug) + if price is None: + continue + entitlement, did_change = self.update_entitlement(course, entitlement_type, price, partial=partial) + entitlements.append(entitlement) + changed = changed or did_change + # Deleting entitlements here since they would be orphaned otherwise. + # One example of how this situation can happen is if a course team is switching between + # "Verified and Audit" and "Audit Only" before actually publishing their course run. + course.entitlements.exclude(mode__in=entitlement_types).delete() + course.entitlements.set(entitlements) + + # If entitlement has changed, get updated course object from DB that has new value for + # data modified timestamp. + if changed: + course.refresh_from_db() # Save video if a new video source is provided, also allow removing the video from course if video_data: diff --git a/course_discovery/apps/course_metadata/models.py b/course_discovery/apps/course_metadata/models.py index 4f724e33e8..6c00d55ab4 100644 --- a/course_discovery/apps/course_metadata/models.py +++ b/course_discovery/apps/course_metadata/models.py @@ -55,8 +55,8 @@ ) from course_discovery.apps.course_metadata.query import CourseQuerySet, CourseRunQuerySet, ProgramQuerySet from course_discovery.apps.course_metadata.toggles import ( - IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, IS_SUBDIRECTORY_SLUG_FORMAT_FOR_BOOTCAMP_ENABLED, - IS_SUBDIRECTORY_SLUG_FORMAT_FOR_EXEC_ED_ENABLED + IS_DISABLE_PRICE_UPDATES_FOR_PUBLISHED_RUNS, IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, + IS_SUBDIRECTORY_SLUG_FORMAT_FOR_BOOTCAMP_ENABLED, IS_SUBDIRECTORY_SLUG_FORMAT_FOR_EXEC_ED_ENABLED ) from course_discovery.apps.course_metadata.utils import ( UploadToFieldNamePath, clean_query, clear_slug_request_cache_for_course, custom_render_variations, @@ -2681,11 +2681,17 @@ def get_seat_upgrade_deadline(self, seat_type): return deadline def update_or_create_seat_helper(self, seat_type, prices, upgrade_deadline_override): + is_price_update_disabled = IS_DISABLE_PRICE_UPDATES_FOR_PUBLISHED_RUNS.is_enabled() defaults = { 'upgrade_deadline': self.get_seat_upgrade_deadline(seat_type), } if seat_type.slug in prices: - defaults['price'] = prices[seat_type.slug] + if ( + self.course.is_external_course or not is_price_update_disabled or ( + self.status != CourseRunStatus.Published and is_price_update_disabled + ) + ): + defaults['price'] = prices[seat_type.slug] if upgrade_deadline_override and seat_type.slug == Seat.VERIFIED: defaults['upgrade_deadline_override'] = upgrade_deadline_override diff --git a/course_discovery/apps/course_metadata/toggles.py b/course_discovery/apps/course_metadata/toggles.py index 74d1ce8276..09b5d64f8b 100644 --- a/course_discovery/apps/course_metadata/toggles.py +++ b/course_discovery/apps/course_metadata/toggles.py @@ -78,3 +78,15 @@ IS_COURSE_RUN_VARIANT_ID_ECOMMERCE_CONSUMABLE = WaffleSwitch( 'course_metadata.is_course_run_variant_id_ecommerce_consumable', __name__ ) +# .. toggle_name: course_metadata.is_disable_price_updates_for_published_runs +# .. toggle_implementation: WaffleSwitch +# .. toggle_default: False +# .. toggle_description: Temporary toggle to disable price updates for published course runs. +# .. toggle_use_cases: open_edx +# .. toggle_type: temporary +# .. toggle_creation_date: 2024-12-20 +# .. toggle_target_removal_date: 2025-01-05 +# .. toggle_tickets: PROD-4264 +IS_DISABLE_PRICE_UPDATES_FOR_PUBLISHED_RUNS = WaffleSwitch( + 'course_metadata.is_disable_price_updates_for_published_runs', __name__ +)