Commit 421f0251 by Julia Hansbrough

Merge pull request #1744 from edx/flowerhack/feature/verified-cert-events

Events for entering verified flow & buying
parents 2bc41987 2f9380c2
...@@ -37,6 +37,7 @@ class ChooseModeView(View): ...@@ -37,6 +37,7 @@ class ChooseModeView(View):
enrollment_mode = CourseEnrollment.enrollment_mode_for_user(request.user, course_id) enrollment_mode = CourseEnrollment.enrollment_mode_for_user(request.user, course_id)
upgrade = request.GET.get('upgrade', False) upgrade = request.GET.get('upgrade', False)
request.session['attempting_upgrade'] = upgrade
# verified users do not need to register or upgrade # verified users do not need to register or upgrade
if enrollment_mode == 'verified': if enrollment_mode == 'verified':
......
...@@ -35,9 +35,7 @@ from track import contexts ...@@ -35,9 +35,7 @@ from track import contexts
from track.views import server_track from track.views import server_track
from eventtracking import tracker from eventtracking import tracker
unenroll_done = Signal(providing_args=["course_enrollment"]) unenroll_done = Signal(providing_args=["course_enrollment"])
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
AUDIT_LOG = logging.getLogger("audit") AUDIT_LOG = logging.getLogger("audit")
......
...@@ -95,4 +95,4 @@ Feature: LMS.Verified certificates ...@@ -95,4 +95,4 @@ Feature: LMS.Verified certificates
Then I see the course on my dashboard Then I see the course on my dashboard
And I see that I am on the verified track And I see that I am on the verified track
And a "edx.course.enrollment.activated" server event is emitted And a "edx.course.enrollment.activated" server event is emitted
And a "edx.course.enrollment.upgrade.succeeded" server event is emitted
...@@ -557,6 +557,7 @@ class CertificateItem(OrderItem): ...@@ -557,6 +557,7 @@ class CertificateItem(OrderItem):
""" """
super(CertificateItem, cls).add_to_order(order, course_id, cost, currency=currency) super(CertificateItem, cls).add_to_order(order, course_id, cost, currency=currency)
course_enrollment = CourseEnrollment.get_or_create_enrollment(order.user, course_id) course_enrollment = CourseEnrollment.get_or_create_enrollment(order.user, course_id)
# do some validation on the enrollment mode # do some validation on the enrollment mode
...@@ -570,7 +571,7 @@ class CertificateItem(OrderItem): ...@@ -570,7 +571,7 @@ class CertificateItem(OrderItem):
user=order.user, user=order.user,
course_id=course_id, course_id=course_id,
course_enrollment=course_enrollment, course_enrollment=course_enrollment,
mode=mode mode=mode,
) )
item.status = order.status item.status = order.status
item.qty = 1 item.qty = 1
...@@ -595,7 +596,6 @@ class CertificateItem(OrderItem): ...@@ -595,7 +596,6 @@ class CertificateItem(OrderItem):
log.exception( log.exception(
"Could not submit verification attempt for enrollment {}".format(self.course_enrollment) "Could not submit verification attempt for enrollment {}".format(self.course_enrollment)
) )
self.course_enrollment.change_mode(self.mode) self.course_enrollment.change_mode(self.mode)
self.course_enrollment.activate() self.course_enrollment.activate()
......
...@@ -6,7 +6,7 @@ import StringIO ...@@ -6,7 +6,7 @@ import StringIO
from textwrap import dedent from textwrap import dedent
from boto.exception import BotoServerError # this is a super-class of SESError and catches connection errors from boto.exception import BotoServerError # this is a super-class of SESError and catches connection errors
from mock import patch, MagicMock from mock import patch, MagicMock, sentinel
from django.core import mail from django.core import mail
from django.conf import settings from django.conf import settings
from django.db import DatabaseError from django.db import DatabaseError
...@@ -425,12 +425,21 @@ class CertificateItemTest(ModuleStoreTestCase): ...@@ -425,12 +425,21 @@ class CertificateItemTest(ModuleStoreTestCase):
min_price=self.cost) min_price=self.cost)
course_mode.save() course_mode.save()
patcher = patch('student.models.server_track')
self.mock_server_track = patcher.start()
self.addCleanup(patcher.stop)
crum_patcher = patch('student.models.crum.get_current_request')
self.mock_get_current_request = crum_patcher.start()
self.addCleanup(crum_patcher.stop)
self.mock_get_current_request.return_value = sentinel.request
def test_existing_enrollment(self): def test_existing_enrollment(self):
CourseEnrollment.enroll(self.user, self.course_id) CourseEnrollment.enroll(self.user, self.course_id)
cart = Order.get_cart_for_user(user=self.user) cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified') CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
# verify that we are still enrolled # verify that we are still enrolled
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id)) self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
self.mock_server_track.reset_mock()
cart.purchase() cart.purchase()
enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id) enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
self.assertEquals(enrollment.mode, u'verified') self.assertEquals(enrollment.mode, u'verified')
......
...@@ -20,7 +20,7 @@ from student.models import CourseEnrollment ...@@ -20,7 +20,7 @@ from student.models import CourseEnrollment
from course_modes.models import CourseMode from course_modes.models import CourseMode
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from shoppingcart.processors import render_purchase_form_html from shoppingcart.processors import render_purchase_form_html
from mock import patch, Mock from mock import patch, Mock, sentinel
def mock_render_purchase_form_html(*args, **kwargs): def mock_render_purchase_form_html(*args, **kwargs):
...@@ -39,6 +39,8 @@ postpay_mock = Mock() ...@@ -39,6 +39,8 @@ postpay_mock = Mock()
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class ShoppingCartViewsTests(ModuleStoreTestCase): class ShoppingCartViewsTests(ModuleStoreTestCase):
def setUp(self): def setUp(self):
patcher = patch('student.models.server_track')
self.mock_server_track = patcher.start()
self.user = UserFactory.create() self.user = UserFactory.create()
self.user.set_password('password') self.user.set_password('password')
self.user.save() self.user.save()
...@@ -53,6 +55,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase): ...@@ -53,6 +55,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.verified_course_id = 'org/test/Test_Course' self.verified_course_id = 'org/test/Test_Course'
CourseFactory.create(org='org', number='test', run='course1', display_name='Test Course') CourseFactory.create(org='org', number='test', run='course1', display_name='Test Course')
self.cart = Order.get_cart_for_user(self.user) self.cart = Order.get_cart_for_user(self.user)
self.addCleanup(patcher.stop)
def login_user(self): def login_user(self):
self.client.login(username=self.user.username, password="password") self.client.login(username=self.user.username, password="password")
...@@ -83,6 +86,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase): ...@@ -83,6 +86,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
def test_add_course_to_cart_success(self): def test_add_course_to_cart_success(self):
self.login_user() self.login_user()
reverse('shoppingcart.views.add_course_to_cart', args=[self.course_id])
resp = self.client.post(reverse('shoppingcart.views.add_course_to_cart', args=[self.course_id])) resp = self.client.post(reverse('shoppingcart.views.add_course_to_cart', args=[self.course_id]))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertTrue(PaidCourseRegistration.contained_in_order(self.cart, self.course_id)) self.assertTrue(PaidCourseRegistration.contained_in_order(self.cart, self.course_id))
...@@ -203,6 +207,55 @@ class ShoppingCartViewsTests(ModuleStoreTestCase): ...@@ -203,6 +207,55 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.assertFalse(context['any_refunds']) self.assertFalse(context['any_refunds'])
@patch('shoppingcart.views.render_to_response', render_mock) @patch('shoppingcart.views.render_to_response', render_mock)
def test_show_receipt_success_with_upgrade(self):
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_id)
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_id, self.cost, 'honor')
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
self.login_user()
# When we come from the upgrade flow, we'll have a session variable showing that
s = self.client.session
s['attempting_upgrade'] = True
s.save()
self.mock_server_track.reset_mock()
resp = self.client.get(reverse('shoppingcart.views.show_receipt', args=[self.cart.id]))
# Once they've upgraded, they're no longer *attempting* to upgrade
attempting_upgrade = self.client.session.get('attempting_upgrade', False)
self.assertFalse(attempting_upgrade)
self.assertEqual(resp.status_code, 200)
self.assertIn('FirstNameTesting123', resp.content)
self.assertIn('80.00', resp.content)
((template, context), _) = render_mock.call_args
# When we come from the upgrade flow, we get these context variables
self.assertEqual(template, 'shoppingcart/receipt.html')
self.assertEqual(context['order'], self.cart)
self.assertIn(reg_item, context['order_items'])
self.assertIn(cert_item, context['order_items'])
self.assertFalse(context['any_refunds'])
course_enrollment = CourseEnrollment.get_or_create_enrollment(self.user, self.course_id)
course_enrollment.emit_event('edx.course.enrollment.upgrade.succeeded')
self.mock_server_track.assert_any_call(
None,
'edx.course.enrollment.upgrade.succeeded',
{
'user_id': course_enrollment.user.id,
'course_id': course_enrollment.course_id,
'mode': course_enrollment.mode
}
)
@patch('shoppingcart.views.render_to_response', render_mock)
def test_show_receipt_success_refund(self): def test_show_receipt_success_refund(self):
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_id) reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_id)
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_id, self.cost, 'honor') cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_id, self.cost, 'honor')
......
...@@ -12,11 +12,14 @@ from django.views.decorators.csrf import csrf_exempt ...@@ -12,11 +12,14 @@ from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from .models import Order, PaidCourseRegistration, OrderItem from .models import Order, PaidCourseRegistration, OrderItem
from student.models import CourseEnrollment
from .processors import process_postpay_callback, render_purchase_form_html from .processors import process_postpay_callback, render_purchase_form_html
from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException
log = logging.getLogger("shoppingcart") log = logging.getLogger("shoppingcart")
EVENT_NAME_USER_UPGRADED = 'edx.course.enrollment.upgrade.succeeded'
@require_POST @require_POST
def add_course_to_cart(request, course_id): def add_course_to_cart(request, course_id):
...@@ -124,6 +127,13 @@ def show_receipt(request, ordernum): ...@@ -124,6 +127,13 @@ def show_receipt(request, ordernum):
receipt_template = order_items[0].single_item_receipt_template receipt_template = order_items[0].single_item_receipt_template
context.update(order_items[0].single_item_receipt_context) context.update(order_items[0].single_item_receipt_context)
# Only orders where order_items.count() == 1 might be attempting to upgrade
attempting_upgrade = request.session.get('attempting_upgrade', False)
if attempting_upgrade:
course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, order_items[0].course_id)
course_enrollment.emit_event(EVENT_NAME_USER_UPGRADED)
request.session['attempting_upgrade'] = False
return render_to_response(receipt_template, context) return render_to_response(receipt_template, context)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<%! <%!
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import waffle
%> %>
<%inherit file="main.html" /> <%inherit file="main.html" />
...@@ -22,6 +23,21 @@ ...@@ -22,6 +23,21 @@
$(this).closest('.message.is-expandable').toggleClass('is-expanded'); $(this).closest('.message.is-expandable').toggleClass('is-expanded');
} }
$("#upgrade-to-verified").click(function(event) {
user = $(event.target).data("user");
course = $(event.target).data("course-id");
Logger.log('edx.course.enrollment.upgrade.clicked', [user, course], null);
% if waffle.flag_is_active(request, 'alternate_upsell_copy'):
analytics.track("Clicked on Alternate Upsell Copy", {
course: course
});
% else:
analytics.track("Clicked on Regular Upsell Copy", {
course: course
});
% endif
});
$(".email-settings").click(function(event) { $(".email-settings").click(function(event) {
$("#email_settings_course_id").val( $(event.target).data("course-id") ); $("#email_settings_course_id").val( $(event.target).data("course-id") );
$("#email_settings_course_number").text( $(event.target).data("course-number") ); $("#email_settings_course_number").text( $(event.target).data("course-number") );
......
...@@ -61,13 +61,17 @@ ...@@ -61,13 +61,17 @@
<%include file='_dashboard_certificate_information.html' args='cert_status=cert_status,course=course, enrollment=enrollment'/> <%include file='_dashboard_certificate_information.html' args='cert_status=cert_status,course=course, enrollment=enrollment'/>
% endif % endif
%if course_mode_info['show_upsell']: % if course_mode_info['show_upsell']:
<div class="message message-upsell has-actions is-expandable is-shown"> <div class="message message-upsell has-actions is-expandable is-shown">
<div class="wrapper-tip"> <div class="wrapper-tip">
<h4 class="message-title"> <h4 class="message-title">
<i class="icon-caret-down ui-toggle-expansion"></i> <i class="icon-caret-down ui-toggle-expansion"></i>
<span class="value">${_("Challenge Yourself!")}</span> % if waffle.flag_is_active(request, 'alternate_upsell_copy'):
<span class="value">${_("Document your accomplishment!")}</span>
% else:
<span class="value">${_("Challenge Yourself!")}</span>
% endif
</h4> </h4>
<p class="message-copy">${_("Take this course as an ID-verified student.")}</p> <p class="message-copy">${_("Take this course as an ID-verified student.")}</p>
</div> </div>
...@@ -80,7 +84,7 @@ ...@@ -80,7 +84,7 @@
<a class="action action-upgrade" href="${reverse('course_modes_choose', kwargs={'course_id': course.id})}?upgrade=True"> <a class="action action-upgrade" href="${reverse('course_modes_choose', kwargs={'course_id': course.id})}?upgrade=True">
<img class="deco-graphic" src="${static.url('images/vcert-ribbon-s.png')}" alt="ID Verified Ribbon/Badge"> <img class="deco-graphic" src="${static.url('images/vcert-ribbon-s.png')}" alt="ID Verified Ribbon/Badge">
<span class="wrapper-copy"> <span class="wrapper-copy">
<span class="copy">${_("Upgrade to Verified Track")}</span> <span class="copy" id="upgrade-to-verified" data-course-id="${course.id}" data-user="${user.username}">${_("Upgrade to Verified Track")}</span>
</span> </span>
</a> </a>
</li> </li>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment