Skip to content

Commit

Permalink
chore: mgmt command changes
Browse files Browse the repository at this point in the history
  • Loading branch information
zawan-ila committed Feb 25, 2025
1 parent 3dc1932 commit b191d42
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 200 deletions.
12 changes: 6 additions & 6 deletions course_discovery/apps/core/api_client/lms.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,23 +210,23 @@ def get_blocks_metadata(self, block_id: str, **kwargs):
cache_key = get_cache_key(block_id=block_id, resource=resource)
return self._get_blocks_data(block_id, cache_key, query_parameters, resource)

def get_course_run_translations(self, course_run_id: str):
def get_course_run_translations_and_transcriptions(self, course_run_id: str):
"""
Get translation information for a given course run.
Get translation and transcription information for a given course run.
Args:
course_run_id (str): The course run ID to fetch translation information for.
course_run_id (str): The course run ID to fetch information for.
Returns:
dict: A dictionary containing the translation information or an empty dict on error.
dict: A dictionary containing the relevant information or an empty dict on error.
"""
resource = settings.LMS_API_URLS['translations']
resource = settings.LMS_API_URLS['translations_and_transcriptions']
resource_url = urljoin(self.lms_url, resource)

try:
response = self.client.get(resource_url, params={'course_id': course_run_id})
response.raise_for_status()
return response.json()
except RequestException as e:
logger.exception(f'Failed to fetch translation data for course run [{course_run_id}]: {e}')
logger.exception(f'Failed to fetch data for course run [{course_run_id}]: {e}')
return {}
34 changes: 21 additions & 13 deletions course_discovery/apps/core/tests/test_api_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,35 +237,43 @@ def test_get_blocks_data_cache_hit(self):
assert len(responses.calls) == 1

