Commit 4ecc1867 by Clinton Blackburn

Merge pull request #277 from edx/ga-update

Sending Course ID to Segment/Google Analytics
parents 0ae5322c 97395ada
...@@ -12,8 +12,9 @@ from ecommerce.settings.base import get_lms_url ...@@ -12,8 +12,9 @@ from ecommerce.settings.base import get_lms_url
from oscar.core.loading import get_class from oscar.core.loading import get_class
post_checkout = get_class('checkout.signals', 'post_checkout')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
post_checkout = get_class('checkout.signals', 'post_checkout')
# Number of orders currently supported for the email notifications # Number of orders currently supported for the email notifications
ORDER_LINE_COUNT = 1 ORDER_LINE_COUNT = 1
...@@ -42,7 +43,7 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus ...@@ -42,7 +43,7 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus
# products other than courses, and will need to change in the future. # products other than courses, and will need to change in the future.
'id': line.partner_sku, 'id': line.partner_sku,
'sku': mode_for_seat(line.product), 'sku': mode_for_seat(line.product),
'name': line.product.title, 'name': line.product.course.id,
'price': str(line.line_price_excl_tax), 'price': str(line.line_price_excl_tax),
'quantity': line.quantity, 'quantity': line.quantity,
'category': line.product.get_product_class().name, 'category': line.product.get_product_class().name,
......
...@@ -36,7 +36,7 @@ def track_completed_refund(sender, refund=None, **kwargs): # pylint: disable=un ...@@ -36,7 +36,7 @@ def track_completed_refund(sender, refund=None, **kwargs): # pylint: disable=un
# products other than courses, and will need to change in the future. # products other than courses, and will need to change in the future.
'id': line.order_line.partner_sku, 'id': line.order_line.partner_sku,
'sku': mode_for_seat(line.order_line.product), 'sku': mode_for_seat(line.order_line.product),
'name': line.order_line.product.title, 'name': line.order_line.product.course.id,
'price': str(line.line_credit_excl_tax), 'price': str(line.line_credit_excl_tax),
'quantity': -1 * line.quantity, 'quantity': -1 * line.quantity,
'category': line.order_line.product.get_product_class().name, 'category': line.order_line.product.get_product_class().name,
......
from decimal import Decimal from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.utils.text import slugify
import factory import factory
from oscar.core.loading import get_model from oscar.core.loading import get_model
from oscar.test import factories from oscar.test import factories
from oscar.test.newfactories import UserFactory from oscar.test.newfactories import UserFactory
from ecommerce.courses.models import Course
from ecommerce.extensions.refund.status import REFUND, REFUND_LINE from ecommerce.extensions.refund.status import REFUND, REFUND_LINE
...@@ -28,7 +26,7 @@ class RefundFactory(factory.DjangoModelFactory): ...@@ -28,7 +26,7 @@ class RefundFactory(factory.DjangoModelFactory):
return factories.create_order(user=self.user) return factories.create_order(user=self.user)
@factory.post_generation @factory.post_generation
def create_lines(self, create, extracted, **kwargs): # pylint: disable=unused-argument def create_lines(self, create, extracted, **kwargs): # pylint: disable=unused-argument
if not create: if not create:
return return
...@@ -54,62 +52,3 @@ class RefundLineFactory(factory.DjangoModelFactory): ...@@ -54,62 +52,3 @@ class RefundLineFactory(factory.DjangoModelFactory):
class Meta(object): class Meta(object):
model = get_model('refund', 'RefundLine') model = get_model('refund', 'RefundLine')
class CourseFactory(object):
def __init__(self, course_id, course_name):
self.course_name = course_name
self.course_id = course_id
self.modes = {}
self.partner, _created = Partner.objects.get_or_create(name='edX')
def _get_parent_seat_product(self):
seat, created = ProductClass.objects.get_or_create(slug='seat',
defaults={'track_stock': False, 'requires_shipping': False,
'name': 'Seat'})
if created:
ProductAttribute.objects.create(product_class=seat, name='course_key', code='course_key', type='text',
required=True)
ProductAttribute.objects.create(product_class=seat, name='id_verification_required',
code='id_verification_required', type='boolean', required=False)
ProductAttribute.objects.create(product_class=seat, name='certificate_type', code='certificate_type',
type='text', required=False)
slug = slugify(self.course_name)
title = u'Seat in {}'.format(self.course_name)
parent_product, created = Product.objects.get_or_create(product_class=seat, slug=slug, structure='parent',
defaults={'title': title})
if created:
parent_product.attr.course_key = self.course_id
parent_product.save()
return parent_product
def add_mode(self, name, price, id_verification_required=False):
parent_product = self._get_parent_seat_product()
certificate_type = Course.certificate_type_for_mode(name)
title = u'{certificate_type} Seat in {course_name}'.format(
certificate_type=certificate_type,
course_name=self.course_name
)
slug = slugify(u'{course_name}-seat-{certificate_type}'.format(
course_name=self.course_name,
certificate_type=certificate_type
))
child_product, created = Product.objects.get_or_create(parent=parent_product, title=title, slug=slug,
structure='child')
if created:
child_product.attr.course_key = self.course_id
child_product.attr.certificate_type = certificate_type
child_product.attr.id_verification_required = id_verification_required
child_product.save()
child_product.stockrecords.create(partner=self.partner, partner_sku=slug, num_in_stock=None,
price_currency=settings.OSCAR_DEFAULT_CURRENCY, price_excl_tax=price)
self.modes[name] = child_product
return child_product
# coding=utf-8 # coding=utf-8
from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.test import override_settings from django.test import override_settings
...@@ -9,11 +8,12 @@ from oscar.core.loading import get_model, get_class ...@@ -9,11 +8,12 @@ from oscar.core.loading import get_model, get_class
from oscar.test.factories import create_order from oscar.test.factories import create_order
from oscar.test.newfactories import BasketFactory from oscar.test.newfactories import BasketFactory
from ecommerce.courses.models import Course
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.fulfillment.status import ORDER from ecommerce.extensions.fulfillment.status import ORDER
from ecommerce.extensions.payment.tests.processors import DummyProcessor from ecommerce.extensions.payment.tests.processors import DummyProcessor
from ecommerce.extensions.refund.status import REFUND, REFUND_LINE from ecommerce.extensions.refund.status import REFUND, REFUND_LINE
from ecommerce.extensions.refund.tests.factories import CourseFactory, RefundFactory from ecommerce.extensions.refund.tests.factories import RefundFactory
post_refund = get_class('refund.signals', 'post_refund') post_refund = get_class('refund.signals', 'post_refund')
Refund = get_model('refund', 'Refund') Refund = get_model('refund', 'Refund')
...@@ -21,13 +21,12 @@ Source = get_model('payment', 'Source') ...@@ -21,13 +21,12 @@ Source = get_model('payment', 'Source')
SourceType = get_model('payment', 'SourceType') SourceType = get_model('payment', 'SourceType')
class RefundTestMixin(object): class RefundTestMixin(CourseCatalogTestMixin):
def setUp(self): def setUp(self):
super(RefundTestMixin, self).setUp() super(RefundTestMixin, self).setUp()
self.course_id = u'edX/DemoX/Demo_Course' self.course, __ = Course.objects.get_or_create(id=u'edX/DemoX/Demo_Course', name=u'edX Demó Course')
self.course = CourseFactory(self.course_id, u'edX Demó Course') self.honor_product = self.course.create_or_update_seat('honor', False, 0)
self.honor_product = self.course.add_mode('honor', 0) self.verified_product = self.course.create_or_update_seat('verified', True, 10)
self.verified_product = self.course.add_mode('verified', Decimal(10.00), id_verification_required=True)
def create_order(self, user=None, multiple_lines=False, free=False): def create_order(self, user=None, multiple_lines=False, free=False):
user = user or self.user user = user or self.user
......
...@@ -32,7 +32,7 @@ class ApiTests(RefundTestMixin, TestCase): ...@@ -32,7 +32,7 @@ class ApiTests(RefundTestMixin, TestCase):
order = self.create_order() order = self.create_order()
self.assertTrue(self.user.orders.exists()) self.assertTrue(self.user.orders.exists())
actual = find_orders_associated_with_course(self.user, self.course_id) actual = find_orders_associated_with_course(self.user, self.course.id)
self.assertEqual(actual, [order]) self.assertEqual(actual, [order])
@ddt.data('', ' ', None) @ddt.data('', ' ', None)
...@@ -44,7 +44,7 @@ class ApiTests(RefundTestMixin, TestCase): ...@@ -44,7 +44,7 @@ class ApiTests(RefundTestMixin, TestCase):
""" An empty list should be returned if the user has never placed an order. """ """ An empty list should be returned if the user has never placed an order. """
self.assertFalse(self.user.orders.exists()) self.assertFalse(self.user.orders.exists())
actual = find_orders_associated_with_course(self.user, self.course_id) actual = find_orders_associated_with_course(self.user, self.course.id)
self.assertEqual(actual, []) self.assertEqual(actual, [])
@ddt.data(ORDER.OPEN, ORDER.FULFILLMENT_ERROR) @ddt.data(ORDER.OPEN, ORDER.FULFILLMENT_ERROR)
...@@ -54,7 +54,7 @@ class ApiTests(RefundTestMixin, TestCase): ...@@ -54,7 +54,7 @@ class ApiTests(RefundTestMixin, TestCase):
order.status = status order.status = status
order.save() order.save()
actual = find_orders_associated_with_course(self.user, self.course_id) actual = find_orders_associated_with_course(self.user, self.course.id)
self.assertEqual(actual, []) self.assertEqual(actual, [])
# TODO Implement this when we begin storing the verification close date. # TODO Implement this when we begin storing the verification close date.
...@@ -67,7 +67,7 @@ class ApiTests(RefundTestMixin, TestCase): ...@@ -67,7 +67,7 @@ class ApiTests(RefundTestMixin, TestCase):
def test_create_refunds(self): def test_create_refunds(self):
""" The method should create refunds for orders/lines that have not been refunded. """ """ The method should create refunds for orders/lines that have not been refunded. """
order = self.create_order() order = self.create_order()
actual = create_refunds([order], self.course_id) actual = create_refunds([order], self.course.id)
refund = Refund.objects.get(order=order) refund = Refund.objects.get(order=order)
self.assertEqual(actual, [refund]) self.assertEqual(actual, [refund])
self.assert_refund_matches_order(refund, order) self.assert_refund_matches_order(refund, order)
...@@ -77,5 +77,5 @@ class ApiTests(RefundTestMixin, TestCase): ...@@ -77,5 +77,5 @@ class ApiTests(RefundTestMixin, TestCase):
order = self.create_order() order = self.create_order()
RefundLineFactory(order_line=order.lines.first()) RefundLineFactory(order_line=order.lines.first())
actual = create_refunds([order], self.course_id) actual = create_refunds([order], self.course.id)
self.assertEqual(actual, []) self.assertEqual(actual, [])
...@@ -2,7 +2,6 @@ from django.test import TestCase, override_settings ...@@ -2,7 +2,6 @@ from django.test import TestCase, override_settings
from mock import patch from mock import patch
from oscar.test.newfactories import UserFactory from oscar.test.newfactories import UserFactory
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.refund.api import create_refunds from ecommerce.extensions.refund.api import create_refunds
from ecommerce.extensions.refund.tests.mixins import RefundTestMixin from ecommerce.extensions.refund.tests.mixins import RefundTestMixin
from ecommerce.tests.mixins import BusinessIntelligenceMixin from ecommerce.tests.mixins import BusinessIntelligenceMixin
...@@ -10,14 +9,15 @@ from ecommerce.tests.mixins import BusinessIntelligenceMixin ...@@ -10,14 +9,15 @@ from ecommerce.tests.mixins import BusinessIntelligenceMixin
@override_settings(SEGMENT_KEY='dummy-key') @override_settings(SEGMENT_KEY='dummy-key')
@patch('analytics.track') @patch('analytics.track')
class RefundTrackingTests(BusinessIntelligenceMixin, CourseCatalogTestMixin, RefundTestMixin, TestCase): class RefundTrackingTests(BusinessIntelligenceMixin, RefundTestMixin, TestCase):
"""Tests verifying the behavior of refund tracking.""" """Tests verifying the behavior of refund tracking."""
def setUp(self): def setUp(self):
super(RefundTrackingTests, self).setUp() super(RefundTrackingTests, self).setUp()
self.user = UserFactory() self.user = UserFactory()
self.order = self.create_order() self.order = self.create_order()
self.refund = create_refunds([self.order], self.course_id)[0] self.refund = create_refunds([self.order], self.course.id)[0]
def test_successful_refund_tracking(self, mock_track): def test_successful_refund_tracking(self, mock_track):
"""Verify that a successfully placed refund is tracked when Segment is enabled.""" """Verify that a successfully placed refund is tracked when Segment is enabled."""
...@@ -48,7 +48,7 @@ class RefundTrackingTests(BusinessIntelligenceMixin, CourseCatalogTestMixin, Ref ...@@ -48,7 +48,7 @@ class RefundTrackingTests(BusinessIntelligenceMixin, CourseCatalogTestMixin, Ref
to a total credit of 0. to a total credit of 0.
""" """
order = self.create_order(free=True) order = self.create_order(free=True)
create_refunds([order], self.course_id) create_refunds([order], self.course.id)
# Verify that no business intelligence event was emitted. Refunds corresponding # Verify that no business intelligence event was emitted. Refunds corresponding
# to a total credit of 0 are automatically approved upon creation. # to a total credit of 0 are automatically approved upon creation.
......
...@@ -199,7 +199,7 @@ class BusinessIntelligenceMixin(object): ...@@ -199,7 +199,7 @@ class BusinessIntelligenceMixin(object):
for line in lines: for line in lines:
tracked_product = tracked_products_dict.get(line.partner_sku) tracked_product = tracked_products_dict.get(line.partner_sku)
self.assertIsNotNone(tracked_product) self.assertIsNotNone(tracked_product)
self.assertEqual(line.product.title, tracked_product['name']) self.assertEqual(line.product.course.id, tracked_product['name'])
self.assertEqual(str(line.line_price_excl_tax), tracked_product['price']) self.assertEqual(str(line.line_price_excl_tax), tracked_product['price'])
self.assertEqual(line.quantity, tracked_product['quantity']) self.assertEqual(line.quantity, tracked_product['quantity'])
self.assertEqual(mode_for_seat(line.product), tracked_product['sku']) self.assertEqual(mode_for_seat(line.product), tracked_product['sku'])
...@@ -210,7 +210,7 @@ class BusinessIntelligenceMixin(object): ...@@ -210,7 +210,7 @@ class BusinessIntelligenceMixin(object):
for line in lines: for line in lines:
tracked_product = tracked_products_dict.get(line.order_line.partner_sku) tracked_product = tracked_products_dict.get(line.order_line.partner_sku)
self.assertIsNotNone(tracked_product) self.assertIsNotNone(tracked_product)
self.assertEqual(line.order_line.product.title, tracked_product['name']) self.assertEqual(line.order_line.product.course.id, tracked_product['name'])
self.assertEqual(str(line.line_credit_excl_tax), tracked_product['price']) self.assertEqual(str(line.line_credit_excl_tax), tracked_product['price'])
self.assertEqual(-1 * line.quantity, tracked_product['quantity']) self.assertEqual(-1 * line.quantity, tracked_product['quantity'])
self.assertEqual(mode_for_seat(line.order_line.product), tracked_product['sku']) self.assertEqual(mode_for_seat(line.order_line.product), tracked_product['sku'])
......
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