From 9934b648b6e007a9f1eca8c067ff7828638fff59 Mon Sep 17 00:00:00 2001 From: George Babey <gbabey@edx.org> Date: Wed, 10 Jan 2018 11:40:25 -0500 Subject: [PATCH] Add util method for localizing catalog pricing --- .../djangoapps/catalog/tests/test_utils.py | 32 +++++++++++++- openedx/core/djangoapps/catalog/utils.py | 44 +++++++++++++++++++ requirements/edx/base.txt | 3 +- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/openedx/core/djangoapps/catalog/tests/test_utils.py b/openedx/core/djangoapps/catalog/tests/test_utils.py index 27be16fd8bb..33028dfb068 100644 --- a/openedx/core/djangoapps/catalog/tests/test_utils.py +++ b/openedx/core/djangoapps/catalog/tests/test_utils.py @@ -7,17 +7,24 @@ import mock from django.contrib.auth import get_user_model from django.core.cache import cache from django.test import TestCase, override_settings +from django.test.client import RequestFactory from student.tests.factories import UserFactory from openedx.core.djangoapps.catalog.cache import PROGRAM_CACHE_KEY_TPL, SITE_PROGRAM_UUIDS_CACHE_KEY_TPL from openedx.core.djangoapps.catalog.models import CatalogIntegration -from openedx.core.djangoapps.catalog.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory, ProgramTypeFactory +from openedx.core.djangoapps.catalog.tests.factories import ( + CourseFactory, + CourseRunFactory, + ProgramFactory, + ProgramTypeFactory +) from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin from openedx.core.djangoapps.catalog.utils import ( get_course_runs, get_course_runs_for_course, get_course_run_details, get_currency_data, + get_localized_price_text, get_program_types, get_programs, get_programs_with_type @@ -266,6 +273,27 @@ class TestGetCurrency(CatalogIntegrationMixin, TestCase): self.assertEqual(data, currency_data) +@mock.patch(UTILS_MODULE + '.get_currency_data') +class TestGetLocalizedPriceText(TestCase): + """ + Tests covering converting prices to a localized currency + """ + def test_localized_string(self, mock_get_currency_data): + currency_data = { + "BEL": {"rate": 0.835621, "code": "EUR", "symbol": u"\u20ac"}, + "GBR": {"rate": 0.737822, "code": "GBP", "symbol": u"\u00a3"}, + "CAN": {"rate": 2, "code": "CAD", "symbol": "$"}, + } + mock_get_currency_data.return_value = currency_data + + request = RequestFactory().get('/dummy-url') + request.session = { + 'country_code': 'CA' + } + expected_result = '$20 CAD' + self.assertEqual(get_localized_price_text(10, request), expected_result) + + @skip_unless_lms @mock.patch(UTILS_MODULE + '.get_edx_api_data') class TestGetCourseRuns(CatalogIntegrationMixin, TestCase): @@ -278,7 +306,7 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase): self.catalog_integration = self.create_catalog_integration(cache_ttl=1) self.user = UserFactory(username=self.catalog_integration.service_username) - def assert_contract(self, call_args): # pylint: disable=redefined-builtin + def assert_contract(self, call_args): """ Verify that API data retrieval utility is used correctly. """ diff --git a/openedx/core/djangoapps/catalog/utils.py b/openedx/core/djangoapps/catalog/utils.py index b1990c12779..1eabbbfd855 100644 --- a/openedx/core/djangoapps/catalog/utils.py +++ b/openedx/core/djangoapps/catalog/utils.py @@ -2,6 +2,7 @@ import copy import datetime import logging +import pycountry from dateutil.parser import parse as datetime_parse from django.conf import settings @@ -144,6 +145,49 @@ def get_currency_data(): return [] +def format_price(price, symbol='$', code='USD'): + """ + Format the price to have the appropriate currency and digits.. + + :param price: The price amount. + :param symbol: The symbol for the price (default: $) + :param code: The currency code to be appended to the price (default: USD) + :return: A formatted price string, i.e. '$10 USD', '$10.52 USD'. + """ + if int(price) == price: + return '{}{} {}'.format(symbol, int(price), code) + return '{}{:0.2f} {}'.format(symbol, price, code) + + +def get_localized_price_text(price, request): + """ + Returns the localized converted price as string (ex. '$150 USD') + + If the users location has been added to the request, this will return the given price based on conversion rate + from the Catalog service and return a localized string otherwise will return the default price in USD + """ + user_currency = { + 'symbol': '$', + 'rate': 1, + 'code': 'USD' + } + + # session.country_code is added via CountryMiddleware in the LMS + user_location = getattr(request, 'session', {}).get('country_code') + + # Override default user_currency if location is available + if user_location and get_currency_data: + currency_data = get_currency_data() + user_country = pycountry.countries.get(alpha2=user_location) + user_currency = currency_data.get(user_country.alpha3, user_currency) + + return format_price( + price=(price * user_currency['rate']), + symbol=user_currency['symbol'], + code=user_currency['code'] + ) + + def get_programs_with_type(site, include_hidden=True): """ Return the list of programs. You can filter the types of programs returned by using the optional diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index e0be0419bda..5f2e7969f7f 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -54,7 +54,7 @@ git+https://github.com/cpennington/pylint-django@fix-field-inference-during-monk enum34==1.1.6 edx-django-oauth2-provider==1.2.5 edx-django-sites-extensions==2.3.0 -edx-enterprise==0.61.3 +edx-enterprise==0.61.4 edx-oauth2-provider==1.2.2 edx-organizations==0.4.9 edx-rest-api-client==1.7.1 @@ -86,6 +86,7 @@ piexif==1.0.2 Pillow==3.4 polib==1.0.3 psutil==1.2.1 +pycountry==1.20 pycryptodomex==3.4.7 pygments==2.2.0 pygraphviz==1.1 -- GitLab