@responses.activate
def test_get_course_run_translations(self):
def test_get_course_run_translations_and_transcriptions(self):
"""
Verify that `get_course_run_translations` returns correct translation data.
Verify that `get_course_run_translations_and_transcriptions` returns correct data.
"""
course_run_id = 'course-v1:edX+DemoX+Demo_Course'
translation_data = {
"en": {"title": "Course Title", "language": "English"},
"fr": {"title": "Titre du cours", "language": "French"}
response_data = {
"available_translation_languages": [
{
"code": "ar",
"enabled": True,
"label": "Arabic"
}
],
"feature_enabled": True,
"feature_available": False,
"transcription_languages": ["en", "fr"]
}
resource = settings.LMS_API_URLS['translations']
resource = settings.LMS_API_URLS['translations_and_transcriptions']
resource_url = urljoin(self.partner.lms_url, resource)

responses.add(
responses.GET,
resource_url,
json=translation_data,
json=response_data,
status=200
)

result = self.lms.get_course_run_translations(course_run_id)
result = self.lms.get_course_run_translations_and_transcriptions(course_run_id)
assert result == translation_data

@responses.activate
def test_get_course_run_translations_with_error(self):
def test_get_course_run_translations_and_transcriptions_with_error(self):
"""
Verify that get_course_run_translations returns an empty dictionary when there's an error.
Verify that `get_course_run_translations_and_transcriptions` returns an empty dictionary when there's an error.
"""
course_run_id = 'course-v1:edX+DemoX+Demo_Course'
resource = settings.LMS_API_URLS['translations']
resource = settings.LMS_API_URLS['translations_and_transcriptions']
resource_url = urljoin(self.partner.lms_url, resource)

responses.add(
Expand All @@ -274,6 +282,6 @@ def test_get_course_run_translations_with_error(self):
status=500
)

result = self.lms.get_course_run_translations(course_run_id)
result = self.lms.get_course_run_translations_and_transcriptions(course_run_id)
assert result == {}
assert 'Failed to fetch translation data for course run [%s]' % course_run_id in self.log_messages['error'][0]
assert 'Failed to fetch data for course run [%s]' % course_run_id in self.log_messages['error'][0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"""
Unit tests for the `update_course_ai_languages` management command.
"""
import datetime
from unittest.mock import patch

import ddt
from django.core.management import CommandError, call_command
from django.test import TestCase
from django.utils.timezone import now

from course_discovery.apps.course_metadata.models import CourseRun, CourseRunType
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, PartnerFactory, SeatFactory


@ddt.ddt
@patch('course_discovery.apps.core.api_client.lms.LMSAPIClient.get_course_run_translations_and_transcriptions')
class UpdateCourseAiLanguagesTests(TestCase):
"""Test Suite for the update_course_ai_languages management command."""

AI_LANGUAGES_DATA = {
'available_translation_languages': [
{'code': 'fr', 'label': 'French'},
{'code': 'es', 'label': 'Spanish'}
],
'feature_enabled': True,
}

AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS = {
**AI_LANGUAGES_DATA,
'transcription_languages': ['en', 'fr']
}

def setUp(self):
self.partner = PartnerFactory()
self.course_run = CourseRunFactory()

def assert_ai_langs(self, run, data):
self.assertListEqual(
run.ai_languages['translation_languages'],
data['available_translation_languages']
)
self.assertListEqual(
run.ai_languages['transcription_languages'],
data.get('transcription_languages', [])
)


@ddt.data(AI_LANGUAGES_DATA, AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS)
def test_update_ai_languages(self, mock_data, mock_get_translations_and_transcriptions):
"""Test the command with a valid course run and response data."""
mock_get_translations_and_transcriptions.return_value = mock_data

call_command('update_course_ai_languages', partner=self.partner.name)

course_run = CourseRun.objects.get(id=self.course_run.id)
self.assert_ai_langs(course_run, mock_data)

@ddt.data(AI_LANGUAGES_DATA, AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS)
def test_update_ai_languages_draft(self, mock_data, mock_get_translations_and_transcriptions):
"""
Test the command with both draft and non-draft course runs, ensuring that the both draft and non-draft
course runs are updated appropriately.
"""
mock_get_translations_and_transcriptions.return_value = mock_data
draft_course_run = CourseRunFactory(
draft=True, end=now() + datetime.timedelta(days=10)
)
course_run = CourseRunFactory(draft=False, draft_version_id=draft_course_run.id)

call_command('update_course_ai_languages', partner=self.partner.name)

course_run.refresh_from_db()
self.assert_ai_langs(course_run, mock_data)

draft_course_run.refresh_from_db()
self.assert_ai_langs(draft_course_run, mock_data)


@ddt.data(AI_LANGUAGES_DATA, AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS)
def test_update_ai_languages_no_translations(self, mock_data, mock_get_translations_and_transcriptions):
"""Test the command when no translations are returned for a course run."""
mock_get_translations_and_transcriptions.return_value = {
**mock_data,
'available_translation_languages': [],
}

call_command('update_course_ai_languages', partner=self.partner.name)

course_run = CourseRun.objects.get(id=self.course_run.id)
self.assertListEqual(course_run.ai_languages['translation_languages'], [])

@ddt.data(AI_LANGUAGES_DATA, AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS)
def test_command_with_active_flag(self, mock_data, mock_get_translations_and_transcriptions):
"""Test the command with the active flag filtering active course runs."""
mock_get_translations_and_transcriptions.return_value = mock_data

active_course_run = CourseRunFactory(end=now() + datetime.timedelta(days=10), ai_languages=None)
non_active_course_run = CourseRunFactory(end=now() - datetime.timedelta(days=10), ai_languages=None)

call_command('update_course_ai_languages', partner=self.partner.name, active=True)

active_course_run.refresh_from_db()
non_active_course_run.refresh_from_db()

self.assert_ai_langs(active_course_run, mock_data)
assert non_active_course_run.ai_languages is None

@ddt.data(AI_LANGUAGES_DATA, AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS)
def test_command_with_marketable_flag(self, mock_data, mock_get_translations_and_transcriptions):
"""Test the command with the marketable flag filtering marketable course runs."""
mock_get_translations_and_transcriptions.return_value = mock_data

verified_and_audit_type = CourseRunType.objects.get(slug='verified-audit')
verified_and_audit_type.is_marketable = True
verified_and_audit_type.save()

marketable_course_run = CourseRunFactory(
status='published',
slug='test-course-run',
type=verified_and_audit_type,
ai_languages=None
)
seat = SeatFactory(course_run=marketable_course_run)
marketable_course_run.seats.add(seat)

call_command('update_course_ai_languages', partner=self.partner.name, marketable=True)

marketable_course_run.refresh_from_db()
self.assert_ai_langs(marketable_course_run, mock_data)

@ddt.data(AI_LANGUAGES_DATA, AI_LANGUAGES_DATA_WITH_TRANSCRIPTIONS)
def test_command_with_marketable_and_active_flag(self, mock_data, mock_get_translations_and_transcriptions):
"""Test the command with the marketable and active flag filtering both marketable and active course runs."""
mock_get_translations_and_transcriptions.return_value = mock_data

non_active_non_marketable_course_run = CourseRunFactory(
end=now() - datetime.timedelta(days=10), ai_languages=None
)
active_non_marketable_course_run = CourseRunFactory(
end=now() + datetime.timedelta(days=10), ai_languages=None
)

verified_and_audit_type = CourseRunType.objects.get(slug='verified-audit')
verified_and_audit_type.is_marketable = True
verified_and_audit_type.save()

marketable_non_active_course_run = CourseRunFactory(
status='published',
slug='test-course-run',
type=verified_and_audit_type,
end=now() - datetime.timedelta(days=10), ai_languages=None
)
seat = SeatFactory(course_run=marketable_non_active_course_run)
marketable_non_active_course_run.seats.add(seat)

call_command('update_course_ai_languages', partner=self.partner.name, marketable=True, active=True)

marketable_non_active_course_run.refresh_from_db()
self.assert_ai_langs(marketable_non_active_course_run, mock_data)
self.assert_ai_langs(active_non_marketable_course_run, mock_data)
assert non_active_non_marketable_course_run.ai_languages is None

def test_command_no_partner(self, _):
"""Test the command raises an error if no valid partner is found."""
with self.assertRaises(CommandError):
call_command('update_course_ai_languages', partner='nonexistent-partner')
Loading

0 comments on commit b191d42

Please sign in to comment.