Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

LEARNER-9767 Create mobile skus for courses #4088

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"""
This command calls the edx-platform API to create mobile skus for given course keys
"""
import json
import logging

Check warning on line 5 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L4-L5

Added lines #L4 - L5 were not covered by tests

import requests
from django.core.management import BaseCommand, CommandError

Check warning on line 8 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L7-L8

Added lines #L7 - L8 were not covered by tests

logger = logging.getLogger(__name__)

Check warning on line 10 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L10

Added line #L10 was not covered by tests

LOCAL_LMS_BASE_URL = 'http://edx.devstack.lms:18000'
LOCAL_ECOMMERCE_BASE_URL = 'http://edx.devstack.ecommerce:18130'
STAGE_LMS_BASE_URL = 'https://courses.stage.edx.org'
STAGE_ECOMMERCE_BASE_URL = 'https://ecommerce.stage.edx.org'
PROD_LMS_BASE_URL = 'https://courses.edx.org'
PROD_ECOMMERCE_BASE_URL = 'https://ecommerce.edx.org'
CREATE_MOBILE_SKUS_URL = '/api/iap/v1/create-mobile-skus/'
OAUTH_LOGIN_URL = '/oauth2/access_token/'
INPUT_FILE_NAME = 'course_keys_for_mobile_skus.txt'
OUTPUT_FILE_NAME = 'mobile_skus_response.txt'
NEW_MOBILE_SKUS_KEY = 'new_mobile_skus'
FAILED_COURSE_IDS_KEY = 'failed_course_ids'
FAILED_IOS_PRODUCTS = 'failed_ios_products'
MISSING_COURSE_RUNS_KEY = 'missing_course_runs'
LOCAL_ENVIRONMENT = 'local'
STAGE_ENVIRONMENT = 'stage'
PROD_ENVIRONMENT = 'prod'

Check warning on line 28 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L12-L28

Added lines #L12 - L28 were not covered by tests


class FailedAuthentication(Exception):
pass

Check warning on line 32 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L31-L32

Added lines #L31 - L32 were not covered by tests


class Command(BaseCommand):

Check warning on line 35 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L35

Added line #L35 was not covered by tests
"""
Call edx-platform API /api/iap/v1/create-mobile-skus/ to create mobile skus for given course keys.
Format of response of the API /api/iap/v1/create-mobile-skus/ is as follows:
{
"new_mobile_skus": {
"course-v1:course-key": [
"mobile.android.sku",
"mobile.ios.sku"
]
},
"failed_course_ids": [],
"missing_course_runs": []
}
"""

help = 'Call edx-platform API to create mobile skus for given course keys.'

Check warning on line 51 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L51

Added line #L51 was not covered by tests

def add_arguments(self, parser):
parser.add_argument(

Check warning on line 54 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L53-L54

Added lines #L53 - L54 were not covered by tests
'--environment',
type=str,
default=1000,
help='Environment i.e. local/stage/prod')
parser.add_argument(

Check warning on line 59 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L59

Added line #L59 was not covered by tests
'--admin_username',
type=str,
default=1000,
help='Username of admin user')
parser.add_argument(

Check warning on line 64 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L64

Added line #L64 was not covered by tests
'--admin_password',
type=str,
default=1000,
help='Password of admin user')

def handle(self, *args, **options):
environment = options['environment']
admin_username = options['admin_username']
admin_password = options['admin_password']
base_lms_url, base_ecommerce_url = self._get_base_urls(environment)
create_skus_url = base_ecommerce_url + CREATE_MOBILE_SKUS_URL
login_url = base_lms_url + OAUTH_LOGIN_URL

Check warning on line 76 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L70-L76

Added lines #L70 - L76 were not covered by tests

