From aaf178902b7e0fdc7f756259c6601b164391c682 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Dec 2021 09:45:19 -0400 Subject: [PATCH 1/6] feat: add second batch of Open edX Filters --- common/djangoapps/student/models.py | 12 +++++++++++- common/djangoapps/student/views/dashboard.py | 19 +++++++++++++++++++ lms/djangoapps/certificates/models.py | 17 ++++++++++++++++- lms/djangoapps/certificates/views/webview.py | 6 ++++++ lms/djangoapps/courseware/views/views.py | 6 ++++++ .../core/djangoapps/course_groups/models.py | 18 +++++++++++++++++- .../course_experience/views/course_home.py | 7 +++++++ requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 10 files changed, 85 insertions(+), 6 deletions(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index ed81d9101bcc..0f1d76170007 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -66,7 +66,7 @@ COURSE_ENROLLMENT_CREATED, COURSE_UNENROLLMENT_COMPLETED, ) -from openedx_filters.learning.filters import CourseEnrollmentStarted +from openedx_filters.learning.filters import CourseEnrollmentStarted, CourseUnenrollmentStarted import openedx.core.djangoapps.django_comment_common.comment_client as cc from common.djangoapps.course_modes.models import CourseMode, get_cosmetic_verified_display_price from common.djangoapps.student.emails import send_proctoring_requirements_email @@ -1122,6 +1122,10 @@ class EnrollmentNotAllowed(CourseEnrollmentException): pass +class UnenrollmentNotAllowed(CourseEnrollmentException): + pass + + class CourseEnrollmentManager(models.Manager): """ Custom manager for CourseEnrollment with Table-level filter methods. @@ -1767,6 +1771,12 @@ def unenroll(cls, user, course_id, skip_refund=False): try: record = cls.objects.get(user=user, course_id=course_id) + + try: + record = CourseUnenrollmentStarted.run_filter(enrollment=record) + except CourseUnenrollmentStarted.PreventUnenrollment as exc: + raise UnenrollmentNotAllowed(str(exc)) from exc + record.update_enrollment(is_active=False, skip_refund=skip_refund) except cls.DoesNotExist: diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py index 56a77c57f12c..c5e158c78602 100644 --- a/common/djangoapps/student/views/dashboard.py +++ b/common/djangoapps/student/views/dashboard.py @@ -18,6 +18,7 @@ from edx_django_utils.plugins import get_plugins_view_context from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace from opaque_keys.edx.keys import CourseKey +from openedx_filters.learning.filters import DashboardRenderStarted from pytz import UTC from lms.djangoapps.bulk_email.api import is_bulk_email_feature_enabled @@ -65,6 +66,19 @@ experiments_namespace = LegacyWaffleFlagNamespace(name='student.experiments') +class DashboardException(Exception): + """ + Exception class that requires redirecting to a URL. + """ + def __init__(self, url): + super().__init__() + self.url = url + + +class DashboardRenderNotAllowed(DashboardException): + pass + + def get_org_black_and_whitelist_for_site(): """ Returns the org blacklist and whitelist for the current site. @@ -863,6 +877,11 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem 'resume_button_urls': resume_button_urls }) + try: + context = DashboardRenderStarted.run_filter(context=context) + except DashboardRenderStarted.PreventDashboardRender as exc: + raise DashboardRenderNotAllowed(reverse(exc.redirect_to or 'account_settings')) from exc + response = render_to_response('dashboard.html', context) if show_account_activation_popup: response.delete_cookie( diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index ef2deca5e5a0..e234ab544b56 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -16,12 +16,12 @@ from django.db import models, transaction from django.db.models import Count from django.dispatch import receiver - from django.utils.translation import gettext_lazy as _ from edx_name_affirmation.api import get_verified_name, should_use_verified_name_for_certs from model_utils import Choices from model_utils.models import TimeStampedModel from opaque_keys.edx.django.models import CourseKeyField +from openedx_filters.learning.filters import CertificateCreationRequested from simple_history.models import HistoricalRecords from common.djangoapps.student import models_api as student_api @@ -50,6 +50,14 @@ class CertificateSocialNetworks: twitter = 'Twitter' +class GeneratedCertificateException(Exception): + pass + + +class CertificateGenerationNotAllowed(GeneratedCertificateException): + pass + + class CertificateAllowlist(TimeStampedModel): """ Tracks students who are on the certificate allowlist for a given course run. @@ -463,6 +471,13 @@ def save(self, *args, **kwargs): # pylint: disable=signature-differs The COURSE_CERT_AWARDED signal helps determine if a Program Certificate can be awarded to a learner in the Credentials IDA. """ + try: + self.user, self.course_id, self.mode, self.status = CertificateCreationRequested.run_filter( + user=self.user, course_id=self.course_id, mode=self.mode, status=self.status, + ) + except CertificateCreationRequested.PreventCertificateCreation as exc: + raise CertificateGenerationNotAllowed(str(exc)) from exc + super().save(*args, **kwargs) COURSE_CERT_CHANGED.send_robust( sender=self.__class__, diff --git a/lms/djangoapps/certificates/views/webview.py b/lms/djangoapps/certificates/views/webview.py index 287d87f7c8af..c07f05aac892 100644 --- a/lms/djangoapps/certificates/views/webview.py +++ b/lms/djangoapps/certificates/views/webview.py @@ -18,6 +18,7 @@ from eventtracking import tracker from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey +from openedx_filters.learning.filters import CertificateRenderStarted from organizations import api as organizations_api from edx_django_utils.plugins import pluggable_override @@ -643,6 +644,11 @@ def render_html_view(request, course_id, certificate=None): # Track certificate view events _track_certificate_events(request, course, user, user_certificate) + try: + context = CertificateRenderStarted.run_filter(context=context) + except CertificateRenderStarted.PreventCertificateRender: + return _render_invalid_certificate(request, course_id, platform_name, configuration) + # Render the certificate return _render_valid_certificate(request, context, custom_template) diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 0ea412181961..33006a7d5573 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -38,6 +38,7 @@ from markupsafe import escape from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey +from openedx_filters.learning.filters import CourseAboutRenderStarted from pytz import UTC from requests.exceptions import ConnectionError, Timeout # pylint: disable=redefined-builtin from rest_framework import status @@ -1025,6 +1026,11 @@ def course_about(request, course_id): 'allow_anonymous': allow_anonymous, } + try: + context = CourseAboutRenderStarted.run_filter(context=context) + except CourseAboutRenderStarted.PreventCourseAboutRender as exc: + raise CourseAccessRedirect(reverse(exc.redirect_to or 'dashboard')) from exc + return render_to_response('courseware/course_about.html', context) diff --git a/openedx/core/djangoapps/course_groups/models.py b/openedx/core/djangoapps/course_groups/models.py index da861f06b606..2fd5c429e557 100644 --- a/openedx/core/djangoapps/course_groups/models.py +++ b/openedx/core/djangoapps/course_groups/models.py @@ -13,15 +13,23 @@ from django.dispatch import receiver from opaque_keys.edx.django.models import CourseKeyField +from openedx_filters.learning.filters import CohortChangeRequested from openedx.core.djangolib.model_mixins import DeletableByUserValue from openedx_events.learning.data import CohortData, CourseData, UserData, UserPersonalData # lint-amnesty, pylint: disable=wrong-import-order from openedx_events.learning.signals import COHORT_MEMBERSHIP_CHANGED # lint-amnesty, pylint: disable=wrong-import-order - log = logging.getLogger(__name__) +class CohortMembershipException(Exception): + pass + + +class CohortChangeNotAllowed(CohortMembershipException): + pass + + class CourseUserGroup(models.Model): """ This model represents groups of users in a course. Groups may have different types, @@ -122,6 +130,14 @@ def assign(cls, cohort, user): cohort_name=cohort.name)) else: previous_cohort = membership.course_user_group + + try: + membership, previous_cohort = CohortChangeRequested.run_filter( + current_cohort=membership, previous_cohort=previous_cohort, + ) + except CohortChangeRequested.PreventCohortChange as exc: + raise CohortChangeNotAllowed(str(exc)) from exc + previous_cohort.users.remove(user) membership.course_user_group = cohort diff --git a/openedx/features/course_experience/views/course_home.py b/openedx/features/course_experience/views/course_home.py index e488936d7bc9..e9501062f8d1 100644 --- a/openedx/features/course_experience/views/course_home.py +++ b/openedx/features/course_experience/views/course_home.py @@ -11,6 +11,7 @@ from django.views.decorators.cache import cache_control from django.views.decorators.csrf import ensure_csrf_cookie from opaque_keys.edx.keys import CourseKey +from openedx_filters.learning.filters import CourseHomeRenderStarted from web_fragments.fragment import Fragment from lms.djangoapps.course_home_api.toggles import course_home_legacy_is_active @@ -240,5 +241,11 @@ def render_to_fragment(self, request, course_id=None, **kwargs): # lint-amnesty 'has_discount': has_discount, 'show_search': show_search, } + + try: + context = CourseHomeRenderStarted.run_filter(context=context) + except CourseHomeRenderStarted.PreventCourseHomeRender as exc: + raise CourseAccessRedirect(reverse(exc.redirect_to or 'dashboard')) from exc + html = render_to_string('course_experience/course-home-fragment.html', context) return Fragment(html) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 9f8096962ebb..d898cbe1ec1f 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -702,7 +702,7 @@ openedx-calc==2.0.1 # -r requirements/edx/base.in openedx-events==0.7.1 # via -r requirements/edx/base.in -openedx-filters==0.4.3 +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_alpha # via -r requirements/edx/base.in ora2==3.8.1 # via -r requirements/edx/base.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index f4339914425a..fb0b16d202e9 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -937,7 +937,7 @@ openedx-calc==2.0.1 # -r requirements/edx/testing.txt openedx-events==0.7.1 # via -r requirements/edx/testing.txt -openedx-filters==0.4.3 +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_alpha # via -r requirements/edx/testing.txt ora2==3.8.1 # via -r requirements/edx/testing.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index c8928413f559..f6ef513d6fef 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -887,7 +887,7 @@ openedx-calc==2.0.1 # -r requirements/edx/base.txt openedx-events==0.7.1 # via -r requirements/edx/base.txt -openedx-filters==0.4.3 +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_alpha # via -r requirements/edx/base.txt ora2==3.8.1 # via -r requirements/edx/base.txt From 1f9e25a61593135399308c13ff8e86c463b25969 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 9 Feb 2022 14:25:19 -0400 Subject: [PATCH 2/6] fix: change arguments for CohortChangeRequested --- openedx/core/djangoapps/course_groups/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openedx/core/djangoapps/course_groups/models.py b/openedx/core/djangoapps/course_groups/models.py index 2fd5c429e557..04f7144e1ed3 100644 --- a/openedx/core/djangoapps/course_groups/models.py +++ b/openedx/core/djangoapps/course_groups/models.py @@ -132,8 +132,8 @@ def assign(cls, cohort, user): previous_cohort = membership.course_user_group try: - membership, previous_cohort = CohortChangeRequested.run_filter( - current_cohort=membership, previous_cohort=previous_cohort, + membership, cohort = CohortChangeRequested.run_filter( + membership=membership, cohort=cohort, ) except CohortChangeRequested.PreventCohortChange as exc: raise CohortChangeNotAllowed(str(exc)) from exc From 54bbcf07e8cf916adc3096f7e4bdb22a6d7c01d9 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 14 Feb 2022 11:51:45 -0400 Subject: [PATCH 3/6] refactor: address PR comments (openedx-filters PR 22) --- common/djangoapps/student/views/dashboard.py | 7 +++++-- lms/djangoapps/certificates/views/webview.py | 4 +++- lms/djangoapps/courseware/views/views.py | 7 +++++-- openedx/core/djangoapps/course_groups/models.py | 2 +- openedx/features/course_experience/views/course_home.py | 7 +++++-- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py index c5e158c78602..45f8399e8f8f 100644 --- a/common/djangoapps/student/views/dashboard.py +++ b/common/djangoapps/student/views/dashboard.py @@ -877,12 +877,15 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem 'resume_button_urls': resume_button_urls }) + dashboard_template = 'dashboard.html' try: - context = DashboardRenderStarted.run_filter(context=context) + context = DashboardRenderStarted.run_filter( + context=context, template_name=dashboard_template, + ) except DashboardRenderStarted.PreventDashboardRender as exc: raise DashboardRenderNotAllowed(reverse(exc.redirect_to or 'account_settings')) from exc - response = render_to_response('dashboard.html', context) + response = render_to_response(dashboard_template, context) if show_account_activation_popup: response.delete_cookie( settings.SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME, diff --git a/lms/djangoapps/certificates/views/webview.py b/lms/djangoapps/certificates/views/webview.py index c07f05aac892..c201898c736a 100644 --- a/lms/djangoapps/certificates/views/webview.py +++ b/lms/djangoapps/certificates/views/webview.py @@ -645,7 +645,9 @@ def render_html_view(request, course_id, certificate=None): _track_certificate_events(request, course, user, user_certificate) try: - context = CertificateRenderStarted.run_filter(context=context) + context, custom_template = CertificateRenderStarted.run_filter( + context=context, custom_template=custom_template, + ) except CertificateRenderStarted.PreventCertificateRender: return _render_invalid_certificate(request, course_id, platform_name, configuration) diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 33006a7d5573..6c53f108184b 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -1026,12 +1026,15 @@ def course_about(request, course_id): 'allow_anonymous': allow_anonymous, } + course_about_template = 'courseware/course_about.html' try: - context = CourseAboutRenderStarted.run_filter(context=context) + context, course_about_template = CourseAboutRenderStarted.run_filter( + context=context, template_name=course_about_template, + ) except CourseAboutRenderStarted.PreventCourseAboutRender as exc: raise CourseAccessRedirect(reverse(exc.redirect_to or 'dashboard')) from exc - return render_to_response('courseware/course_about.html', context) + return render_to_response(course_about_template, context) @ensure_csrf_cookie diff --git a/openedx/core/djangoapps/course_groups/models.py b/openedx/core/djangoapps/course_groups/models.py index 04f7144e1ed3..6eb0e1e94772 100644 --- a/openedx/core/djangoapps/course_groups/models.py +++ b/openedx/core/djangoapps/course_groups/models.py @@ -133,7 +133,7 @@ def assign(cls, cohort, user): try: membership, cohort = CohortChangeRequested.run_filter( - membership=membership, cohort=cohort, + current_membership=membership, target_cohort=cohort, ) except CohortChangeRequested.PreventCohortChange as exc: raise CohortChangeNotAllowed(str(exc)) from exc diff --git a/openedx/features/course_experience/views/course_home.py b/openedx/features/course_experience/views/course_home.py index e9501062f8d1..805bd5c3aece 100644 --- a/openedx/features/course_experience/views/course_home.py +++ b/openedx/features/course_experience/views/course_home.py @@ -242,10 +242,13 @@ def render_to_fragment(self, request, course_id=None, **kwargs): # lint-amnesty 'show_search': show_search, } + course_home_template = 'course_experience/course-home-fragment.html' try: - context = CourseHomeRenderStarted.run_filter(context=context) + context, course_home_template = CourseHomeRenderStarted.run_filter( + context=context, template_name=course_home_template, + ) except CourseHomeRenderStarted.PreventCourseHomeRender as exc: raise CourseAccessRedirect(reverse(exc.redirect_to or 'dashboard')) from exc - html = render_to_string('course_experience/course-home-fragment.html', context) + html = render_to_string(course_home_template, context) return Fragment(html) From 9b97eac97fbb59bf73ac70681d078d83255356c8 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 14 Feb 2022 11:57:26 -0400 Subject: [PATCH 4/6] chore: update openedx-filters from alpha to beta --- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index d898cbe1ec1f..ba25d2e0fb69 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -702,7 +702,7 @@ openedx-calc==2.0.1 # -r requirements/edx/base.in openedx-events==0.7.1 # via -r requirements/edx/base.in -git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_alpha +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_beta # via -r requirements/edx/base.in ora2==3.8.1 # via -r requirements/edx/base.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index fb0b16d202e9..f03df82953bd 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -937,7 +937,7 @@ openedx-calc==2.0.1 # -r requirements/edx/testing.txt openedx-events==0.7.1 # via -r requirements/edx/testing.txt -git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_alpha +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_beta # via -r requirements/edx/testing.txt ora2==3.8.1 # via -r requirements/edx/testing.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index f6ef513d6fef..bb4bffcd33b6 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -887,7 +887,7 @@ openedx-calc==2.0.1 # -r requirements/edx/base.txt openedx-events==0.7.1 # via -r requirements/edx/base.txt -git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_alpha +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_beta # via -r requirements/edx/base.txt ora2==3.8.1 # via -r requirements/edx/base.txt From 8f587d7a8792f864c0daad1e03da955cb3787c1a Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 16 Feb 2022 13:16:37 -0400 Subject: [PATCH 5/6] chore: add extra argument to dashboard rendering --- common/djangoapps/student/views/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py index 45f8399e8f8f..be57bf74e5ac 100644 --- a/common/djangoapps/student/views/dashboard.py +++ b/common/djangoapps/student/views/dashboard.py @@ -879,7 +879,7 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem dashboard_template = 'dashboard.html' try: - context = DashboardRenderStarted.run_filter( + context, dashboard_template = DashboardRenderStarted.run_filter( context=context, template_name=dashboard_template, ) except DashboardRenderStarted.PreventDashboardRender as exc: From 51c4c46dfe5019a8aee0a76fb61468143bcb2e09 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 16 Feb 2022 13:19:24 -0400 Subject: [PATCH 6/6] chore: change beta from gamma --- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index ba25d2e0fb69..1b64752d2938 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -702,7 +702,7 @@ openedx-calc==2.0.1 # -r requirements/edx/base.in openedx-events==0.7.1 # via -r requirements/edx/base.in -git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_beta +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_gamma # via -r requirements/edx/base.in ora2==3.8.1 # via -r requirements/edx/base.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index f03df82953bd..a46732b83243 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -937,7 +937,7 @@ openedx-calc==2.0.1 # -r requirements/edx/testing.txt openedx-events==0.7.1 # via -r requirements/edx/testing.txt -git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_beta +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_gamma # via -r requirements/edx/testing.txt ora2==3.8.1 # via -r requirements/edx/testing.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index bb4bffcd33b6..ccc9f23e50cf 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -887,7 +887,7 @@ openedx-calc==2.0.1 # -r requirements/edx/base.txt openedx-events==0.7.1 # via -r requirements/edx/base.txt -git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_beta +git+https://github.com/eduNEXT/openedx-filters.git@MJG/2nd_filters_batch#egg=openedx_filters==0.5.0_gamma # via -r requirements/edx/base.txt ora2==3.8.1 # via -r requirements/edx/base.txt