Merge pull request #17582 from edx/MLoTurco/learner-3925-feature-view-and-template

Add EntitlementSupportView, Template, and base react element
This commit is contained in:
Michael LoTurco
2018-03-12 11:00:42 -04:00
committed by GitHub
8 changed files with 78 additions and 99 deletions

View File

@@ -0,0 +1,9 @@
import React from 'react';
const EntitlementSupportPage = () => (
<div>
Base Entitlement Support Page
</div>
);
export default EntitlementSupportPage;

View File

@@ -437,82 +437,3 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
)
verified_mode.expiration_datetime = datetime(year=1970, month=1, day=9, tzinfo=UTC)
verified_mode.save()
@ddt.ddt
class SupportViewCourseEntitlementsTests(SupportViewTestCase):
""" Tests for the course entitlement support view."""
def setUp(self):
super(SupportViewCourseEntitlementsTests, self).setUp()
self.user = UserFactory(is_staff=True)
SupportStaffRole().add_users(self.user)
self.client.login(username=self.user.username, password=TEST_PASSWORD)
self.student = UserFactory.create(username='student', email='test@example.com', password='test')
self.course_uuid = uuid4()
self.url = reverse('support:course_entitlement')
@ddt.data('username', 'email')
def test_get_entitlements(self, search_string_type):
CourseEntitlementFactory.create(mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid)
url = self.url + getattr(self.student, search_string_type)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)
self.assertEqual(len(data), 1)
self.assertDictContainsSubset({
'user': self.student.username,
'course_uuid': unicode(self.course_uuid),
'enrollment_course_run': None,
'mode': CourseMode.VERIFIED,
'support_details': []
}, data[0])
def test_reinstate_entitlement(self):
selected_run = CourseEnrollmentFactory(mode=CourseMode.VERIFIED, user=self.student)
expired_entitlement = CourseEntitlementFactory.create(
mode=CourseMode.VERIFIED, user=self.student, enrollment_course_run=selected_run, expired_at=datetime.now()
)
url = self.url + self.student.username
response = self.client.put(url, data=json.dumps(
{
'entitlement_uuid': unicode(expired_entitlement.uuid),
'reason': CourseEntitlementSupportDetail.LEAVE_SESSION
}),
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)
self.assertEqual(len(data['support_details']), 1)
self.assertDictContainsSubset({
'support_user': self.user.username,
'reason': CourseEntitlementSupportDetail.LEAVE_SESSION,
'comments': None,
'unenrolled_run': unicode(selected_run.course_id)
}, data['support_details'][0])
def test_create_entitlement(self):
CourseEntitlementFactory.create(
mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid, expired_at=datetime.now()
)
url = self.url + self.student.username
response = self.client.post(
url,
data=json.dumps({
'course_uuid': unicode(self.course_uuid),
'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW,
'mode': CourseMode.VERIFIED
}),
content_type='application/json',
)
self.assertEqual(response.status_code, 201)
data = json.loads(response.content)
self.assertEqual(len(data['support_details']), 1)
self.assertDictContainsSubset({
'support_user': self.user.username,
'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW,
'comments': None,
'unenrolled_run': None
}, data['support_details'][0])

View File

