Skip to content

Commit

Permalink
feat: add translation_languages to CourseRun (#4417)
Browse files Browse the repository at this point in the history
  • Loading branch information
zawan-ila authored Aug 28, 2024
1 parent 6aa9957 commit 5c8ceb1
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 13 deletions.
4 changes: 2 additions & 2 deletions course_discovery/apps/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1099,10 +1099,10 @@ class Meta(MinimalCourseRunSerializer.Meta):
'first_enrollable_paid_seat_price', 'has_ofac_restrictions', 'ofac_comment',
'enrollment_count', 'recent_enrollment_count', 'expected_program_type', 'expected_program_name',
'course_uuid', 'estimated_hours', 'content_language_search_facet_name', 'enterprise_subscription_inclusion',
'transcript_languages_search_facet_names'
'transcript_languages_search_facet_names', 'translation_languages'
)
read_only_fields = ('enrollment_count', 'recent_enrollment_count', 'content_language_search_facet_name',
'enterprise_subscription_inclusion')
'enterprise_subscription_inclusion', 'translation_languages')

def get_instructors(self, obj): # pylint: disable=unused-argument
# This field is deprecated. Use the staff field.
Expand Down
3 changes: 2 additions & 1 deletion course_discovery/apps/api/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,8 @@ def get_expected_data(cls, course_run, request):
'enterprise_subscription_inclusion': course_run.enterprise_subscription_inclusion,
'transcript_languages_search_facet_names': [
lang.get_search_facet_display() for lang in course_run.transcript_languages.all()
]
],
'translation_languages': course_run.translation_languages,
})
return expected

Expand Down
2 changes: 1 addition & 1 deletion course_discovery/apps/course_metadata/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class CourseRunAdmin(SimpleHistoryAdmin):
raw_id_fields = ('course', 'draft_version',)
readonly_fields = [
'enrollment_count', 'recent_enrollment_count', 'hidden', 'key', 'enterprise_subscription_inclusion',
'variant_id', 'fixed_price_usd'
'variant_id', 'fixed_price_usd', 'translation_languages'
]
search_fields = ('uuid', 'key', 'title_override', 'course__title', 'slug', 'external_key', 'variant_id')
save_error = False
Expand Down
13 changes: 12 additions & 1 deletion course_discovery/apps/course_metadata/algolia_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ def delegate_attributes(cls):
search_fields = ['partner_names', 'partner_keys', 'product_title', 'product_source', 'primary_description',
'secondary_description', 'tertiary_description']
facet_fields = ['availability_level', 'subject_names', 'levels', 'active_languages', 'staff_slugs',
'product_allowed_in', 'product_blocked_in', 'learning_type', 'learning_type_exp']
'product_allowed_in', 'product_blocked_in', 'learning_type', 'learning_type_exp',
'product_translation_languages']
ranking_fields = ['availability_rank', 'product_recent_enrollment_count', 'promoted_in_spanish_index',
'product_value_per_click_usa', 'product_value_per_click_international',
'product_value_per_lead_usa', 'product_value_per_lead_international']
Expand Down Expand Up @@ -353,6 +354,12 @@ def product_min_effort(self):
def product_max_effort(self):
return getattr(self.advertised_course_run, 'max_effort', None)

@property
def product_translation_languages(self):
if self.advertised_course_run and self.advertised_course_run.translation_languages:
return self.advertised_course_run.translation_languages
return []

@property
def owners(self):
return get_owners(self)
Expand Down Expand Up @@ -533,6 +540,10 @@ def product_min_effort(self):
def product_max_effort(self):
return self.max_hours_effort_per_week

@property
def product_translation_languages(self):
return []

@property
def subject_names(self):
if self.primary_subject_override:
Expand Down
11 changes: 7 additions & 4 deletions course_discovery/apps/course_metadata/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class EnglishProductIndex(BaseProductIndex):
('active_languages', 'language'), ('product_type', 'product'), ('program_types', 'program_type'),
('staff_slugs', 'staff'), ('product_allowed_in', 'allowed_in'),
('product_blocked_in', 'blocked_in'), 'subscription_eligible',
'subscription_prices', 'learning_type', 'learning_type_exp',)
'subscription_prices', 'learning_type', 'learning_type_exp',
('product_translation_languages', 'translation_languages'))
ranking_fields = ('availability_rank', ('product_recent_enrollment_count', 'recent_enrollment_count'),
('product_value_per_click_usa', 'value_per_click_usa'),
('product_value_per_click_international', 'value_per_click_international'),
Expand Down Expand Up @@ -116,7 +117,7 @@ class EnglishProductIndex(BaseProductIndex):
'partner', 'availability', 'subject', 'level', 'language', 'product', 'program_type',
'filterOnly(staff)', 'filterOnly(allowed_in)', 'filterOnly(blocked_in)', 'skills.skill',
'skills.category', 'skills.subcategory', 'tags', 'subscription_eligible', 'subscription_prices',
'learning_type', 'learning_type_exp',
'learning_type', 'learning_type_exp', 'translation_languages.code', 'translation_languages.label',
],
'customRanking': ['asc(availability_rank)', 'desc(recent_enrollment_count)']
}
Expand All @@ -133,7 +134,8 @@ class SpanishProductIndex(BaseProductIndex):
('active_languages', 'language'), ('product_type', 'product'), ('program_types', 'program_type'),
('staff_slugs', 'staff'), ('product_allowed_in', 'allowed_in'),
('product_blocked_in', 'blocked_in'), 'subscription_eligible',
'subscription_prices', 'learning_type', 'learning_type_exp',)
'subscription_prices', 'learning_type', 'learning_type_exp',
('product_translation_languages', 'translation_languages'))
ranking_fields = ('availability_rank', ('product_recent_enrollment_count', 'recent_enrollment_count'),
('product_value_per_click_usa', 'value_per_click_usa'),
('product_value_per_click_international', 'value_per_click_international'),
Expand Down Expand Up @@ -171,7 +173,8 @@ class SpanishProductIndex(BaseProductIndex):
'partner', 'availability', 'subject', 'level', 'language', 'product', 'program_type',
'filterOnly(staff)', 'filterOnly(allowed_in)', 'filterOnly(blocked_in)',
'skills.skill', 'skills.category', 'skills.subcategory', 'tags', 'subscription_eligible',
'subscription_prices', 'learning_type', 'learning_type_exp',
'subscription_prices', 'learning_type', 'learning_type_exp', 'translation_languages.code',
'translation_languages.label',
],
'customRanking': ['desc(promoted_in_spanish_index)', 'asc(availability_rank)', 'desc(recent_enrollment_count)']
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.13 on 2024-08-28 04:13

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('course_metadata', '0344_courserun_fixed_price_usd_and_more'),
]

operations = [
migrations.AddField(
model_name='courserun',
name='translation_languages',
field=models.JSONField(blank=True, help_text='A JSON list detailing the available translations for this run. Each element in the list is a dictionary containing two keys: the language code and the language label. These entries represent the languages into which the run content can be translated.', null=True),
),
migrations.AddField(
model_name='historicalcourserun',
name='translation_languages',
field=models.JSONField(blank=True, help_text='A JSON list detailing the available translations for this run. Each element in the list is a dictionary containing two keys: the language code and the language label. These entries represent the languages into which the run content can be translated.', null=True),
),
]
8 changes: 8 additions & 0 deletions course_discovery/apps/course_metadata/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2157,6 +2157,14 @@ class CourseRun(ManageHistoryMixin, DraftModelMixin, CachedMixin, TimeStampedMod
help_text=_('Estimated number of weeks needed to complete this course run.'))
language = models.ForeignKey(LanguageTag, models.CASCADE, null=True, blank=True)
transcript_languages = models.ManyToManyField(LanguageTag, blank=True, related_name='transcript_courses')
translation_languages = models.JSONField(
null=True,
blank=True,
help_text=_('A JSON list detailing the available translations for this run. '
'Each element in the list is a dictionary containing two keys: the language code '
'and the language label. These entries represent the languages into which the run '
'content can be translated.')
)
pacing_type = models.CharField(max_length=255, db_index=True, null=True, blank=True,
choices=CourseRunPacing.choices)
syllabus = models.ForeignKey(SyllabusItem, models.CASCADE, default=None, null=True, blank=True)
Expand Down
1 change: 1 addition & 0 deletions course_discovery/apps/course_metadata/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ class CourseRunFactory(SalesforceRecordFactory):
type = factory.SubFactory(CourseRunTypeFactory)
variant_id = factory.LazyFunction(uuid4)
fixed_price_usd = FuzzyDecimal(0.0, 650.0)
translation_languages = [{'code': 'fr', 'label': 'French'}]

@factory.post_generation
def staff(self, create, extracted, **kwargs):
Expand Down
20 changes: 16 additions & 4 deletions course_discovery/apps/course_metadata/tests/test_algolia_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ def create_course_with_basic_active_course_run(self, **kwargs):
)
return course

def create_blocked_course_run(self, **kwargs):
def create_blocked_course(self, status=CourseRunStatus.Published, **kwargs):
course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner,
product_source=SourceFactory(slug='blocked'))

course_run = CourseRunFactory(
course=course,
start=self.YESTERDAY,
end=self.YESTERDAY,
status=CourseRunStatus.Published,
status=status,
**kwargs
)
SeatFactory(
Expand Down Expand Up @@ -442,13 +442,13 @@ def test_product_source_with_empty_source(self, model_factory, product_source):

@override_settings(ALGOLIA_INDEX_EXCLUDED_SOURCES=[])
def test_product_source_excluded(self):
course = self.create_blocked_course_run()
course = self.create_blocked_course()
course.authoring_organizations.add(OrganizationFactory())
assert course.should_index

@override_settings(ALGOLIA_INDEX_EXCLUDED_SOURCES=['blocked'])
def test_product_source_should_excluded(self):
course = self.create_blocked_course_run()
course = self.create_blocked_course()
course.authoring_organizations.add(OrganizationFactory())
assert not course.should_index

Expand Down Expand Up @@ -569,6 +569,14 @@ def test_learning_type_exp_non_open_course(self, course_type_slug, expected_resu
course.type = CourseTypeFactory(slug=course_type_slug)
assert course.learning_type_exp == [expected_result]

def test_course_translation_languages(self):
course = self.create_current_upgradeable_course()
assert course.product_translation_languages == [{'code': 'fr', 'label': 'French'}]

def test_course_translation_languages__no_advertised_run(self):
course = self.create_blocked_course(status=CourseRunStatus.Unpublished)
assert course.product_translation_languages == []


@ddt.ddt
@pytest.mark.django_db
Expand Down Expand Up @@ -908,3 +916,7 @@ def test_learning_type_exp(self, program_type_slug, learning_type):
program_type = ProgramType.objects.get(slug=program_type_slug)
program = AlgoliaProxyProgramFactory(partner=self.__class__.edxPartner, type=program_type)
assert program.learning_type_exp == [learning_type]

def test_program_translation_languages(self):
program = AlgoliaProxyProgramFactory(partner=self.__class__.edxPartner)
assert program.product_translation_languages == []
11 changes: 11 additions & 0 deletions course_discovery/apps/course_metadata/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,17 @@ def test_course_run_fixed_usd_price(self):
)
assert course_run.fixed_price_usd is None

def test_course_run_translation_languages(self):
"""
Sanity checks for the translation_languages field
"""
DEFAULT_TRANSLATION_LANGUAGES = [{'code': 'fr', 'label': 'French'}]
course_run = factories.CourseRunFactory()
assert course_run.translation_languages == DEFAULT_TRANSLATION_LANGUAGES

course_run = factories.CourseRunFactory(translation_languages=None)
assert course_run.translation_languages is None

@ddt.data('full_description_override', 'outcome_override', 'short_description_override')
def test_html_fields_are_validated(self, field_name):
# Happy path
Expand Down

0 comments on commit 5c8ceb1

Please sign in to comment.