-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add management command for course prompts
- Loading branch information
Showing
16 changed files
with
287 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
104 changes: 104 additions & 0 deletions
104
learning_assistant/management/commands/set_course_prompts.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
""" | ||
Django management command to generate course prompts. | ||
""" | ||
import json | ||
import logging | ||
from posixpath import join as urljoin | ||
|
||
from django.conf import settings | ||
from django.core.management.base import BaseCommand | ||
from edx_rest_api_client.client import OAuthAPIClient | ||
from opaque_keys.edx.keys import CourseKey | ||
|
||
from learning_assistant.models import CoursePrompt | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Django Management command to create a set of course prompts | ||
""" | ||
|
||
def add_arguments(self, parser): | ||
|
||
# list of course ids | ||
parser.add_argument( | ||
'--course_ids', | ||
dest='course_ids', | ||
help='Comma separated list of course_ids to generate. Only newer style course ids can be supplied.', | ||
) | ||
|
||
# pre-message | ||
parser.add_argument( | ||
'--pre_message', | ||
dest='pre_message', | ||
help='Message to prepend to course topics', | ||
) | ||
|
||
parser.add_argument( | ||
'--skills_descriptor', | ||
dest='skills_descriptor', | ||
help='Message that describes skill structure' | ||
) | ||
|
||
# post-message | ||
parser.add_argument( | ||
'--post_message', | ||
dest='post_message', | ||
help='Message to append to course topics', | ||
) | ||
|
||
@staticmethod | ||
def _get_discovery_api_client(): | ||
""" | ||
Returns an API client which can be used to make Catalog API requests. | ||
""" | ||
return OAuthAPIClient( | ||
base_url=settings.DISCOVERY_BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL, | ||
client_id=settings.DISCOVERY_BACKEND_SERVICE_EDX_OAUTH2_KEY, | ||
client_secret=settings.DISCOVERY_BACKEND_SERVICE_EDX_OAUTH2_SECRET, | ||
) | ||
|
||
def handle(self, *args, **options): | ||
""" | ||
Management command entry point. | ||
This command is meant to generate a small (<500) set of course prompts. If a larger number of prompts | ||
should be created, consider adding batching to this command. | ||
""" | ||
course_ids = options['course_ids'] | ||
pre_message = options['pre_message'] | ||
skills_descriptor = options['skills_descriptor'] | ||
post_message = options['post_message'] | ||
|
||
client = self._get_discovery_api_client() | ||
|
||
course_ids_list = course_ids.split(',') | ||
for course_run_id in course_ids_list: | ||
course_key = CourseKey.from_string(course_run_id) | ||
|
||
# discovery API requires course keys, not course run keys | ||
course_id = f'{course_key.org}+{course_key.course}' | ||
|
||
url = urljoin( | ||
settings.DISCOVERY_BASE_URL, | ||
'api/v1/courses/{course_id}'.format(course_id=course_id) | ||
) | ||
response_data = client.get(url).json() | ||
title = response_data['title'] | ||
skill_names = response_data['skill_names'] | ||
|
||
# create restructured dictionary with data | ||
course_dict = {'title': title, 'topics': skill_names} | ||
|
||
# append descriptor message and decode json dict into a string | ||
skills_message = skills_descriptor + json.dumps(course_dict) | ||
|
||
# finally, create list of prompt messages and save | ||
prompt_messages = [pre_message, skills_message, post_message] | ||
CoursePrompt.objects.update_or_create( | ||
course_id=course_run_id, json_prompt_content=prompt_messages | ||
) | ||
|
||
logger.info('Updated course prompt for course_run_id=%s', course_run_id) |
Empty file.
75 changes: 75 additions & 0 deletions
75
learning_assistant/management/commands/tests/test_set_course_prompts.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
""" | ||
Tests for the set_course_prompts management command. | ||
""" | ||
import json | ||
from posixpath import join as urljoin | ||
from unittest.mock import MagicMock, patch | ||
|
||
from django.conf import settings | ||
from django.core.management import call_command | ||
from django.test import TestCase | ||
|
||
from learning_assistant.models import CoursePrompt | ||
|
||
|
||
class SetCoursePromptsTests(TestCase): | ||
"""Test set_course_prompts command""" | ||
command = 'set_course_prompts' | ||
|
||
def setUp(self): | ||
self.pre_message = 'This is the first message' | ||
self.skills_descriptor = 'These are the skills: ' | ||
self.post_message = 'This message comes after' | ||
self.course_ids = 'course-v1:edx+test+23,course-v1:edx+test+24' | ||
self.course_title = 'Intro to Testing' | ||
self.skill_names = ['Testing', 'Computers', 'Coding'] | ||
|
||
def get_mock_discovery_response(self): | ||
""" | ||
Create scaled down mock of discovery response | ||
""" | ||
response_data = { | ||
'title': self.course_title, | ||
'skill_names': self.skill_names | ||
} | ||
return response_data | ||
|
||
@patch('learning_assistant.management.commands.set_course_prompts.Command._get_discovery_api_client') | ||
def test_course_prompts_created(self, mock_get_discovery_client): | ||
""" | ||
Assert that course prompts are created by calling management command. | ||
""" | ||
mock_client = MagicMock() | ||
mock_get_discovery_client.return_value = mock_client | ||
mock_client.get.return_value = MagicMock( | ||
status_code=200, | ||
json=lambda: self.get_mock_discovery_response() # pylint: disable=unnecessary-lambda | ||
) | ||
|
||
call_command( | ||
self.command, | ||
course_ids=self.course_ids, | ||
pre_message=self.pre_message, | ||
skills_descriptor=self.skills_descriptor, | ||
post_message=self.post_message, | ||
) | ||
|
||
# assert that discovery api was called with course id, not course run id | ||
expected_url = urljoin( | ||
settings.DISCOVERY_BASE_URL, | ||
'api/v1/courses/{course_id}'.format(course_id='edx+test') | ||
) | ||
mock_client.get.assert_any_call(expected_url) | ||
mock_client.get.assert_called() | ||
|
||
# assert that number of prompts created is equivalent to number of courses passed in to command | ||
prompts = CoursePrompt.objects.filter() | ||
self.assertEqual(len(prompts), len(self.course_ids.split(','))) | ||
|
||
# assert structure of prompt | ||
course_prompt = prompts[0].json_prompt_content | ||
self.assertEqual(len(course_prompt), 3) | ||
|
||
skills_message = self.skills_descriptor + json.dumps({'title': self.course_title, 'topics': self.skill_names}) | ||
expected_response = [self.pre_message, skills_message, self.post_message] | ||
self.assertEqual(course_prompt, expected_response) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.