diff --git a/lms/djangoapps/support/static/support/jsx/program_enrollments/inspector.jsx b/lms/djangoapps/support/static/support/jsx/program_enrollments/inspector.jsx deleted file mode 100644 index 8467de0dd2..0000000000 --- a/lms/djangoapps/support/static/support/jsx/program_enrollments/inspector.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Button, InputText, StatusAlert, InputSelect } from '@edx/paragon'; - -export const ProgramEnrollmentsInspectorPage = props => ( -
-); - -ProgramEnrollmentsInspectorPage.propTypes = { - successes: PropTypes.arrayOf(PropTypes.string), - errors: PropTypes.arrayOf(PropTypes.string), - learnerInfo: PropTypes.string, - orgKeys: PropTypes.arrayOf(PropTypes.object), -}; - -ProgramEnrollmentsInspectorPage.defaultProps = { - successes: [], - errors: [], - learnerInfo: '', - orgKeys: [], -}; diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py index c6a529f72b..41d0366fd6 100644 --- a/lms/djangoapps/support/tests/test_views.py +++ b/lms/djangoapps/support/tests/test_views.py @@ -8,7 +8,7 @@ import itertools import json import re from datetime import datetime, timedelta -from uuid import UUID, uuid4 +from uuid import uuid4, UUID import ddt import six @@ -17,19 +17,15 @@ from django.db.models import signals from django.http import HttpResponse from django.urls import reverse from mock import patch -from organizations.tests.factories import OrganizationFactory from pytz import UTC -from social_django.models import UserSocialAuth from common.test.utils import disable_signal from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory -from lms.djangoapps.program_enrollments.tests.factories import ProgramCourseEnrollmentFactory, ProgramEnrollmentFactory from lms.djangoapps.verify_student.models import VerificationDeadline from student.models import ENROLLED_TO_ENROLLED, CourseEnrollment, CourseEnrollmentAttribute, ManualEnrollmentAudit from student.roles import GlobalStaff, SupportStaffRole from student.tests.factories import CourseEnrollmentFactory, UserFactory -from third_party_auth.tests.factories import SAMLProviderConfigFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -543,238 +539,3 @@ class SupportViewLinkProgramEnrollmentsTests(SupportViewTestCase): msg = u"All linking lines must be in the format 'external_user_key,lms_username'" render_call_dict = mocked_render.call_args[0][1] assert render_call_dict['errors'] == [msg] - - -class ProgramEnrollmentsInspectorViewTests(SupportViewTestCase): - """ - View tests for Program Enrollments Inspector - """ - patch_render = patch( - 'support.views.program_enrollments.render_to_response', - return_value=HttpResponse(), - autospec=True, - ) - - def setUp(self): - super(ProgramEnrollmentsInspectorViewTests, self).setUp() - self.url = reverse("support:program_enrollments_inspector") - SupportStaffRole().add_users(self.user) - self.program_uuid = str(uuid4()) - self.external_user_key = 'abcaaa' - # Setup three orgs and their SAML providers - self.org_key_list = ['test_org', 'donut_org', 'tri_org'] - for org_key in self.org_key_list: - lms_org = OrganizationFactory( - short_name=org_key - ) - SAMLProviderConfigFactory( - organization=lms_org, - slug=org_key, - enabled=True, - ) - self.no_saml_org_key = 'no_saml_org' - self.no_saml_lms_org = OrganizationFactory( - short_name=self.no_saml_org_key - ) - - def _serialize_datetime(self, dt): - return dt.strftime('%Y-%m-%dT%H:%M:%S') - - def test_initial_rendering(self): - response = self.client.get(self.url) - content = six.text_type(response.content, encoding='utf-8') - expected_organization_serialized = '"orgKeys": {}'.format(json.dumps(self.org_key_list)) - assert response.status_code == 200 - assert expected_organization_serialized in content - assert '"learnerInfo": {}' in content - - def _construct_user(self, username, org_key=None, external_user_key=None): - """ - Provided the username, create an edx account user. If the org_key is provided, - SSO link the user with the IdP associated with org_key. Return the created user and - expected user info object from the view - """ - user = UserFactory(username=username) - user_info = { - 'username': user.username, - 'email': user.email - } - if org_key and external_user_key: - user_social_auth = UserSocialAuth.objects.create( - user=user, - uid='{0}:{1}'.format(org_key, external_user_key), - provider=org_key - ) - user_info['external_user_key'] = external_user_key - user_info['SSO'] = { - 'uid': user_social_auth.uid, - 'provider': user_social_auth.provider - } - return user, user_info - - def _construct_enrollments(self, program_uuids, course_ids, external_user_key, edx_user=None): - """ - A helper function to setup the program enrollments for a given learner. - If the edx user is provided, it will try to SSO the user with the enrollments - Return the expected info object that should be created based on the model setup - """ - expected_enrollments = [] - for program_uuid in program_uuids: - expected_enrollment = {} - expected_course_enrollment = {} - course_enrollment = None - program_enrollment = ProgramEnrollmentFactory.create( - external_user_key=external_user_key, - program_uuid=program_uuid, - user=edx_user - ) - expected_enrollment['program_enrollment'] = { - 'created': self._serialize_datetime( - program_enrollment.created - ), - 'modified': self._serialize_datetime( - program_enrollment.modified - ), - 'program_uuid': program_enrollment.program_uuid, - 'external_user_key': external_user_key, - 'status': program_enrollment.status - } - - for course_id in course_ids: - if edx_user: - course_enrollment = CourseEnrollmentFactory.create( - course_id=course_id, - user=edx_user, - mode=CourseMode.MASTERS, - is_active=True - ) - expected_course_enrollment = { - 'course_id': str(course_enrollment.course_id), - 'is_active': course_enrollment.is_active, - 'mode': course_enrollment.mode, - } - - program_course_enrollment = ProgramCourseEnrollmentFactory.create( - program_enrollment=program_enrollment, - course_key=course_id, - course_enrollment=course_enrollment, - status='active', - ) - expected_program_course_enrollment = { - 'created': self._serialize_datetime( - program_course_enrollment.created - ), - 'modified': self._serialize_datetime( - program_course_enrollment.modified - ), - 'status': program_course_enrollment.status, - 'course_key': str(program_course_enrollment.course_key), - } - if expected_course_enrollment: - expected_program_course_enrollment['course_enrollment'] = expected_course_enrollment - - expected_enrollment.setdefault('program_course_enrollments', []).append( - expected_program_course_enrollment - ) - - expected_enrollments.append(expected_enrollment) - - return expected_enrollments - - @patch_render - def test_search_username_well_connected_user(self, mocked_render): - created_user, expected_user_info = self._construct_user( - 'test_user_connected', - self.org_key_list[0], - self.external_user_key - ) - expected_enrollments = self._construct_enrollments( - [self.program_uuid], - [self.course.id], - self.external_user_key, - created_user - ) - self.client.get(self.url, data={ - 'edx_user': created_user.username - }) - expected_info = { - 'user': expected_user_info, - 'enrollments': expected_enrollments - } - - render_call_dict = mocked_render.call_args[0][1] - assert expected_info == render_call_dict['learner_program_enrollments'] - - @patch_render - def test_search_username_user_not_connected(self, mocked_render): - created_user, expected_user_info = self._construct_user('user_not_connected') - self.client.get(self.url, data={ - 'edx_user': created_user.email - }) - expected_info = { - 'user': expected_user_info - } - - render_call_dict = mocked_render.call_args[0][1] - assert expected_info == render_call_dict['learner_program_enrollments'] - - @patch_render - def test_search_username_user_no_enrollment(self, mocked_render): - created_user, expected_user_info = self._construct_user( - 'user_connected', - self.org_key_list[0], - self.external_user_key - ) - self.client.get(self.url, data={ - 'edx_user': created_user.email - }) - expected_info = { - 'user': expected_user_info - } - - render_call_dict = mocked_render.call_args[0][1] - assert expected_info == render_call_dict['learner_program_enrollments'] - - @patch_render - def test_search_username_user_no_course_enrollment(self, mocked_render): - created_user, expected_user_info = self._construct_user( - 'user_connected', - self.org_key_list[0], - self.external_user_key - ) - expected_enrollments = self._construct_enrollments( - [self.program_uuid], - [], - self.external_user_key, - created_user, - ) - self.client.get(self.url, data={ - 'edx_user': created_user.email - }) - expected_info = { - 'user': expected_user_info, - 'enrollments': expected_enrollments - } - - render_call_dict = mocked_render.call_args[0][1] - assert expected_info == render_call_dict['learner_program_enrollments'] - - @patch_render - def test_search_username_user_not_connected_with_enrollments(self, mocked_render): - created_user, expected_user_info = self._construct_user( - 'user_not_connected' - ) - self._construct_enrollments( - [self.program_uuid], - [], - self.external_user_key, - ) - self.client.get(self.url, data={ - 'edx_user': created_user.email - }) - expected_info = { - 'user': expected_user_info, - } - - render_call_dict = mocked_render.call_args[0][1] - assert expected_info == render_call_dict['learner_program_enrollments'] diff --git a/lms/djangoapps/support/urls.py b/lms/djangoapps/support/urls.py index 6dcbe5a063..49b3bf2eee 100644 --- a/lms/djangoapps/support/urls.py +++ b/lms/djangoapps/support/urls.py @@ -12,8 +12,8 @@ from support.views.enrollments import EnrollmentSupportListView, EnrollmentSuppo from support.views.feature_based_enrollments import FeatureBasedEnrollmentsSupportView from support.views.index import index from support.views.manage_user import ManageUserDetailView, ManageUserSupportView -from support.views.program_enrollments import LinkProgramEnrollmentSupportView, ProgramEnrollmentsInspectorView from support.views.refund import RefundSupportView +from support.views.program_enrollments import LinkProgramEnrollmentSupportView COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view() @@ -41,10 +41,5 @@ urlpatterns = [ FeatureBasedEnrollmentsSupportView.as_view(), name="feature_based_enrollments" ), - url(r'link_program_enrollments/?$', LinkProgramEnrollmentSupportView.as_view(), name='link_program_enrollments'), - url( - r'program_enrollments_inspector/?$', - ProgramEnrollmentsInspectorView.as_view(), - name='program_enrollments_inspector' - ) + url(r'link_program_enrollments/?$', LinkProgramEnrollmentSupportView.as_view(), name='link_program_enrollments') ] diff --git a/lms/djangoapps/support/views/certificate.py b/lms/djangoapps/support/views/certificate.py index ab60ffec39..7f0952f03f 100644 --- a/lms/djangoapps/support/views/certificate.py +++ b/lms/djangoapps/support/views/certificate.py @@ -3,9 +3,9 @@ Certificate tool in the student support app. """ +from six.moves.urllib.parse import unquote, quote_plus # pylint: disable=import-error from django.utils.decorators import method_decorator from django.views.generic import View -from six.moves.urllib.parse import quote_plus, unquote from edxmako.shortcuts import render_to_response from support.decorators import require_support_permission diff --git a/lms/djangoapps/support/views/program_enrollments.py b/lms/djangoapps/support/views/program_enrollments.py index 5aa9a649f4..54615d84a2 100644 --- a/lms/djangoapps/support/views/program_enrollments.py +++ b/lms/djangoapps/support/views/program_enrollments.py @@ -6,22 +6,14 @@ Support tool for changing course enrollments. import csv from uuid import UUID -from django.contrib.auth.models import User -from django.db.models import Q from django.utils.decorators import method_decorator from django.views.generic import View -from social_django.models import UserSocialAuth from edxmako.shortcuts import render_to_response -from lms.djangoapps.program_enrollments.api import ( - fetch_program_enrollments_by_student, - link_program_enrollments -) +from lms.djangoapps.program_enrollments.api import link_program_enrollments from lms.djangoapps.support.decorators import require_support_permission -from third_party_auth.models import SAMLProviderConfig TEMPLATE_PATH = 'support/link_program_enrollments.html' -DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S' class LinkProgramEnrollmentSupportView(View): @@ -106,153 +98,3 @@ class LinkProgramEnrollmentSupportView(View): ] errors = [message for message in link_errors.values()] return successes, errors - - -class ProgramEnrollmentsInspectorView(View): - """ - The view to search and display the program enrollments - information of a learner. - """ - exclude_from_schema = True - CONSOLE_TEMPLATE_PATH = 'support/program_enrollments_inspector.html' - - @method_decorator(require_support_permission) - def get(self, request): - """ - Based on the query string parameters passed through the GET request - Search the data store for information about ProgramEnrollment and - SSO linkage with the user. - """ - errors = [] - edx_username_or_email = request.GET.get('edx_user', '').strip() - org_key = request.GET.get('IdPSelect', '').strip() - external_user_key = request.GET.get('external_user_key', '').strip() - learner_program_enrollments = {} - if edx_username_or_email: - learner_program_enrollments, error = self._get_account_info(edx_username_or_email) - if error: - errors.append(error) - elif org_key and external_user_key: - learner_program_enrollments = {} - elif not external_user_key and org_key: - errors.append( - 'You must provide either the edX username or email, or the ' - 'Learner Account Provider and External Key pair to do search!' - ) - - return render_to_response( - self.CONSOLE_TEMPLATE_PATH, - { - 'successes': [], - 'errors': errors, - 'learner_program_enrollments': learner_program_enrollments, - 'org_keys': self._get_org_keys_with_idp(), - } - ) - - def _get_org_keys_with_idp(self): - """ - From our Third_party_auth models, return a list - of organizations whose SAMLProviders are active and configured - """ - saml_providers = SAMLProviderConfig.objects.current_set().filter( - enabled=True, - organization__isnull=False - ).select_related('organization') - - return [saml_provider.organization.short_name for saml_provider in saml_providers] - - def _get_account_info(self, username_or_email): - """ - Provided the edx account username or email, return edx account info - and program_enrollments_info. If we cannot identify the user, return - empty object and error. - """ - user_info = {} - external_key = None - try: - user = User.objects.get(Q(username=username_or_email) | Q(email=username_or_email)) - user_info['username'] = user.username - user_info['email'] = user.email - try: - user_social_auth = UserSocialAuth.objects.get(user=user) - _, external_key = user_social_auth.uid.split(':', 1) - user_info['external_user_key'] = external_key - user_info['SSO'] = { - 'uid': user_social_auth.uid, - 'provider': user_social_auth.provider - } - except UserSocialAuth.DoesNotExist: - pass - - enrollments = self._get_enrollments(user=user) - result = {'user': user_info} - if enrollments: - result['enrollments'] = enrollments - - return result, '' - except User.DoesNotExist: - return {}, 'Could not find edx account with {}'.format(username_or_email) - - def _get_enrollments(self, user=None, external_user_key=None): - """ - With the user or external_user_key passed in, - return an array of dictionariers with corresponding ProgramEnrollments - and ProgramCourseEnrollments all serialized for view - """ - program_enrollments = fetch_program_enrollments_by_student( - user=user, - external_user_key=external_user_key - ).prefetch_related('program_course_enrollments') - - enrollments_by_program_uuid = {} - for program_enrollment in program_enrollments: - serialized_program_enrollment = self._serialize_program_enrollment(program_enrollment) - enrollment_item = { - 'program_enrollment': serialized_program_enrollment - } - program_course_enrollments = program_enrollment.program_course_enrollments.all() - for program_course_enrollment in program_course_enrollments.select_related( - 'course_enrollment' - ): - serialized_program_course_enrollment = self._serialize_program_course_enrollment( - program_course_enrollment - ) - enrollment_item.setdefault('program_course_enrollments', []).append( - serialized_program_course_enrollment - ) - enrollments_by_program_uuid[program_enrollment.program_uuid] = enrollment_item - - return list(enrollments_by_program_uuid.values()) - - def _serialize_program_enrollment(self, program_enrollment): - if not program_enrollment: - return {} - - return { - 'created': program_enrollment.created.strftime(DATETIME_FORMAT), - 'modified': program_enrollment.modified.strftime(DATETIME_FORMAT), - 'program_uuid': str(program_enrollment.program_uuid), - 'external_user_key': program_enrollment.external_user_key, - 'status': program_enrollment.status - } - - def _serialize_program_course_enrollment(self, program_course_enrollment): - """ - Return a dictionary of ProgramCourseEnrollment serialized - """ - if not program_course_enrollment: - return {} - - course_enrollment = program_course_enrollment.course_enrollment - return { - 'created': program_course_enrollment.created.strftime(DATETIME_FORMAT), - 'modified': program_course_enrollment.modified.strftime(DATETIME_FORMAT), - 'course_enrollment': { - 'course_id': str(course_enrollment.course_id), - 'is_active': course_enrollment.is_active, - 'mode': course_enrollment.mode, - }, - 'status': program_course_enrollment.status, - 'course_key': str(program_course_enrollment.course_key), - } diff --git a/lms/templates/support/program_enrollments_inspector.html b/lms/templates/support/program_enrollments_inspector.html deleted file mode 100644 index a6b5be932e..0000000000 --- a/lms/templates/support/program_enrollments_inspector.html +++ /dev/null @@ -1,37 +0,0 @@ -<%page expression_filter="h"/> - -<%! -from django.utils.translation import ugettext as _ -from openedx.core.djangolib.js_utils import js_escaped_string -%> - -## Override the default styles_version to use Bootstrap -<%! main_css = "css/bootstrap/lms-main.css" %> - -<%namespace name='static' file='../static_content.html'/> - -<%inherit file="../main.html" /> - -<%block name="js_extra"> -%block> - -<%block name="pagetitle"> - ${_("Program Enrollments Inspector")} -%block> - -<%block name="content"> -