Skip to content

Commit

Permalink
feat: Add courseware language support
Browse files Browse the repository at this point in the history
  • Loading branch information
arslanashraf7 committed Jan 8, 2025
1 parent 6482515 commit 91c897f
Show file tree
Hide file tree
Showing 17 changed files with 315 additions and 12 deletions.
6 changes: 5 additions & 1 deletion cms/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ def filter_and_sort_catalog_pages(
page.product.current_price,
page.title,
)
default_sorting_key = lambda page: (page_run_dates[page], page.title) # noqa: E731
default_sorting_key = lambda page: ( # noqa: E731
page.language.priority,
page_run_dates[page],
page.title,
)

# Best Match and Start Date sorting has same logic
sorting_key_map = defaultdict(
Expand Down
16 changes: 11 additions & 5 deletions cms/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,21 @@ def test_filter_and_sort_catalog_pages_with_default_sorting(sort_by):
non_program_run = CourseRunFactory.create(
course__no_program=True, start_date=(now + timedelta(days=1))
)
first_program_run = CourseRunFactory.create(start_date=(now + timedelta(days=2)))
second_program_run = CourseRunFactory.create(start_date=(now + timedelta(days=3)))
first_program_run = CourseRunFactory.create(
start_date=(now + timedelta(days=2)),
course__program__page__language__priority=1,
)
second_program_run = CourseRunFactory.create(
start_date=(now + timedelta(days=3)),
course__program__page__language__priority=1,
)
later_external_course_page = ExternalCoursePageFactory.create()
CourseRunFactory.create(
course=later_external_course_page.course, start_date=now + timedelta(days=4)
)
later_external_program_page = ExternalProgramPageFactory.create()
later_external_program_page = ExternalProgramPageFactory.create(
language__priority=1
)
# Create course run with past start_date and future enrollment_end, which should appear in the catalog
CourseRunFactory.create(
course__program=later_external_program_page.program,
Expand Down Expand Up @@ -85,7 +93,6 @@ def test_filter_and_sort_catalog_pages_with_default_sorting(sort_by):
initial_program_pages = [
run.course.program.page for run in [second_program_run, first_program_run]
]

all_pages, program_pages, course_pages = filter_and_sort_catalog_pages(
initial_program_pages,
initial_course_pages,
Expand Down Expand Up @@ -114,7 +121,6 @@ def test_filter_and_sort_catalog_pages_with_default_sorting(sort_by):
assert past_run.course not in (
None if page.is_external_course_page else page.course for page in course_pages
)

# Pages should be sorted by next run date
assert [page.program for page in program_pages] == [
first_program_run.course.program,
Expand Down
11 changes: 10 additions & 1 deletion cms/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@
WebinarPage,
WhoShouldEnrollPage,
)
from courses.factories import CourseFactory, PlatformFactory, ProgramFactory
from courses.factories import (
CourseFactory,
CourseLanguageFactory,
PlatformFactory,
ProgramFactory,
)

factory.Faker.add_provider(internet)

Expand All @@ -78,6 +83,7 @@ class ProgramPageFactory(wagtail_factories.PageFactory):
subhead = factory.fuzzy.FuzzyText(prefix="Subhead ")
thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory)
background_image = factory.SubFactory(wagtail_factories.ImageFactory)
language = factory.SubFactory(CourseLanguageFactory)
parent = factory.SubFactory(wagtail_factories.PageFactory)
certificate_page = factory.RelatedFactory(
"cms.factories.CertificatePageFactory", "parent"
Expand Down Expand Up @@ -108,6 +114,7 @@ class CoursePageFactory(wagtail_factories.PageFactory):
subhead = factory.fuzzy.FuzzyText(prefix="Subhead ")
thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory)
background_image = factory.SubFactory(wagtail_factories.ImageFactory)
language = factory.SubFactory(CourseLanguageFactory)
parent = factory.SubFactory(wagtail_factories.PageFactory)
certificate_page = factory.RelatedFactory(
"cms.factories.CertificatePageFactory", "parent"
Expand Down Expand Up @@ -141,6 +148,7 @@ class ExternalCoursePageFactory(wagtail_factories.PageFactory):
subhead = factory.fuzzy.FuzzyText(prefix="Subhead ")
thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory)
background_image = factory.SubFactory(wagtail_factories.ImageFactory)
language = factory.SubFactory(CourseLanguageFactory)
parent = factory.SubFactory(wagtail_factories.PageFactory)

class Meta:
Expand Down Expand Up @@ -170,6 +178,7 @@ class ExternalProgramPageFactory(wagtail_factories.PageFactory):
subhead = factory.fuzzy.FuzzyText(prefix="Subhead ")
thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory)
background_image = factory.SubFactory(wagtail_factories.ImageFactory)
language = factory.SubFactory(CourseLanguageFactory)
parent = factory.SubFactory(wagtail_factories.PageFactory)

class Meta:
Expand Down
78 changes: 78 additions & 0 deletions cms/migrations/0078_add_courseware_page_language.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Generated by Django 4.2.17 on 2025-01-03 08:36

import django.db.models.deletion
from django.db import migrations, models


def populate_course_language(apps, schema_editor):
"""Prepopulate the course language for the course pages"""

CoursePage = apps.get_model("cms.CoursePage")
ExternalCoursePage = apps.get_model("cms.ExternalCoursePage")
ProgramPage = apps.get_model("cms.ProgramPage")
ExternalProgramPage = apps.get_model("cms.ExternalProgramPage")

CourseLanguage = apps.get_model("courses.CourseLanguage")
course_language_english = CourseLanguage.objects.get(name="English")

CoursePage.objects.update(language=course_language_english)
ExternalCoursePage.objects.update(language=course_language_english)
ProgramPage.objects.update(language=course_language_english)
ExternalProgramPage.objects.update(language=course_language_english)


class Migration(migrations.Migration):
dependencies = [
("courses", "0042_add_course_language"),
("cms", "0077_alter_certificatepage_ceus"),
]

operations = [
migrations.AddField(
model_name="coursepage",
name="language",
field=models.ForeignKey(
blank=True,
help_text="The course/program language for this page",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
),
migrations.AddField(
model_name="externalcoursepage",
name="language",
field=models.ForeignKey(
blank=True,
help_text="The course/program language for this page",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
),
migrations.AddField(
model_name="externalprogrampage",
name="language",
field=models.ForeignKey(
blank=True,
help_text="The course/program language for this page",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
),
migrations.AddField(
model_name="programpage",
name="language",
field=models.ForeignKey(
blank=True,
help_text="The course/program language for this page",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
),
migrations.RunPython(
populate_course_language, reverse_code=migrations.RunPython.noop
),
]
53 changes: 53 additions & 0 deletions cms/migrations/0079_make_language_non_nullable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 4.2.17 on 2025-01-03 08:39

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("cms", "0078_add_courseware_page_language"),
]

operations = [
migrations.AlterField(
model_name="coursepage",
name="language",
field=models.ForeignKey(
help_text="The course/program language for this page",
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
),
migrations.AlterField(
model_name="externalcoursepage",
name="language",
field=models.ForeignKey(
help_text="The course/program language for this page",
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
),
migrations.AlterField(
model_name="externalprogrampage",
name="language",
field=models.ForeignKey(
default="",
help_text="The course/program language for this page",
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
preserve_default=False,
),
migrations.AlterField(
model_name="programpage",
name="language",
field=models.ForeignKey(
default="",
help_text="The course/program language for this page",
on_delete=django.db.models.deletion.PROTECT,
to="courses.courselanguage",
),
preserve_default=False,
),
]
9 changes: 9 additions & 0 deletions cms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,14 @@ class ProductPage(MetadataPageMixin, WagtailCachedPageMixin, Page):
class Meta:
abstract = True

language = models.ForeignKey(
"courses.CourseLanguage",
null=False,
blank=False,
on_delete=models.PROTECT,
help_text="The course/program language for this page",
)

description = RichTextField(
blank=True, help_text="The description shown on the product page"
)
Expand Down Expand Up @@ -1030,6 +1038,7 @@ class Meta:
use_json_field=True,
)
content_panels = Page.content_panels + [ # noqa: RUF005
FieldPanel("language"),
FieldPanel("external_marketing_url"),
FieldPanel("marketing_hubspot_form_id"),
FieldPanel("subhead"),
Expand Down
4 changes: 3 additions & 1 deletion cms/models_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
from cms.wagtail_hooks import create_product_and_versions_for_courseware_pages
from courses.factories import (
CourseFactory,
CourseLanguageFactory,
CourseRunCertificateFactory,
CourseRunFactory,
ProgramCertificateFactory,
Expand Down Expand Up @@ -2173,7 +2174,7 @@ def _create_external_course_page(superuser_client, course_id, slug):
Asserts:
Response status code is 302 (successful redirection).
"""

language = CourseLanguageFactory.create()
post_data = {
"course": course_id,
"title": "Icon Grid #6064",
Expand All @@ -2182,6 +2183,7 @@ def _create_external_course_page(superuser_client, course_id, slug):
"content-count": 0,
"slug": slug,
"action-publish": "action-publish",
"language": language.id,
}
response = superuser_client.post(
reverse(
Expand Down
4 changes: 2 additions & 2 deletions cms/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ def test_catalog_page_product(client, wagtail_basics):
start_date = now + timedelta(days=2)
end_date = now + timedelta(days=10)

active_program_1 = ProgramFactory.create()
active_program_2 = ProgramFactory.create()
active_program_1 = ProgramFactory.create(page__language__priority=1)
active_program_2 = ProgramFactory.create(page__language__priority=2)

# Live course page and course with a future course run. Should be included in upcoming context
active_course_run = CourseRunFactory.create(
Expand Down
10 changes: 10 additions & 0 deletions courses/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from .models import (
Course,
CourseLanguage,
CourseRun,
CourseRunCertificate,
CourseRunEnrollment,
Expand Down Expand Up @@ -422,3 +423,12 @@ class PlatformAdmin(TimestampedModelAdmin):
model = Platform
list_display = ["id", "name", "created_on", "updated_on"]
search_fields = ["name"]


@admin.register(CourseLanguage)
class CourseLanguageAdmin(admin.ModelAdmin):
"""Admin for CourseLanguage"""

model = CourseLanguage
list_display = ["id", "name", "priority"]
search_fields = ["name"]
10 changes: 10 additions & 0 deletions courses/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from .models import (
Course,
CourseLanguage,
CourseRun,
CourseRunCertificate,
CourseRunEnrollment,
Expand All @@ -38,6 +39,15 @@ class Meta:
model = Company


class CourseLanguageFactory(DjangoModelFactory):
"""Factory for Course Language"""

name = factory.Sequence(lambda n: f"Language_{n}")

class Meta:
model = CourseLanguage


class PlatformFactory(DjangoModelFactory):
"""Factory for Platform"""

Expand Down
Loading

0 comments on commit 91c897f

Please sign in to comment.