Skip to content

Commit

Permalink
feat: Publish mobile skus to lms (openedx-unsupported#4057)
Browse files Browse the repository at this point in the history
  • Loading branch information
moeez96 authored Nov 23, 2023
1 parent 7aa3886 commit e53555c
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 1 deletion.
22 changes: 21 additions & 1 deletion ecommerce/courses/publishers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import logging
from urllib.parse import urljoin

from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from oscar.core.loading import get_model
from requests.exceptions import HTTPError

from ecommerce.core.constants import ENROLLMENT_CODE_SEAT_TYPES
from ecommerce.courses.constants import CertificateType
from ecommerce.courses.utils import mode_for_product

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -36,13 +38,27 @@ def serialize_seat_for_commerce_api(self, seat):
if enrollment_code:
bulk_sku = enrollment_code.stockrecords.first().partner_sku

android_sku = None
ios_sku = None
if getattr(seat.attr, 'certificate_type', '') == CertificateType.VERIFIED:
android_stock_record = StockRecord.objects.filter(
product__parent=seat.parent, partner_sku__contains='mobile.android').first()
ios_stock_record = StockRecord.objects.filter(
product__parent=seat.parent, partner_sku__contains='mobile.ios').first()
if android_stock_record:
android_sku = android_stock_record.partner_sku
if ios_stock_record:
ios_sku = ios_stock_record.partner_sku

return {
'name': mode_for_product(seat),
'currency': stock_record.price_currency,
'price': int(stock_record.price_excl_tax),
'sku': stock_record.partner_sku,
'bulk_sku': bulk_sku,
'expires': self.get_seat_expiration(seat),
'android_sku': android_sku,
'ios_sku': ios_sku,
}

def publish(self, course):
Expand All @@ -63,7 +79,11 @@ def publish(self, course):

name = course.name
verification_deadline = self.get_course_verification_deadline(course)
modes = [self.serialize_seat_for_commerce_api(seat) for seat in course.seat_products]

# Do not fetch mobile seats to create Course modes. Mobile skus are
# added to the verified course mode in serialize_seat_for_commerce_api()
seat_products = course.seat_products.filter(~Q(stockrecords__partner_sku__contains="mobile"))
modes = [self.serialize_seat_for_commerce_api(seat) for seat in seat_products]

has_credit = 'credit' in [mode['name'] for mode in modes]
if has_credit:
Expand Down
72 changes: 72 additions & 0 deletions ecommerce/courses/tests/test_publishers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import ddt
import mock
import responses
from django.db.models import Q
from django.utils import timezone
from oscar.core.loading import get_model
from requests import Timeout
Expand All @@ -16,6 +17,7 @@
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.publishers import LMSPublisher
from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.catalogue.models import Product
from ecommerce.extensions.catalogue.tests.mixins import DiscoveryTestMixin
from ecommerce.tests.testcases import TestCase

Expand All @@ -41,6 +43,39 @@ def setUp(self):
self.publisher = LMSPublisher()
self.error_message = 'Failed to publish commerce data for {course_id} to LMS.'.format(course_id=self.course.id)

def _create_mobile_seat_for_course(self, course, sku_prefix):
""" Create a mobile seat for a course given the sku_prefix """
web_seat = Product.objects.filter(
~Q(stockrecords__partner_sku__icontains="mobile"),
parent__isnull=False,
course=course,
attributes__name="id_verification_required",
parent__product_class__name="Seat"
).first()
web_stock_record = web_seat.stockrecords.first()
mobile_seat = Product.objects.create(
course=course,
parent=web_seat.parent,
structure=web_seat.structure,
expires=web_seat.expires,
is_public=web_seat.is_public,
title="{} {}".format(sku_prefix.capitalize(), web_seat.title.lower())
)

mobile_seat.attr.certificate_type = web_seat.attr.certificate_type
mobile_seat.attr.course_key = web_seat.attr.course_key
mobile_seat.attr.id_verification_required = web_seat.attr.id_verification_required
mobile_seat.attr.save()

StockRecord.objects.create(
partner=web_stock_record.partner,
product=mobile_seat,
partner_sku="mobile.{}.{}".format(sku_prefix.lower(), web_stock_record.partner_sku.lower()),
price_currency=web_stock_record.price_currency,
price_excl_tax=web_stock_record.price_excl_tax,
)
return mobile_seat

def tearDown(self):
super().tearDown()
responses.stop()
Expand Down Expand Up @@ -133,6 +168,39 @@ def test_serialize_seat_for_commerce_api(self):
'sku': stock_record.partner_sku,
'bulk_sku': None,
'expires': None,
'android_sku': None,
'ios_sku': None,
}
self.assertDictEqual(actual, expected)

# Try with an expiration datetime
expires = datetime.datetime.utcnow()
seat.expires = expires
expected['expires'] = expires.isoformat()
actual = self.publisher.serialize_seat_for_commerce_api(seat)
self.assertDictEqual(actual, expected)

def test_serialize_seat_for_commerce_api_with_mobile_skus(self):
""" Verify that mobile SKUS are added to the JSON serializable key as expected. """
# Grab the verified seat
self._create_mobile_seat_for_course(self.course, 'android')
self._create_mobile_seat_for_course(self.course, 'ios')
seat = self.course.seat_products.filter(
~Q(stockrecords__partner_sku__contains="mobile"),
attribute_values__value_text='verified',
).first()
stock_record = seat.stockrecords.first()

actual = self.publisher.serialize_seat_for_commerce_api(seat)
expected = {
'name': 'verified',
'currency': 'USD',
'price': int(stock_record.price_excl_tax),
'sku': stock_record.partner_sku,
'bulk_sku': None,
'expires': None,
'android_sku': 'mobile.android.{}'.format(stock_record.partner_sku.lower()),
'ios_sku': 'mobile.ios.{}'.format(stock_record.partner_sku.lower()),
}
self.assertDictEqual(actual, expected)

Expand Down Expand Up @@ -165,6 +233,8 @@ def test_serialize_seat_for_commerce_api_with_professional(self, is_verified, ex
'sku': stock_record.partner_sku,
'bulk_sku': None,
'expires': None,
'android_sku': None,
'ios_sku': None,
}
self.assertDictEqual(actual, expected)

Expand All @@ -181,6 +251,8 @@ def test_serialize_seat_with_enrollment_code(self):
'sku': stock_record.partner_sku,
'bulk_sku': ec_stock_record.partner_sku,
'expires': None,
'android_sku': None,
'ios_sku': None,
}
self.assertDictEqual(actual, expected)

Expand Down

0 comments on commit e53555c

Please sign in to comment.