Commit 7792d7eb by Matthew Piatetsky

Add currency view

LEARNER-2409
parent 0498046b
import mock
from django.core.cache import cache
from django.test import override_settings
from django.urls import reverse
from course_discovery.apps.api.v1.tests.test_views.mixins import APITestCase
from course_discovery.apps.api.v1.views.currency import CurrencyView
from course_discovery.apps.core.tests.factories import USER_PASSWORD, UserFactory
class CurrencyViewTests(APITestCase):
list_path = reverse('api:v1:currency')
def setUp(self):
super(CurrencyViewTests, self).setUp()
self.user = UserFactory(is_staff=True, is_superuser=True)
self.request.user = self.user
self.client.login(username=self.user.username, password=USER_PASSWORD)
# Clear the cache between test cases, so they don't interfere with each other.
cache.clear()
def test_get(self):
""" Verify the endpoint returns the right currency data and uses the cache. """
rates = {"GBP": 0.766609, "CAD": 1.222252, "CNY": 6.514431, "EUR": 0.838891}
currencies = {
'GBR': {'code': 'GBP', 'symbol': u'£'},
'CHN': {'code': 'CNY', 'symbol': u'¥'},
'CAN': {'code': 'CAD', 'symbol': '$'}
}
eurozone_countries = ['FRA']
get_data_return_value = [rates, currencies, eurozone_countries]
expected = {
"GBR": {"code": "GBP", "symbol": u"£", "rate": 0.766609},
"CAN": {"code": "CAD", "symbol": "$", "rate": 1.222252},
"CHN": {"code": "CNY", "symbol": u"¥", "rate": 6.514431},
"FRA": {"code": "EUR", "symbol": "€", "rate": 0.838891}
}
with mock.patch.object(CurrencyView, 'get_data', return_value=get_data_return_value) as mock_get_rates:
response = self.client.get(self.list_path)
self.assertDictEqual(response.data, expected)
self.assertEqual(mock_get_rates.call_count, 1)
# next request hits the cache
response = self.client.get(self.list_path)
self.assertEqual(mock_get_rates.call_count, 1)
# clearing the cache calls means the function gets called again
cache.clear()
response = self.client.get(self.list_path)
self.assertEqual(mock_get_rates.call_count, 2)
def test_no_api_key(self):
response = self.client.get(self.list_path)
self.assertEqual(response.json(), {})
@override_settings(OPENEXCHANGERATES_API_KEY='test')
def test_get_rates(self):
def mocked_requests_get(*args, **kwargs): # pylint: disable=unused-argument
class MockResponse:
def __init__(self, json_data, status_code, text):
self.json_data = json_data
self.status_code = status_code
self.text = text
def json(self):
return self.json_data
return MockResponse({"bad": "data"}, 500, "baddata")
with mock.patch('course_discovery.apps.api.v1.views.currency.requests.get', side_effect=mocked_requests_get):
response = self.client.get(self.list_path)
response_json = response.json()
self.assertEqual(response_json, {})
......@@ -7,6 +7,7 @@ from course_discovery.apps.api.v1.views.affiliates import AffiliateWindowViewSet
from course_discovery.apps.api.v1.views.catalogs import CatalogViewSet
from course_discovery.apps.api.v1.views.course_runs import CourseRunViewSet
from course_discovery.apps.api.v1.views.courses import CourseViewSet
from course_discovery.apps.api.v1.views.currency import CurrencyView
from course_discovery.apps.api.v1.views.organizations import OrganizationViewSet
from course_discovery.apps.api.v1.views.people import PersonViewSet
from course_discovery.apps.api.v1.views.program_types import ProgramTypeViewSet
......@@ -18,7 +19,8 @@ partners_router.register(r'affiliate_window/catalogs', AffiliateWindowViewSet, b
urlpatterns = [
url(r'^partners/', include(partners_router.urls, namespace='partners')),
url(r'search/typeahead', search_views.TypeaheadSearchView.as_view(), name='search-typeahead')
url(r'search/typeahead', search_views.TypeaheadSearchView.as_view(), name='search-typeahead'),
url(r'currency', CurrencyView.as_view(), name='currency')
]
router = routers.SimpleRouter()
......
import logging
import requests
from django.conf import settings
from rest_framework import views
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework_extensions.cache.decorators import cache_response
class CurrencyView(views.APIView):
permission_classes = (IsAuthenticated,)
def get_rates(self):
try:
app_id = settings.OPENEXCHANGERATES_API_KEY
if app_id:
url = 'https://openexchangerates.org/api/latest.json'
response = requests.get(url, params={'app_id': app_id}, timeout=2)
response_json = response.json()
result = response_json['rates']
return result
else:
logging.warning('No app id available for openexchangerates')
return {}
except Exception as e: # pylint: disable=broad-except
response_text = '' if not isinstance(response, object) else response.text
message = 'Exception Type {}. Message {}. Response {}.'.format(
type(e).__name__, e, response_text
)
logging.error('Could not retrieve rates from openexchangerates. ' + message)
return {}
def get_data(self):
rates = self.get_rates()
# ISO 3166-1 alpha-3 codes
currencies = {
'IND': {'code': 'INR', 'symbol': u'₹'},
'BRA': {'code': 'BRL', 'symbol': 'R$'},
'MEX': {'code': 'MXN', 'symbol': '$'},
'GBR': {'code': 'GBP', 'symbol': u'£'},
'AUS': {'code': 'AUD', 'symbol': '$'},
'CHN': {'code': 'CNY', 'symbol': u'¥'},
'COL': {'code': 'COP', 'symbol': '$'},
'PER': {'code': 'PEN', 'symbol': 'S/.'},
'CAN': {'code': 'CAD', 'symbol': '$'}
}
eurozone_countries = [
'AUT', 'BEL', 'CYP', 'EST', 'FIN', 'FRA', 'DEU', 'GRC', 'IRL',
'ITA', 'LVA', 'LTU', 'LUX', 'MLT', 'NLD', 'PRT', 'SVK', 'SVN', 'ESP'
]
return [rates, currencies, eurozone_countries]
# Cache exchange rates for 1 day
@cache_response(60 * 60 * 24)
def get(self, request, *args, **kwargs):
rates, currencies, eurozone_countries = self.get_data()
if not rates:
return Response({})
for country, currency in currencies.items():
currency_name = currency['code']
currencies[country]['rate'] = rates.get(currency_name)
eurozone_data = {'code': 'EUR', 'symbol': '€', 'rate': rates.get('EUR')}
for country in eurozone_countries:
currencies[country] = eurozone_data
return Response(currencies)
......@@ -13,6 +13,8 @@ path.append(root('apps'))
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('COURSE_DISCOVERY_SECRET_KEY', 'insecure-secret-key')
OPENEXCHANGERATES_API_KEY = None
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = 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