headers = self._login_user_and_get_headers(admin_username, admin_password, login_url)
complete_response = {

Check warning on line 79 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L78-L79

Added lines #L78 - L79 were not covered by tests
NEW_MOBILE_SKUS_KEY: {},
FAILED_COURSE_IDS_KEY: [],
MISSING_COURSE_RUNS_KEY: [],
FAILED_IOS_PRODUCTS: [],
"error_messages": []
}
try:
with open(INPUT_FILE_NAME, 'r+', encoding="utf-8") as input_file:
course_keys = input_file.readlines()
for course_key in course_keys:
course_key = course_key.strip()
payload = json.dumps({"courses": [course_key]})
response = requests.post(create_skus_url, data=payload, headers=headers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we double the default timeout of requests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are processing a single course in one request, are you sure we should double the request timeout?

if response.status_code == 401:
headers = self._login_user_and_get_headers(admin_username, admin_password, login_url)

Check warning on line 94 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L86-L94

Added lines #L86 - L94 were not covered by tests
# Retry request with fresh auth token
response = requests.post(create_skus_url, data=payload, headers=headers)

Check warning on line 96 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L96

Added line #L96 was not covered by tests

if response.status_code != 200:
error_message = 'Failed for course key {}. {}'.format(course_key, response.status_code)
complete_response['error_messages'].append(error_message)
continue

Check warning on line 101 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L98-L101

Added lines #L98 - L101 were not covered by tests

self._update_response_dictionary(response.json(), complete_response)
logger.info(response)
except FileNotFoundError as error:
raise CommandError('Input file with name {} does not exists.'.format(INPUT_FILE_NAME)) from error

Check warning on line 106 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L103-L106

Added lines #L103 - L106 were not covered by tests

self._write_response_to_file(complete_response)

Check warning on line 108 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L108

Added line #L108 was not covered by tests

def _login_user(self, username, password, login_url):

Check warning on line 110 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L110

Added line #L110 was not covered by tests
"""
Login the user given the username and password.
Return the JWT token if login successful.
Raise exception if login failed.
"""
payload = {

Check warning on line 116 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L116

Added line #L116 was not covered by tests
'client_id': 'login-service-client-id',
'grant_type': 'password',
'username': username,
'password': password,
'token_type': 'jwt'
}
response = requests.post(login_url, payload)
if not response.status_code == 200:
raise FailedAuthentication

Check warning on line 125 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L123-L125

Added lines #L123 - L125 were not covered by tests

jwt_token = response.json().get('access_token')
return jwt_token

Check warning on line 128 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L127-L128

Added lines #L127 - L128 were not covered by tests

def _login_user_and_get_headers(self, admin_username, admin_password, login_url):

Check warning on line 130 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L130

Added line #L130 was not covered by tests
""" Login user and return auth headers """
try:
jwt_token = self._login_user(admin_username, admin_password, login_url)
except FailedAuthentication as error:
raise CommandError('Login failed') from error
headers = self._get_headers(jwt_token)
return headers

Check warning on line 137 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L132-L137

Added lines #L132 - L137 were not covered by tests

def _get_headers(self, jwt_token):

Check warning on line 139 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L139

Added line #L139 was not covered by tests
""" Return related headers for requests """
return {

Check warning on line 141 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L141

Added line #L141 was not covered by tests
'Authorization': 'JWT ' + jwt_token,
'Content-Type': 'application/json',
}

def _get_base_urls(self, environment):

Check warning on line 146 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L146

Added line #L146 was not covered by tests
""" Get base url for LMS and Ecommerce based on environment selected """
base_lms_url = LOCAL_LMS_BASE_URL
base_ecommerce_url = LOCAL_ECOMMERCE_BASE_URL
if environment == STAGE_ENVIRONMENT:
base_lms_url = STAGE_LMS_BASE_URL
base_ecommerce_url = STAGE_ECOMMERCE_BASE_URL
elif environment == PROD_ENVIRONMENT:
base_lms_url = PROD_LMS_BASE_URL
base_ecommerce_url = PROD_ECOMMERCE_BASE_URL

Check warning on line 155 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L148-L155

Added lines #L148 - L155 were not covered by tests

return base_lms_url, base_ecommerce_url

Check warning on line 157 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L157

Added line #L157 was not covered by tests

def _update_response_dictionary(self, new_response, complete_response):

Check warning on line 159 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L159

Added line #L159 was not covered by tests
""" Update the complete_response dict with the contents of the new response """
if new_response.get(NEW_MOBILE_SKUS_KEY):
new_mobile_skus = new_response.get(NEW_MOBILE_SKUS_KEY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add if here we can use these lines:
complete_response.update(new_response[NEW_MOBILE_SKUS_KEY])
complete_response[FAILED_COURSE_IDS_KEY].extend(new_response[FAILED_COURSE_IDS_KEY])
complete_response[MISSING_COURSE_RUNS_KEY].extend(new_response[MISSING_COURSE_RUNS_KEY])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dict.update does not append, but replaces in the second level of the dict. hence not using it.

for key, value in new_mobile_skus.items():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we adjust this script to include another array for ios errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done.

complete_response[NEW_MOBILE_SKUS_KEY][key] = value
if new_response.get(FAILED_COURSE_IDS_KEY):
complete_response[FAILED_COURSE_IDS_KEY] += new_response.get(FAILED_COURSE_IDS_KEY)
if new_response.get(MISSING_COURSE_RUNS_KEY):
complete_response[MISSING_COURSE_RUNS_KEY] += new_response.get(MISSING_COURSE_RUNS_KEY)
if new_response.get(FAILED_IOS_PRODUCTS):
complete_response[FAILED_IOS_PRODUCTS] += new_response.get(FAILED_IOS_PRODUCTS)

Check warning on line 170 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L161-L170

Added lines #L161 - L170 were not covered by tests

def _write_response_to_file(self, response):
with open(OUTPUT_FILE_NAME, 'w+', encoding="utf-8") as output_file:
output_file.write(json.dumps(response))

Check warning on line 174 in ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py

View check run for this annotation

Codecov / codecov/patch

ecommerce/extensions/iap/management/commands/create_mobile_skus_for_courses.py#L172-L174

Added lines #L172 - L174 were not covered by tests
Loading