Merge pull request #33530 from openedx/dkaplan1/APER-2407_remove-coaching-functionality-from-edx-platform

feat: remove deprecated coaching from edx-platform
This commit is contained in:
Deborah Kaplan
2023-10-19 10:51:51 -04:00
committed by GitHub
13 changed files with 4 additions and 382 deletions

View File

@@ -19121,10 +19121,6 @@ msgid ""
"subjects."
msgstr "עיין בקורסים שהושקו לאחרונה וראה מה חדש בנושאים האהובים עליך."
#: themes/edx.org/lms/templates/dashboard.html
msgid "Take advantage of free coaching!"
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Get Started"
msgstr ""

View File

@@ -18201,10 +18201,6 @@ msgid ""
"subjects."
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Take advantage of free coaching!"
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Get Started"
msgstr ""

View File

@@ -17370,10 +17370,6 @@ msgid ""
"subjects."
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Take advantage of free coaching!"
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Get Started"
msgstr ""

View File

@@ -19492,10 +19492,6 @@ msgid ""
"subjects."
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Take advantage of free coaching!"
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Get Started"
msgstr ""

View File

@@ -20296,10 +20296,6 @@ msgstr ""
"Ознакомьтесь с недавно запущенными курсами и новостями по вашим любимым "
"предметам."
#: themes/edx.org/lms/templates/dashboard.html
msgid "Take advantage of free coaching!"
msgstr ""
#: themes/edx.org/lms/templates/dashboard.html
msgid "Get Started"
msgstr ""

View File

@@ -236,7 +236,6 @@ class ProgramTypeFactory(DictFactoryBase):
class ProgramTypeAttrsFactory(DictFactoryBase):
uuid = factory.Faker('uuid4')
slug = factory.Faker('word')
coaching_supported = False
class ProgramFactory(DictFactoryBase):

View File

@@ -11,6 +11,3 @@ class ExternalUserIDConfig(AppConfig):
Default configuration for the "openedx.core.djangoapps.credit" Django application.
"""
name = 'openedx.core.djangoapps.external_user_ids'
def ready(self):
from . import signals # pylint: disable=unused-import

View File

@@ -20,7 +20,7 @@ class ExternalIdType(TimeStampedModel):
.. no_pii:
"""
MICROBACHELORS_COACHING = 'mb_coaching'
CALIPER = 'caliper'
XAPI = 'xapi'
LTI = 'lti'

View File

@@ -1,64 +0,0 @@
"""
Signal Handlers for External User Ids to be created and maintainer
"""
from logging import getLogger
from django.db.models.signals import post_save
from django.dispatch import receiver
from openedx.core.djangoapps.catalog.utils import get_programs
from .models import ExternalId, ExternalIdType
LOGGER = getLogger(__name__)
def _user_needs_external_id(instance, created):
return (
created and
instance.user and
not ExternalId.user_has_external_id(
user=instance.user,
type_name=ExternalIdType.MICROBACHELORS_COACHING)
)
@receiver(post_save, sender='student.CourseEnrollment')
def create_external_id_for_microbachelors_program(
sender, instance, created, **kwargs # pylint: disable=unused-argument
):
"""
Watches for post_save signal for creates on the CourseEnrollment table.
Generate an External ID if the Enrollment is in a MicroBachelors Program
"""
if _user_needs_external_id(instance, created):
mb_programs = [
program for program in get_programs(course=instance.course_id)
if program.get('type_attrs', {}).get('coaching_supported')
]
if mb_programs:
ExternalId.add_new_user_id(
user=instance.user,
type_name=ExternalIdType.MICROBACHELORS_COACHING
)
@receiver(post_save, sender='entitlements.CourseEntitlement')
def create_external_id_for_microbachelors_program_entitlement(
sender, instance, created, **kwargs # pylint: disable=unused-argument
):
"""
Watches for post_save signal for creates on the CourseEntitlement table.
Generate an External ID if the Entitlement is in a MicroBachelors Program
"""
if _user_needs_external_id(instance, created):
mb_programs = [
program for program in get_programs(catalog_course_uuid=instance.course_uuid)
if program.get('type_attrs', {}).get('coaching_supported')
]
if mb_programs:
ExternalId.add_new_user_id(
user=instance.user,
type_name=ExternalIdType.MICROBACHELORS_COACHING
)

