Commit 96eac3b5 by Clinton Blackburn Committed by Clinton Blackburn

Updated date publishing for E-Commerce API calls

- Verification deadline now defaults to the course end date
- Product expiration date (upgrade deadline) now defaults to course a few days before the course end date

LEARNER-2621
parent 7008beb1
import datetime
import random
import pytest
from course_discovery.apps.core.utils import serialize_datetime
from course_discovery.apps.publisher.api.utils import serialize_seat_for_ecommerce_api
from course_discovery.apps.publisher.models import Seat
from course_discovery.apps.publisher.tests.factories import SeatFactory
@pytest.mark.django_db
class TestSerializeSeatForEcommerceApi:
def test_serialize_seat_for_ecommerce_api(self):
seat = SeatFactory()
actual = serialize_seat_for_ecommerce_api(seat)
assert actual['price'] == str(seat.price)
assert actual['product_class'] == 'Seat'
def test_serialize_seat_for_ecommerce_api_with_audit_seat(self):
seat = SeatFactory(type=Seat.AUDIT)
actual = serialize_seat_for_ecommerce_api(seat)
expected = {
'expires': serialize_datetime(seat.upgrade_deadline),
'price': str(seat.price),
'product_class': 'Seat',
'attribute_values': [
{
'name': 'certificate_type',
'value': None,
},
{
'name': 'id_verification_required',
'value': False,
}
]
}
assert actual == expected
def test_serialize_seat_for_ecommerce_api_without_upgrade_deadline(self, settings):
settings.PUBLISHER_UPGRADE_DEADLINE_DAYS = random.randint(1, 21)
now = datetime.datetime.utcnow()
seat = SeatFactory(upgrade_deadline=None, course_run__end=now)
actual = serialize_seat_for_ecommerce_api(seat)
assert actual['expires'] == serialize_datetime(
now - datetime.timedelta(days=settings.PUBLISHER_UPGRADE_DEADLINE_DAYS))
@pytest.mark.parametrize('seat_type', (Seat.VERIFIED, Seat.PROFESSIONAL))
def test_serialize_seat_for_ecommerce_api_with_id_verification(self, seat_type):
seat = SeatFactory(type=seat_type)
actual = serialize_seat_for_ecommerce_api(seat)
expected_attribute_values = [
{
'name': 'certificate_type',
'value': seat_type,
},
{
'name': 'id_verification_required',
'value': True,
}
]
assert actual['attribute_values'] == expected_attribute_values
import datetime
from django.conf import settings
from course_discovery.apps.core.utils import serialize_datetime
from course_discovery.apps.publisher.models import Seat
def serialize_seat_for_ecommerce_api(seat):
upgrade_deadline = seat.upgrade_deadline
if not upgrade_deadline:
upgrade_deadline = seat.course_run.end - datetime.timedelta(days=settings.PUBLISHER_UPGRADE_DEADLINE_DAYS)
return {
'expires': serialize_datetime(upgrade_deadline),
'price': str(seat.price),
'product_class': 'Seat',
'attribute_values': [
{
'name': 'certificate_type',
'value': None if seat.type == Seat.AUDIT else seat.type,
},
{
'name': 'id_verification_required',
'value': seat.type in (Seat.VERIFIED, Seat.PROFESSIONAL),
}
]
}
...@@ -7,11 +7,14 @@ from rest_framework.test import APITestCase ...@@ -7,11 +7,14 @@ from rest_framework.test import APITestCase
from course_discovery.apps.core.models import Partner from course_discovery.apps.core.models import Partner
from course_discovery.apps.core.tests.factories import StaffUserFactory, UserFactory from course_discovery.apps.core.tests.factories import StaffUserFactory, UserFactory
from course_discovery.apps.core.utils import serialize_datetime
from course_discovery.apps.course_metadata.models import CourseRun, Video from course_discovery.apps.course_metadata.models import CourseRun, Video
from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory, PersonFactory from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory, PersonFactory
from course_discovery.apps.ietf_language_tags.models import LanguageTag from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.api.utils import serialize_seat_for_ecommerce_api
from course_discovery.apps.publisher.api.v1.views import CourseRunViewSet from course_discovery.apps.publisher.api.v1.views import CourseRunViewSet
from course_discovery.apps.publisher.tests.factories import CourseRunFactory from course_discovery.apps.publisher.models import Seat
from course_discovery.apps.publisher.tests.factories import CourseRunFactory, SeatFactory
class CourseRunViewSetTests(APITestCase): class CourseRunViewSetTests(APITestCase):
...@@ -64,10 +67,32 @@ class CourseRunViewSetTests(APITestCase): ...@@ -64,10 +67,32 @@ class CourseRunViewSetTests(APITestCase):
url = '{root}publication/'.format(root=partner.ecommerce_api_url) url = '{root}publication/'.format(root=partner.ecommerce_api_url)
responses.add(responses.POST, url, json=body, status=status) responses.add(responses.POST, url, json=body, status=status)
def serialize_seat_for_ecommerce_api(self, seat):
return {
'expires': serialize_datetime(seat.upgrade_deadline or seat.course_run.end),
'price': str(seat.price),
'product_class': 'Seat',
'attribute_values': [
{
'name': 'certificate_type',
'value': None if seat.type is Seat.AUDIT else seat.type,
},
{
'name': 'id_verification_required',
'value': seat.type in (Seat.VERIFIED, Seat.PROFESSIONAL),
}
]
}
@responses.activate @responses.activate
@mock.patch.object(Partner, 'access_token', return_value='JWT fake') @mock.patch.object(Partner, 'access_token', return_value='JWT fake')
def test_publish(self, mock_access_token): # pylint: disable=unused-argument def test_publish(self, mock_access_token): # pylint: disable=unused-argument,too-many-statements
publisher_course_run = self._create_course_run_for_publication() publisher_course_run = self._create_course_run_for_publication()
audit_seat = SeatFactory(course_run=publisher_course_run, type=Seat.AUDIT, upgrade_deadline=None)
professional_seat = SeatFactory(course_run=publisher_course_run, type=Seat.PROFESSIONAL)
verified_seat = SeatFactory(course_run=publisher_course_run, type=Seat.VERIFIED)
partner = publisher_course_run.course.organizations.first().partner partner = publisher_course_run.course.organizations.first().partner
self._set_test_client_domain_and_login(partner) self._set_test_client_domain_and_login(partner)
...@@ -85,6 +110,16 @@ class CourseRunViewSetTests(APITestCase): ...@@ -85,6 +110,16 @@ class CourseRunViewSetTests(APITestCase):
} }
assert response.data == expected assert response.data == expected
# Verify the correct deadlines were sent to the E-Commerce API
ecommerce_body = json.loads(responses.calls[2].request.body)
expected = [
serialize_seat_for_ecommerce_api(audit_seat),
serialize_seat_for_ecommerce_api(professional_seat),
serialize_seat_for_ecommerce_api(verified_seat),
]
assert ecommerce_body['products'] == expected
assert ecommerce_body['verification_deadline'] == serialize_datetime(publisher_course_run.end)
discovery_course_run = CourseRun.objects.get(key=publisher_course_run.lms_course_id) discovery_course_run = CourseRun.objects.get(key=publisher_course_run.lms_course_id)
publisher_course = publisher_course_run.course publisher_course = publisher_course_run.course
discovery_course = discovery_course_run.course discovery_course = discovery_course_run.course
......
...@@ -11,7 +11,8 @@ from slumber.exceptions import SlumberBaseException ...@@ -11,7 +11,8 @@ from slumber.exceptions import SlumberBaseException
from course_discovery.apps.core.utils import serialize_datetime from course_discovery.apps.core.utils import serialize_datetime
from course_discovery.apps.course_metadata.models import CourseRun as DiscoveryCourseRun from course_discovery.apps.course_metadata.models import CourseRun as DiscoveryCourseRun
from course_discovery.apps.course_metadata.models import Course, Video from course_discovery.apps.course_metadata.models import Course, Video
from course_discovery.apps.publisher.models import CourseRun, Seat from course_discovery.apps.publisher.api.utils import serialize_seat_for_ecommerce_api
from course_discovery.apps.publisher.models import CourseRun
from course_discovery.apps.publisher.studio_api_utils import StudioAPI from course_discovery.apps.publisher.studio_api_utils import StudioAPI
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -72,25 +73,10 @@ class CourseRunViewSet(viewsets.GenericViewSet): ...@@ -72,25 +73,10 @@ class CourseRunViewSet(viewsets.GenericViewSet):
data = { data = {
'id': course_run.lms_course_id, 'id': course_run.lms_course_id,
'name': course_run.title_override or course_run.course.title, 'name': course_run.title_override or course_run.course.title,
'verification_deadline': None, 'verification_deadline': serialize_datetime(course_run.end),
'create_or_activate_enrollment_code': False, 'create_or_activate_enrollment_code': False,
'products': [ # NOTE (CCB): We only order here to aid testing. The E-Commerce API does NOT care about ordering.
{ 'products': [serialize_seat_for_ecommerce_api(seat) for seat in course_run.seats.all().order_by('created')],
'expires': serialize_datetime(seat.upgrade_deadline),
'price': str(seat.price),
'product_class': 'Seat',
'attribute_values': [
{
'name': 'certificate_type',
'value': None if seat.type is Seat.AUDIT else seat.type,
},
{
'name': 'id_verification_required',
'value': seat.type in (Seat.VERIFIED, Seat.PROFESSIONAL),
}
]
} for seat in course_run.seats.all()
]
} }
try: try:
......
...@@ -501,6 +501,10 @@ SOLO_CACHE_TIMEOUT = 3600 ...@@ -501,6 +501,10 @@ SOLO_CACHE_TIMEOUT = 3600
PUBLISHER_FROM_EMAIL = None PUBLISHER_FROM_EMAIL = None
# If no upgrade deadline is specified for a course run seat, when the course is published the deadline will default to
# the course run end date minus the specified number of days.
PUBLISHER_UPGRADE_DEADLINE_DAYS = 10
# Django Debug Toolbar settings # Django Debug Toolbar settings
# http://django-debug-toolbar.readthedocs.org/en/latest/installation.html # http://django-debug-toolbar.readthedocs.org/en/latest/installation.html
if os.environ.get('ENABLE_DJANGO_TOOLBAR', False): if os.environ.get('ENABLE_DJANGO_TOOLBAR', False):
......
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