Commit 58c45e26 by McKenzie Welter Committed by McKenzie Welter

Track bundle purchases in Order Completed GA event

parent 6ddbbf41
...@@ -402,6 +402,7 @@ class CouponMixin(object): ...@@ -402,6 +402,7 @@ class CouponMixin(object):
request.site = self.site request.site = self.site
request.user = factories.UserFactory() request.user = factories.UserFactory()
request.COOKIES = {} request.COOKIES = {}
request.GET = {}
self.basket = prepare_basket(request, [coupon]) self.basket = prepare_basket(request, [coupon])
......
...@@ -18,6 +18,7 @@ class CouponAppViewTests(TestCase): ...@@ -18,6 +18,7 @@ class CouponAppViewTests(TestCase):
self.user = self.create_user(email='test@tester.fake') self.user = self.create_user(email='test@tester.fake')
self.request.user = self.user self.request.user = self.user
self.request.GET = {}
@ddt.data( @ddt.data(
(['verIfiEd', 'profeSSional'], 'verified,professional'), (['verIfiEd', 'profeSSional'], 'verified,professional'),
......
...@@ -90,6 +90,7 @@ class CouponViewSetTest(CouponMixin, DiscoveryTestMixin, TestCase): ...@@ -90,6 +90,7 @@ class CouponViewSetTest(CouponMixin, DiscoveryTestMixin, TestCase):
request.data = self.coupon_data request.data = self.coupon_data
request.site = self.site request.site = self.site
request.COOKIES = {} request.COOKIES = {}
request.GET = {}
return request return request
def test_create(self): def test_create(self):
......
...@@ -25,6 +25,9 @@ from ecommerce.tests.testcases import TestCase, TransactionTestCase ...@@ -25,6 +25,9 @@ from ecommerce.tests.testcases import TestCase, TransactionTestCase
Benefit = get_model('offer', 'Benefit') Benefit = get_model('offer', 'Benefit')
Basket = get_model('basket', 'Basket') Basket = get_model('basket', 'Basket')
BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
BUNDLE = 'bundle_identifier'
Product = get_model('catalogue', 'Product') Product = get_model('catalogue', 'Product')
...@@ -359,6 +362,20 @@ class BasketUtilsTests(DiscoveryTestMixin, TestCase): ...@@ -359,6 +362,20 @@ class BasketUtilsTests(DiscoveryTestMixin, TestCase):
basket = prepare_basket(self.request, [enrollment_code]) basket = prepare_basket(self.request, [enrollment_code])
self.assertIsNotNone(basket) self.assertIsNotNone(basket)
def test_prepare_basket_with_bundle(self):
"""
Test prepare_basket updates or creates a basket attribute for the associated bundle
"""
product = ProductFactory()
request = self.request
basket = prepare_basket(request, [product])
with self.assertRaises(BasketAttribute.DoesNotExist):
BasketAttribute.objects.get(basket=basket, attribute_type__name=BUNDLE)
request.GET = {'bundle': 'test_bundle'}
basket = prepare_basket(request, [product])
bundle_id = BasketAttribute.objects.get(basket=basket, attribute_type__name=BUNDLE).value_text
self.assertEqual(bundle_id, 'test_bundle')
class BasketUtilsTransactionTests(TransactionTestCase): class BasketUtilsTransactionTests(TransactionTestCase):
def setUp(self): def setUp(self):
......
...@@ -18,6 +18,9 @@ from ecommerce.referrals.models import Referral ...@@ -18,6 +18,9 @@ from ecommerce.referrals.models import Referral
Applicator = get_class('offer.utils', 'Applicator') Applicator = get_class('offer.utils', 'Applicator')
Basket = get_model('basket', 'Basket') Basket = get_model('basket', 'Basket')
BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
BUNDLE = 'bundle_identifier'
StockRecord = get_model('partner', 'StockRecord') StockRecord = get_model('partner', 'StockRecord')
OrderLine = get_model('order', 'Line') OrderLine = get_model('order', 'Line')
Refund = get_model('refund', 'Refund') Refund = get_model('refund', 'Refund')
...@@ -59,6 +62,14 @@ def prepare_basket(request, products, voucher=None): ...@@ -59,6 +62,14 @@ def prepare_basket(request, products, voucher=None):
basket.save() basket.save()
basket_addition = get_class('basket.signals', 'basket_addition') basket_addition = get_class('basket.signals', 'basket_addition')
already_purchased_products = [] already_purchased_products = []
bundle = request.GET.get('bundle')
if bundle:
BasketAttribute.objects.update_or_create(
basket=basket,
attribute_type=BasketAttributeType.objects.get(name=BUNDLE),
value_text=bundle
)
if request.site.siteconfiguration.enable_embargo_check: if request.site.siteconfiguration.enable_embargo_check:
if not embargo_check(request.user, request.site, products): if not embargo_check(request.user, request.site, products):
......
...@@ -2,13 +2,17 @@ import logging ...@@ -2,13 +2,17 @@ import logging
import waffle import waffle
from django.dispatch import receiver from django.dispatch import receiver
from oscar.core.loading import get_class from oscar.core.loading import get_class, get_model
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.analytics.utils import silence_exceptions, track_segment_event from ecommerce.extensions.analytics.utils import silence_exceptions, track_segment_event
from ecommerce.extensions.checkout.utils import get_credit_provider_details, get_receipt_page_url from ecommerce.extensions.checkout.utils import get_credit_provider_details, get_receipt_page_url
from ecommerce.notifications.notifications import send_notification from ecommerce.notifications.notifications import send_notification
from ecommerce.programs.utils import get_program
BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
BUNDLE = 'bundle_identifier'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
post_checkout = get_class('checkout.signals', 'post_checkout') post_checkout = get_class('checkout.signals', 'post_checkout')
...@@ -29,6 +33,10 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus ...@@ -29,6 +33,10 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus
voucher = order.basket_discounts.filter(voucher_id__isnull=False).first() voucher = order.basket_discounts.filter(voucher_id__isnull=False).first()
coupon = voucher.voucher_code if voucher else None coupon = voucher.voucher_code if voucher else None
try:
bundle_id = BasketAttribute.objects.get(basket=order.basket, attribute_type__name=BUNDLE).value_text
except BasketAttribute.DoesNotExist:
bundle_id = None
properties = { properties = {
'orderId': order.number, 'orderId': order.number,
...@@ -51,6 +59,26 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus ...@@ -51,6 +59,26 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus
} for line in order.lines.all() } for line in order.lines.all()
], ],
} }
try:
bundle_id = BasketAttribute.objects.get(basket=order.basket, attribute_type__name=BUNDLE).value_text
program = get_program(bundle_id, order.basket.site.siteconfiguration)
if len(order.lines.all()) < len(program['courses']):
variant = 'partial'
else:
variant = 'full'
bundle_product = {
'id': bundle_id,
'price': '0',
'quantity': str(len(order.lines.all())),
'category': 'bundle',
'variant': variant,
'name': program['name']
}
properties['products'].append(bundle_product)
except BasketAttribute.DoesNotExist:
logger.info('There is no program or bundle associated with order number %s', order.number)
track_segment_event(order.site, order.user, 'Order Completed', properties) track_segment_event(order.site, order.user, 'Order Completed', properties)
......
...@@ -13,24 +13,26 @@ from ecommerce.core.tests import toggle_switch ...@@ -13,24 +13,26 @@ from ecommerce.core.tests import toggle_switch
from ecommerce.coupons.tests.mixins import CouponMixin from ecommerce.coupons.tests.mixins import CouponMixin
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.catalogue.tests.mixins import DiscoveryTestMixin
from ecommerce.extensions.checkout.signals import send_course_purchase_email, track_completed_order from ecommerce.extensions.checkout.signals import send_course_purchase_email, track_completed_order
from ecommerce.extensions.checkout.utils import get_receipt_page_url from ecommerce.extensions.checkout.utils import get_receipt_page_url
from ecommerce.extensions.test.factories import create_order, prepare_voucher from ecommerce.extensions.test.factories import create_order, prepare_voucher
from ecommerce.programs.tests.mixins import ProgramTestMixin
from ecommerce.tests.factories import ProductFactory from ecommerce.tests.factories import ProductFactory
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
Applicator = get_class('offer.utils', 'Applicator') Applicator = get_class('offer.utils', 'Applicator')
BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
Benefit = get_model('offer', 'Benefit') Benefit = get_model('offer', 'Benefit')
Condition = get_model('offer', 'Condition') Condition = get_model('offer', 'Condition')
ConditionalOffer = get_model('offer', 'ConditionalOffer') ConditionalOffer = get_model('offer', 'ConditionalOffer')
Product = get_model('catalogue', 'Product')
BUNDLE = 'bundle_identifier'
LOGGER_NAME = 'ecommerce.extensions.checkout.signals' LOGGER_NAME = 'ecommerce.extensions.checkout.signals'
Product = get_model('catalogue', 'Product')
class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase): class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
def setUp(self): def setUp(self):
super(SignalTests, self).setUp() super(SignalTests, self).setUp()
self.user = self.create_user() self.user = self.create_user()
...@@ -55,6 +57,12 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase): ...@@ -55,6 +57,12 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase):
order = create_order(basket=basket, user=self.user) order = create_order(basket=basket, user=self.user)
return order return order
def mock_get_program_data(self, isFull):
data = {'name': 'test_program', 'courses': [{}]}
if isFull:
data['courses'].append({})
return data
@httpretty.activate @httpretty.activate
def test_post_checkout_callback(self): def test_post_checkout_callback(self):
""" """
...@@ -122,9 +130,9 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase): ...@@ -122,9 +130,9 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase):
) )
) )
def _generate_event_properties(self, order, voucher=None): def _generate_event_properties(self, order, voucher=None, bundle_id=None, fullBundle=False):
coupon = voucher.code if voucher else None coupon = voucher.code if voucher else None
return { properties = {
'orderId': order.number, 'orderId': order.number,
'total': str(order.total_excl_tax), 'total': str(order.total_excl_tax),
'currency': order.currency, 'currency': order.currency,
...@@ -141,6 +149,24 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase): ...@@ -141,6 +149,24 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase):
} for line in order.lines.all() } for line in order.lines.all()
], ],
} }
if bundle_id:
program = self.mock_get_program_data(fullBundle)
if len(order.lines.all()) < len(program['courses']):
variant = 'partial'
else:
variant = 'full'
bundle_product = {
'id': bundle_id,
'price': '0',
'quantity': str(len(order.lines.all())),
'category': 'bundle',
'variant': variant,
'name': program['name']
}
properties['products'].append(bundle_product)
return properties
def test_track_completed_order(self): def test_track_completed_order(self):
""" An event should be sent to Segment. """ """ An event should be sent to Segment. """
...@@ -151,13 +177,38 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase): ...@@ -151,13 +177,38 @@ class SignalTests(DiscoveryTestMixin, CouponMixin, TestCase):
properties = self._generate_event_properties(order) properties = self._generate_event_properties(order)
mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties) mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
# We should be able to fire events even if the product is not releated to a course. # We should be able to fire events even if the product is not related to a course.
mock_track.reset_mock() mock_track.reset_mock()
order = create_order() order = create_order()
track_completed_order(None, order) track_completed_order(None, order)
properties = self._generate_event_properties(order) properties = self._generate_event_properties(order)
mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties) mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
@mock.patch('ecommerce.extensions.checkout.signals.track_segment_event')
def test_track_bundle_order(self, mock_track):
""" If the order is a bundle purchase, we should track the associated bundle in the properties """
order = self.prepare_order('verified')
BasketAttribute.objects.update_or_create(
basket=order.basket,
attribute_type=BasketAttributeType.objects.get(name=BUNDLE),
value_text='test_bundle'
)
# Tracks a full bundle order
with mock.patch('ecommerce.extensions.checkout.signals.get_program',
mock.Mock(return_value=self.mock_get_program_data(True))):
track_completed_order(None, order)
properties = self._generate_event_properties(order, bundle_id='test_bundle', fullBundle=True)
mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
# Tracks a partial bundle order
with mock.patch('ecommerce.extensions.checkout.signals.get_program',
mock.Mock(return_value=self.mock_get_program_data(False))):
mock_track.reset_mock()
track_completed_order(None, order)
properties = self._generate_event_properties(order, bundle_id='test_bundle')
mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
def test_track_completed_discounted_order_with_voucher(self): def test_track_completed_discounted_order_with_voucher(self):
""" An event including coupon information should be sent to Segment""" """ An event including coupon information should be sent to Segment"""
with mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') as mock_track: with mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') as mock_track:
......
...@@ -11,7 +11,7 @@ from oscar.core.loading import get_model ...@@ -11,7 +11,7 @@ from oscar.core.loading import get_model
from slumber.exceptions import HttpNotFoundError, SlumberBaseException from slumber.exceptions import HttpNotFoundError, SlumberBaseException
from ecommerce.core.utils import get_cache_key from ecommerce.core.utils import get_cache_key
from ecommerce.programs.api import ProgramsApiClient from ecommerce.programs.utils import get_program
Condition = get_model('offer', 'Condition') Condition = get_model('offer', 'Condition')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -26,27 +26,10 @@ class ProgramCourseRunSeatsCondition(Condition): ...@@ -26,27 +26,10 @@ class ProgramCourseRunSeatsCondition(Condition):
def name(self): def name(self):
return 'Basket contains a seat for every course in program {}'.format(self.program_uuid) return 'Basket contains a seat for every course in program {}'.format(self.program_uuid)
def get_program(self, site_configuration):
"""
Returns details for the program associated with this condition.
Data is retrieved from the Discovery Service, and cached for ``settings.PROGRAM_CACHE_TIMEOUT`` seconds.
Args:
site_configuration (SiteConfiguration): Configuration containing the requisite parameters
to connect to the Discovery Service.
Returns:
dict
"""
program_uuid = str(self.program_uuid)
client = ProgramsApiClient(site_configuration.discovery_api_client, site_configuration.site.domain)
return client.get_program(program_uuid)
def get_applicable_skus(self, site_configuration): def get_applicable_skus(self, site_configuration):
""" SKUs to which this condition applies. """ """ SKUs to which this condition applies. """
program_course_run_skus = set() program_course_run_skus = set()
program = self.get_program(site_configuration) program = get_program(self.program_uuid, site_configuration)
applicable_seat_types = program['applicable_seat_types'] applicable_seat_types = program['applicable_seat_types']
for course in program['courses']: for course in program['courses']:
...@@ -76,7 +59,7 @@ class ProgramCourseRunSeatsCondition(Condition): ...@@ -76,7 +59,7 @@ class ProgramCourseRunSeatsCondition(Condition):
basket_skus = set([line.stockrecord.partner_sku for line in basket.all_lines()]) basket_skus = set([line.stockrecord.partner_sku for line in basket.all_lines()])
try: try:
program = self.get_program(basket.site.siteconfiguration) program = get_program(self.program_uuid, basket.site.siteconfiguration)
except (HttpNotFoundError, SlumberBaseException, requests.Timeout): except (HttpNotFoundError, SlumberBaseException, requests.Timeout):
return False return False
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from ecommerce.extensions.checkout.signals import BUNDLE
def create_attribute(apps, schema_editor):
BasketAttributeType = apps.get_model('basket', 'BasketAttributeType')
BasketAttributeType.objects.create(name=BUNDLE)
def delete_attribute(apps, schema_editor):
BasketAttributeType = apps.get_model('basket', 'BasketAttributeType')
try:
BasketAttributeType.objects.get(name=BUNDLE).delete()
except BasketAttributeType.DoesNotExist:
pass
class Migration(migrations.Migration):
dependencies = [
('programs', '0001_initial'),
('basket', '0007_auto_20160907_2040')
]
operations = [
migrations.RunPython(create_attribute, delete_attribute)
]
...@@ -31,19 +31,6 @@ class ProgramCourseRunSeatsConditionTests(ProgramTestMixin, TestCase): ...@@ -31,19 +31,6 @@ class ProgramCourseRunSeatsConditionTests(ProgramTestMixin, TestCase):
self.assertEqual(condition.name, expected) self.assertEqual(condition.name, expected)
@httpretty.activate @httpretty.activate
def test_get_program(self):
"""
The method should return data from the Discovery Service API.
Data should be cached for subsequent calls.
"""
data = self.mock_program_detail_endpoint(self.condition.program_uuid, self.site_configuration.discovery_api_url)
self.assertEqual(self.condition.get_program(self.site.siteconfiguration), data)
# The program data should be cached
httpretty.disable()
self.assertEqual(self.condition.get_program(self.site.siteconfiguration), data)
@httpretty.activate
def test_is_satisfied_no_enrollments(self): def test_is_satisfied_no_enrollments(self):
""" The method should return True if the basket contains one course run seat corresponding to each """ The method should return True if the basket contains one course run seat corresponding to each
course in the program. """ course in the program. """
...@@ -136,7 +123,7 @@ class ProgramCourseRunSeatsConditionTests(ProgramTestMixin, TestCase): ...@@ -136,7 +123,7 @@ class ProgramCourseRunSeatsConditionTests(ProgramTestMixin, TestCase):
# Verify the user enrollments are cached # Verify the user enrollments are cached
basket.site.siteconfiguration.enable_partial_program = True basket.site.siteconfiguration.enable_partial_program = True
httpretty.disable() httpretty.disable()
with mock.patch('ecommerce.programs.conditions.ProgramCourseRunSeatsCondition.get_program', with mock.patch('ecommerce.programs.conditions.get_program',
return_value=program): return_value=program):
self.assertTrue(self.condition.is_satisfied(offer, basket)) self.assertTrue(self.condition.is_satisfied(offer, basket))
...@@ -147,7 +134,7 @@ class ProgramCourseRunSeatsConditionTests(ProgramTestMixin, TestCase): ...@@ -147,7 +134,7 @@ class ProgramCourseRunSeatsConditionTests(ProgramTestMixin, TestCase):
basket = factories.BasketFactory(site=self.site, owner=factories.UserFactory()) basket = factories.BasketFactory(site=self.site, owner=factories.UserFactory())
basket.add_product(self.test_product) basket.add_product(self.test_product)
with mock.patch('ecommerce.programs.conditions.ProgramCourseRunSeatsCondition.get_program', with mock.patch('ecommerce.programs.conditions.get_program',
side_effect=value): side_effect=value):
self.assertFalse(self.condition.is_satisfied(offer, basket)) self.assertFalse(self.condition.is_satisfied(offer, basket))
......
import uuid
import httpretty
from ecommerce.programs.tests.mixins import ProgramTestMixin
from ecommerce.programs.utils import get_program
from ecommerce.tests.testcases import TestCase
class UtilTests(ProgramTestMixin, TestCase):
def setUp(self):
super(UtilTests, self).setUp()
@httpretty.activate
def test_get_program(self):
"""
The method should return data from the Discovery Service API.
Data should be cached for subsequent calls.
"""
program_uuid = uuid.uuid4()
data = self.mock_program_detail_endpoint(program_uuid, self.site.siteconfiguration.discovery_api_url)
self.assertEqual(get_program(program_uuid, self.site.siteconfiguration), data)
# The program data should be cached
httpretty.disable()
self.assertEqual(get_program(program_uuid, self.site.siteconfiguration), data)
from ecommerce.programs.api import ProgramsApiClient
def get_program(program_uuid, siteconfiguration):
"""
Returns details for the program identified by the program_uuid.
Data is retrieved from the Discovery Service, and cached for ``settings.PROGRAM_CACHE_TIMEOUT`` seconds.
Args:
siteconfiguration (SiteConfiguration): Configuration containing the requisite parameters
to connect to the Discovery Service.
program_uuid (uuid): id to query the specified program
Returns:
dict
"""
client = ProgramsApiClient(siteconfiguration.discovery_api_client, siteconfiguration.site.domain)
return client.get_program(str(program_uuid))
...@@ -7,8 +7,8 @@ from django.views.generic import CreateView, ListView, UpdateView ...@@ -7,8 +7,8 @@ from django.views.generic import CreateView, ListView, UpdateView
from oscar.core.loading import get_model from oscar.core.loading import get_model
from ecommerce.core.views import StaffOnlyMixin from ecommerce.core.views import StaffOnlyMixin
from ecommerce.programs.api import ProgramsApiClient
from ecommerce.programs.forms import ProgramOfferForm from ecommerce.programs.forms import ProgramOfferForm
from ecommerce.programs.utils import get_program
Benefit = get_model('offer', 'Benefit') Benefit = get_model('offer', 'Benefit')
ConditionalOffer = get_model('offer', 'ConditionalOffer') ConditionalOffer = get_model('offer', 'ConditionalOffer')
...@@ -23,20 +23,6 @@ class ProgramOfferViewMixin(StaffOnlyMixin): ...@@ -23,20 +23,6 @@ class ProgramOfferViewMixin(StaffOnlyMixin):
context['admin'] = 'program_offers' context['admin'] = 'program_offers'
return context return context
def get_program_details(self, program_uuid):
site = self.request.site
details = {
'title': '(unknown)',
'uuid': program_uuid,
}
try:
programs_api_client = ProgramsApiClient(site.siteconfiguration.discovery_api_client, site.domain)
details = programs_api_client.get_program(program_uuid)
except: # pylint: disable=bare-except
logger.exception('Failed to retrieve program [%s] from the Programs API!', program_uuid)
return details
def get_queryset(self): def get_queryset(self):
return super(ProgramOfferViewMixin, self).get_queryset().filter( return super(ProgramOfferViewMixin, self).get_queryset().filter(
condition__program_uuid__isnull=False, condition__program_uuid__isnull=False,
...@@ -80,7 +66,8 @@ class ProgramOfferUpdateView(ProgramOfferProcessFormViewMixin, UpdateView): ...@@ -80,7 +66,8 @@ class ProgramOfferUpdateView(ProgramOfferProcessFormViewMixin, UpdateView):
context = super(ProgramOfferUpdateView, self).get_context_data(**kwargs) context = super(ProgramOfferUpdateView, self).get_context_data(**kwargs)
context.update({ context.update({
'editing': True, 'editing': True,
'program': self.get_program_details(self.object.condition.program_uuid), 'program': get_program(self.object.condition.program_uuid,
self.request.site.siteconfiguration),
}) })
return context return context
...@@ -94,7 +81,7 @@ class ProgramOfferListView(ProgramOfferViewMixin, ListView): ...@@ -94,7 +81,7 @@ class ProgramOfferListView(ProgramOfferViewMixin, ListView):
# TODO: In the future, we should optimize our API calls, pulling the program data in as few calls as possible. # TODO: In the future, we should optimize our API calls, pulling the program data in as few calls as possible.
offers = [] offers = []
for offer in context['object_list']: for offer in context['object_list']:
offer.program = self.get_program_details(offer.condition.program_uuid) offer.program = get_program(offer.condition.program_uuid, self.request.site.siteconfiguration)
offers.append(offer) offers.append(offer)
return context return context
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