@@ -11,21 +11,13 @@ from support.views.index import index
from support.views.manage_user import ManageUserDetailView, ManageUserSupportView
from support.views.refund import RefundSupportView
COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view({
'get': 'list',
'post': 'create',
'put': 'update'
})
COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view()
urlpatterns = [
url(r'^$', index, name="index"),
url(r'^certificates/?$', CertificatesSupportView.as_view(), name="certificates"),
url(r'^refund/?$', RefundSupportView.as_view(), name="refund"),
url(
r'^course_entitlement/(?P<username_or_email>[\w.@+-]+)?$',
COURSE_ENTITLEMENTS_VIEW,
name="course_entitlement"
),
url(r'^course_entitlement/?$', COURSE_ENTITLEMENTS_VIEW, name="course_entitlement"),
url(r'^enrollment/?$', EnrollmentSupportView.as_view(), name="enrollment"),
url(r'^contact_us/?$', ContactUsView.as_view(), name="contact_us"),
url(

View File

@@ -6,20 +6,43 @@ from django.db import DatabaseError, transaction
from django.db.models import Q
from django.http import HttpResponseBadRequest
from django.utils.decorators import method_decorator
from django.views.generic import View
from edx_rest_framework_extensions.authentication import JwtAuthentication
from rest_framework import permissions, status, viewsets
from rest_framework.response import Response
from edxmako.shortcuts import render_to_response
from entitlements.api.v1.permissions import IsAdminOrAuthenticatedReadOnly
from entitlements.api.v1.serializers import SupportCourseEntitlementSerializer
from entitlements.models import CourseEntitlement, CourseEntitlementSupportDetail
from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.support.decorators import require_support_permission
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
REQUIRED_CREATION_FIELDS = ['course_uuid', 'reason', 'mode']
class EntitlementSupportView(viewsets.ModelViewSet):
class EntitlementSupportView(View):
"""
View for viewing and changing learner enrollments, used by the
support team.
"""
@method_decorator(require_support_permission)
def get(self, request):
"""Render the enrollment support tool view."""
support_actions = CourseEntitlementSupportDetail.get_support_actions_list()
ecommerce_url = EcommerceService().get_order_dashboard_url()
context = {
'username': request.GET.get('user', ''),
'uses_bootstrap': True,
'ecommerce_url': ecommerce_url,
'support_actions': support_actions
}
return render_to_response('support/entitlement.html', context)
class EntitlementSupportListView(viewsets.ModelViewSet):
"""
Allows viewing and changing learner course entitlements, used the support team.
"""

View File

@@ -0,0 +1,34 @@
<%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">
${_("Entitlements")}
</%block>
<%block name="content">
<section class="container outside-app">
${static.renderReact(
component="EntitlementSupportPage",
id="entitlement-support-page",
props={
'ecommerceUrl': ecommerce_url,
'supportReasons': support_actions
}
)
}
</section>
</%block>

13
package-lock.json generated
View File

@@ -7981,11 +7981,10 @@
"integrity": "sha1-eHTi04kR0nGeoncx0yRF2l7EOUw="
},
"react": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
"integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=",
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.1.0.tgz",
"integrity": "sha512-hvKYlKqde2JNnNiEzORvSA0J1L7uSZ43l+J89ZNoP4EXxQrVNH0CFj8vorfPou3w+1ou1BNMBir2VVsuXtETRA==",
"requires": {
"create-react-class": "15.6.3",
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
@@ -8003,9 +8002,9 @@
}
},
"react-dom": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz",
"integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=",
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.1.0.tgz",
"integrity": "sha512-i9in5qW3H2PDinUPD9bnQK7tLAD8LhjYQ+fXi3nJOvVnxOO3ErHq6RNEnKY7pbjTPt155e74q7al8eBUuyLtew==",
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",

View File

@@ -40,8 +40,8 @@
"popper.js": "1.12.9",
"prop-types": "15.6.0",
"raw-loader": "0.5.1",
"react": "15.6.2",
"react-dom": "15.6.2",
"react": "16.1.0",
"react-dom": "16.1.0",
"react-slick": "0.16.0",
"requirejs": "2.3.5",
"rtlcss": "2.2.1",

View File

@@ -29,6 +29,7 @@ module.exports = {
LearnerAnalyticsDashboard: './lms/static/js/learner_analytics_dashboard/LearnerAnalyticsDashboard.jsx',
UpsellExperimentModal: './lms/static/common/js/components/UpsellExperimentModal.jsx',
PortfolioExperimentUpsellModal: './lms/static/common/js/components/PortfolioExperimentUpsellModal.jsx',
EntitlementSupportPage: './lms/djangoapps/support/static/support/jsx/entitlements/index.jsx',
// Learner Dashboard
EntitlementFactory: './lms/static/js/learner_dashboard/course_entitlement_factory.js',