Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
course-discovery
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
course-discovery
Commits
7792d7eb
Commit
7792d7eb
authored
Sep 06, 2017
by
Matthew Piatetsky
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add currency view
LEARNER-2409
parent
0498046b
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
148 additions
and
1 deletions
+148
-1
course_discovery/apps/api/v1/tests/test_views/test_currency.py
+74
-0
course_discovery/apps/api/v1/urls.py
+3
-1
course_discovery/apps/api/v1/views/currency.py
+69
-0
course_discovery/settings/base.py
+2
-0
No files found.
course_discovery/apps/api/v1/tests/test_views/test_currency.py
0 → 100644
View file @
7792d7eb
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
,
{})
course_discovery/apps/api/v1/urls.py
View file @
7792d7eb
...
...
@@ -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
()
...
...
course_discovery/apps/api/v1/views/currency.py
0 → 100644
View file @
7792d7eb
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
)
course_discovery/settings/base.py
View file @
7792d7eb
...
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment