Commit e8196d97 by Julia Hansbrough

Events for entering verified flow & buying

parent 09befd2a
......@@ -17,10 +17,12 @@ from edxmako.shortcuts import render_to_response
from course_modes.models import CourseMode
from courseware.access import has_access
from student.models import CourseEnrollment
from student.models import CourseEnrollment, UserMethods
from student.views import course_from_id
from verify_student.models import SoftwareSecurePhotoVerification
EVENT_NAME_USER_CLICKED_UPGRADE = 'edx.user.upgrade.clicked'
class ChooseModeView(View):
"""
......@@ -37,6 +39,8 @@ class ChooseModeView(View):
enrollment_mode = CourseEnrollment.enrollment_mode_for_user(request.user, course_id)
upgrade = request.GET.get('upgrade', False)
if upgrade == "True":
UserMethods.emit_event(request.user, course_id, EVENT_NAME_USER_CLICKED_UPGRADE)
# verified users do not need to register or upgrade
if enrollment_mode == 'verified':
......
......@@ -16,6 +16,12 @@ import json
import logging
import uuid
import crum
from track import contexts
from track.views import server_track
from eventtracking import tracker
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_logged_in, user_logged_out
......@@ -35,9 +41,7 @@ from track import contexts
from track.views import server_track
from eventtracking import tracker
unenroll_done = Signal(providing_args=["course_enrollment"])
log = logging.getLogger(__name__)
AUDIT_LOG = logging.getLogger("audit")
......
......@@ -340,7 +340,14 @@ class EnrollInCourseTest(TestCase):
user=user,
course_id=course_id
)
self.assertFalse(enrollment_record.is_active)
def test_user_emitted_events(self):
user = User.objects.create_user("joe", "joe@joe.com", "password")
course_id = "edX/Test101/2013"
course_id_partial = "edX/Test101"
with patch('eventtracking.tracker.get_tracker', side_effect=Exception):
UserMethods.emit_event(user, course_id, "fake")
self.assertTrue(True)
# Make sure mode is updated properly if user unenrolls & re-enrolls
enrollment = CourseEnrollment.enroll(user, course_id, "verified")
......
......@@ -57,3 +57,13 @@ def course_context_from_course_id(course_id):
)
return context
def user_context(user):
"""
Creates a user context from `user`
"""
context = {
'user': user,
}
return context
......@@ -94,5 +94,7 @@ Feature: LMS.Verified certificates
And I navigate to my dashboard
Then I see the course on my dashboard
And I see that I am on the verified track
And a "edx.user.upgrade.clicked" server event is emitted
And a "edx.user.upgrade.purchased" sever event is emitted
And a "edx.course.enrollment.activated" server event is emitted
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'CertificateItem.upgrade'
db.add_column('shoppingcart_certificateitem', 'upgrade',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'CertificateItem.upgrade'
db.delete_column('shoppingcart_certificateitem', 'upgrade')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'shoppingcart.certificateitem': {
'Meta': {'object_name': 'CertificateItem', '_ormbases': ['shoppingcart.OrderItem']},
'course_enrollment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['student.CourseEnrollment']"}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
'mode': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'}),
'upgrade': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'shoppingcart.order': {
'Meta': {'object_name': 'Order'},
'bill_to_cardtype': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
'bill_to_ccnum': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}),
'bill_to_city': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'bill_to_country': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'bill_to_first': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'bill_to_last': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'bill_to_postalcode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
'bill_to_state': ('django.db.models.fields.CharField', [], {'max_length': '8', 'blank': 'True'}),
'bill_to_street1': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
'bill_to_street2': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'processor_reply_dump': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'purchase_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'shoppingcart.orderitem': {
'Meta': {'object_name': 'OrderItem'},
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
'fulfilled_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'line_desc': ('django.db.models.fields.CharField', [], {'default': "'Misc. Item'", 'max_length': '1024'}),
'order': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['shoppingcart.Order']"}),
'qty': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'cart'", 'max_length': '32'}),
'unit_cost': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'max_digits': '30', 'decimal_places': '2'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'shoppingcart.paidcourseregistration': {
'Meta': {'object_name': 'PaidCourseRegistration', '_ormbases': ['shoppingcart.OrderItem']},
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
'mode': ('django.db.models.fields.SlugField', [], {'default': "'honor'", 'max_length': '50'}),
'orderitem_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['shoppingcart.OrderItem']", 'unique': 'True', 'primary_key': 'True'})
},
'student.courseenrollment': {
'Meta': {'ordering': "('user', 'course_id')", 'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'},
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['shoppingcart']
\ No newline at end of file
......@@ -25,7 +25,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from course_modes.models import CourseMode
from edxmako.shortcuts import render_to_string
from student.views import course_from_id
from student.models import CourseEnrollment, unenroll_done
from student.models import CourseEnrollment, unenroll_done, UserMethods
from verify_student.models import SoftwareSecurePhotoVerification
......@@ -43,6 +43,8 @@ ORDER_STATUSES = (
# we need a tuple to represent the primary key of various OrderItem subclasses
OrderItemSubclassPK = namedtuple('OrderItemSubclassPK', ['cls', 'pk']) # pylint: disable=C0103
EVENT_NAME_USER_UPGRADED = 'edx.user.upgrade.purchased'
class Order(models.Model):
"""
......@@ -489,6 +491,7 @@ class CertificateItem(OrderItem):
course_id = models.CharField(max_length=128, db_index=True)
course_enrollment = models.ForeignKey(CourseEnrollment)
mode = models.SlugField()
upgrade = models.BooleanField()
@receiver(unenroll_done)
def refund_cert_callback(sender, course_enrollment=None, **kwargs):
......@@ -557,7 +560,14 @@ class CertificateItem(OrderItem):
"""
super(CertificateItem, cls).add_to_order(order, course_id, cost, currency=currency)
course_enrollment = CourseEnrollment.get_or_create_enrollment(order.user, course_id)
try:
# If a course_enrollment already exists, this is an "upgrade" order
course_enrollment = CourseEnrollment.objects.get(user=order.user, course_id=course_id)
upgrade = True
except ObjectDoesNotExist:
course_enrollment = CourseEnrollment.get_or_create_enrollment(order.user, course_id)
upgrade = False
# do some validation on the enrollment mode
valid_modes = CourseMode.modes_for_course_dict(course_id)
......@@ -570,7 +580,8 @@ class CertificateItem(OrderItem):
user=order.user,
course_id=course_id,
course_enrollment=course_enrollment,
mode=mode
mode=mode,
upgrade=upgrade,
)
item.status = order.status
item.qty = 1
......@@ -595,7 +606,8 @@ class CertificateItem(OrderItem):
log.exception(
"Could not submit verification attempt for enrollment {}".format(self.course_enrollment)
)
if self.upgrade is True:
UserMethods.emit_event(self.user, self.course_enrollment.course_id, EVENT_NAME_USER_UPGRADED)
self.course_enrollment.change_mode(self.mode)
self.course_enrollment.activate()
......
......@@ -6,7 +6,7 @@ import StringIO
from textwrap import dedent
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.conf import settings
from django.db import DatabaseError
......@@ -425,15 +425,37 @@ class CertificateItemTest(ModuleStoreTestCase):
min_price=self.cost)
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):
CourseEnrollment.enroll(self.user, self.course_id)
enrollment = CourseEnrollment.enroll(self.user, self.course_id)
cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
# verify that we are still enrolled
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
self.mock_server_track.reset_mock()
cart.purchase()
enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
self.assertEquals(enrollment.mode, u'verified')
self.assert_upgrade_event_was_emitted(self.user, self.course_id)
def assert_upgrade_event_was_emitted(self, user, course_id):
""" Helper function; checks that a particular was called only once """
self.mock_server_track.assert_called_once_with(
sentinel.request,
'edx.user.upgrade.purchased',
{
'user': user,
'course_id': course_id,
}
)
self.mock_server_track.reset_mock()
def test_single_item_template(self):
cart = Order.get_cart_for_user(user=self.user)
......
......@@ -83,6 +83,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
def test_add_course_to_cart_success(self):
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]))
self.assertEqual(resp.status_code, 200)
self.assertTrue(PaidCourseRegistration.contained_in_order(self.cart, self.course_id))
......
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