View File

@@ -14,7 +14,7 @@ class ExternalIDTypeFactory(factory.django.DjangoModelFactory): # lint-amnesty,
class Meta:
model = ExternalIdType
name = FuzzyChoice([ExternalIdType.MICROBACHELORS_COACHING])
name = FuzzyChoice([ExternalIdType.CALIPER])
description = FuzzyText()

View File

@@ -1,192 +0,0 @@
"""
Signal Tests for External User Ids that are sent out of Open edX
"""
from opaque_keys.edx.keys import CourseKey
from django.core.cache import cache
from edx_django_utils.cache import RequestCache
from common.djangoapps.entitlements.models import CourseEntitlement
from openedx.core.djangoapps.catalog.tests.factories import (
CourseFactory,
ProgramFactory,
)
from common.djangoapps.student.tests.factories import TEST_PASSWORD, UserFactory, CourseEnrollmentFactory
from openedx.core.djangoapps.catalog.cache import (
CATALOG_COURSE_PROGRAMS_CACHE_KEY_TPL,
COURSE_PROGRAMS_CACHE_KEY_TPL,
PROGRAM_CACHE_KEY_TPL,
)
from openedx.core.djangoapps.external_user_ids.models import ExternalId, ExternalIdType
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.course_modes.models import CourseMode
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
class MicrobachelorsExternalIDTest(ModuleStoreTestCase, CacheIsolationTestCase):
"""
Test cases for Signals for External User Ids
"""
ENABLED_CACHES = ['default']
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.course_list = []
cls.user = UserFactory.create()
cls.course_keys = [
CourseKey.from_string('course-v1:edX+DemoX+Test_Course'),
CourseKey.from_string('course-v1:edX+DemoX+Another_Test_Course'),
]
ExternalIdType.objects.create(
name=ExternalIdType.MICROBACHELORS_COACHING,
description='test'
)
def setUp(self):
super().setUp()
RequestCache.clear_all_namespaces()
self.program = self._create_cached_program()
self.client.login(username=self.user.username, password=TEST_PASSWORD)
def _create_cached_program(self):
""" helper method to create a cached program """
program = ProgramFactory.create()
for course_key in self.course_keys:
program['courses'].append(CourseFactory(id=course_key))
program['type'] = 'MicroBachelors'
program['type_attrs']['coaching_supported'] = True
for course in program['courses']:
cache.set(
CATALOG_COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_uuid=course['uuid']),
[program['uuid']],
None
)
course_run = course['course_runs'][0]['key']
cache.set(
COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_run_id=course_run),
[program['uuid']],
None
)
cache.set(
PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']),
program,
None
)
return program
def test_enroll_mb_create_external_id(self):
course_run_key = self.program['courses'][0]['course_runs'][0]['key']
# Enroll user
enrollment = CourseEnrollmentFactory.create(
course_id=course_run_key,
user=self.user,
mode=CourseMode.VERIFIED,
)
enrollment.save()
external_id = ExternalId.objects.get(
user=self.user
)
assert external_id is not None
assert external_id.external_id_type.name == ExternalIdType.MICROBACHELORS_COACHING
def test_second_enroll_mb_no_new_external_id(self):
course_run_key1 = self.program['courses'][0]['course_runs'][0]['key']
course_run_key2 = self.program['courses'][1]['course_runs'][0]['key']
# Enroll user
CourseEnrollmentFactory.create(
course_id=course_run_key1,
user=self.user,
mode=CourseMode.VERIFIED,
)
external_id = ExternalId.objects.get(
user=self.user
)
assert external_id is not None
assert external_id.external_id_type.name == ExternalIdType.MICROBACHELORS_COACHING
original_external_user_uuid = external_id.external_user_id
CourseEnrollmentFactory.create(
course_id=course_run_key2,
user=self.user,
mode=CourseMode.VERIFIED,
)
enrollments = CourseEnrollment.objects.filter(user=self.user)
assert len(enrollments) == 2
external_ids = ExternalId.objects.filter(
user=self.user
)
assert len(external_ids) == 1
assert external_ids[0].external_id_type.name == ExternalIdType.MICROBACHELORS_COACHING
assert original_external_user_uuid == external_ids[0].external_user_id
def test_entitlement_mb_create_external_id(self):
catalog_course = self.program['courses'][0]
assert ExternalId.objects.filter(
user=self.user
).count() == 0
entitlement = CourseEntitlement.objects.create(
course_uuid=catalog_course['uuid'],
mode=CourseMode.VERIFIED,
user=self.user,
order_number='TEST-12345'
)
entitlement.save()
external_id = ExternalId.objects.get(
user=self.user
)
assert external_id is not None
assert external_id.external_id_type.name == ExternalIdType.MICROBACHELORS_COACHING
def test_second_entitlement_mb_no_new_external_id(self):
catalog_course1 = self.program['courses'][0]
catalog_course2 = self.program['courses'][1]
# Enroll user
entitlement = CourseEntitlement.objects.create(
course_uuid=catalog_course1['uuid'],
mode=CourseMode.VERIFIED,
user=self.user,
order_number='TEST-12345'
)
entitlement.save()
external_id = ExternalId.objects.get(
user=self.user
)
assert external_id is not None
assert external_id.external_id_type.name == ExternalIdType.MICROBACHELORS_COACHING
original_external_user_uuid = external_id.external_user_id
CourseEntitlement.objects.create(
course_uuid=catalog_course2['uuid'],
mode=CourseMode.VERIFIED,
user=self.user,
order_number='TEST-12345'
)
entitlements = CourseEntitlement.objects.filter(user=self.user)
assert len(entitlements) == 2
external_ids = ExternalId.objects.filter(
user=self.user
)
assert len(external_ids) == 1
assert external_ids[0].external_id_type.name == ExternalIdType.MICROBACHELORS_COACHING
assert original_external_user_uuid == external_ids[0].external_user_id

View File

@@ -39,7 +39,7 @@ from openedx.core.djangoapps.credit.models import (
CreditRequirement,
CreditRequirementStatus
)
from openedx.core.djangoapps.external_user_ids.models import ExternalId, ExternalIdType
from openedx.core.djangoapps.external_user_ids.models import ExternalIdType
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.accounts.views import AccountRetirementPartnerReportView
@@ -496,7 +496,6 @@ class TestPartnerReportingList(ModuleStoreTestCase):
"""
EXPECTED_MB_ORGS_CONFIG = [
{
AccountRetirementPartnerReportView.ORGS_CONFIG_ORG_KEY: 'mb_coaching',
AccountRetirementPartnerReportView.ORGS_CONFIG_FIELD_HEADINGS_KEY: [
AccountRetirementPartnerReportView.STUDENT_ID_KEY,
AccountRetirementPartnerReportView.ORIGINAL_EMAIL_KEY,
@@ -516,7 +515,7 @@ class TestPartnerReportingList(ModuleStoreTestCase):
self.url = reverse('accounts_retirement_partner_report')
self.maxDiff = None
self.test_created_datetime = datetime.datetime(2018, 1, 1, tzinfo=pytz.UTC)
ExternalIdType.objects.get_or_create(name=ExternalIdType.MICROBACHELORS_COACHING)
ExternalIdType.objects.get_or_create(name=ExternalIdType.CALIPER)
def get_user_dict(self, user, enrollments):
"""
@@ -624,56 +623,6 @@ class TestPartnerReportingList(ModuleStoreTestCase):
self.assert_status_and_user_list(user_dicts)
def test_success_mb_coaching(self):
"""
Check that MicroBachelors users who have consented to coaching have the proper info
included for the partner report.
"""
path = 'openedx.core.djangoapps.user_api.accounts.views.has_ever_consented_to_coaching'
with mock.patch(path, return_value=True) as mock_has_ever_consented:
user_dicts, users = self.create_partner_reporting_statuses(num=1)
external_id, created = ExternalId.add_new_user_id( # lint-amnesty, pylint: disable=unused-variable
user=users[0],
type_name=ExternalIdType.MICROBACHELORS_COACHING
)
expected_user = user_dicts[0]
expected_users = [expected_user]
expected_user[AccountRetirementPartnerReportView.STUDENT_ID_KEY] = str(external_id.external_user_id)
expected_user[
AccountRetirementPartnerReportView.ORGS_CONFIG_KEY] = TestPartnerReportingList.EXPECTED_MB_ORGS_CONFIG
self.assert_status_and_user_list(expected_users)
mock_has_ever_consented.assert_called_once()
def test_success_mb_coaching_no_external_id(self):
"""
Check that MicroBachelors users who have consented to coaching, but who do not have an external id, have the
proper info included for the partner report.
"""
path = 'openedx.core.djangoapps.user_api.accounts.views.has_ever_consented_to_coaching'
with mock.patch(path, return_value=True) as mock_has_ever_consented:
user_dicts, users = self.create_partner_reporting_statuses(num=1) # lint-amnesty, pylint: disable=unused-variable
self.assert_status_and_user_list(user_dicts)
mock_has_ever_consented.assert_called_once()
def test_success_mb_coaching_no_consent(self):
"""
Check that MicroBachelors users who have not consented to coaching have the proper info
included for the partner report.
"""
path = 'openedx.core.djangoapps.user_api.accounts.views.has_ever_consented_to_coaching'
with mock.patch(path, return_value=False) as mock_has_ever_consented:
user_dicts, users = self.create_partner_reporting_statuses(num=1)
ExternalId.add_new_user_id(
user=users[0],
type_name=ExternalIdType.MICROBACHELORS_COACHING
)
self.assert_status_and_user_list(user_dicts)
mock_has_ever_consented.assert_called_once()
def test_no_users(self):
"""
Checks that the call returns a success code and empty list if no users are found.

View File

@@ -57,7 +57,6 @@ from openedx.core.djangoapps.ace_common.template_context import get_base_templat
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest
from openedx.core.djangoapps.course_groups.models import UnregisteredLearnerCohortAssignments
from openedx.core.djangoapps.credit.models import CreditRequest, CreditRequirementStatus
from openedx.core.djangoapps.external_user_ids.models import ExternalId, ExternalIdType
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.profile_images.images import remove_profile_images
from openedx.core.djangoapps.user_api import accounts
@@ -93,11 +92,6 @@ from .serializers import (
from .signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC, USER_RETIRE_MAILINGS
from .utils import create_retirement_request_and_deactivate_account, username_suffix_generator
try:
from coaching.api import has_ever_consented_to_coaching
except ImportError:
has_ever_consented_to_coaching = None
log = logging.getLogger(__name__)
USER_PROFILE_PII = {
@@ -746,49 +740,8 @@ class AccountRetirementPartnerReportView(ViewSet):
'created': retirement_status.created,
}
# Some orgs have a custom list of headings and content for the partner report. Add this, if applicable.
self._add_orgs_config_for_user(retirement, retirement_status.user)
return retirement
def _add_orgs_config_for_user(self, retirement, user):
"""
Check to see if the user's info was sent to any partners (orgs) that have a a custom list of headings and
content for the partner report. If so, add this.
"""
# See if the MicroBachelors coaching provider needs to be notified of this user's retirement
if has_ever_consented_to_coaching is not None and has_ever_consented_to_coaching(user):
# See if the user has a MicroBachelors external id. If not, they were never sent to the
# coaching provider.
external_ids = ExternalId.objects.filter(
user=user,
external_id_type__name=ExternalIdType.MICROBACHELORS_COACHING
)
if external_ids.exists():
# User has an external id. Add the additional info.
external_id = str(external_ids[0].external_user_id)
self._add_coaching_orgs_config(retirement, external_id)
def _add_coaching_orgs_config(self, retirement, external_id):
"""
Add the orgs configuration for MicroBachelors coaching
"""
# Add the custom field headings
retirement[AccountRetirementPartnerReportView.ORGS_CONFIG_KEY] = [
{
AccountRetirementPartnerReportView.ORGS_CONFIG_ORG_KEY: 'mb_coaching',
AccountRetirementPartnerReportView.ORGS_CONFIG_FIELD_HEADINGS_KEY: [
AccountRetirementPartnerReportView.STUDENT_ID_KEY,
AccountRetirementPartnerReportView.ORIGINAL_EMAIL_KEY,
AccountRetirementPartnerReportView.ORIGINAL_NAME_KEY,
AccountRetirementPartnerReportView.DELETION_COMPLETED_KEY
]
}
]
# Add the custom field value
retirement[AccountRetirementPartnerReportView.STUDENT_ID_KEY] = external_id
@request_requires_username
def retirement_partner_status_create(self, request):
"""