Skip to content

Commit

Permalink
External/3rd Party Programs (#2062)
Browse files Browse the repository at this point in the history
* External Page for Program (CMS)

* Filter and Sort Program Pages

* Changes catalog page implementation to support External Programs

* Added support for External Programs in product page

* Fixed carousal card data url

Co-authored-by: Arslan Ashraf <arslan.ashraf@arbisoft.com>
Co-authored-by: Arslan Ashraf <34372316+arslanashraf7@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 15, 2021
1 parent 006aa58 commit e951abc
Show file tree
Hide file tree
Showing 13 changed files with 694 additions and 28 deletions.
28 changes: 19 additions & 9 deletions cms/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
DEFAULT_SITE_PROPS = dict(hostname="localhost", port=80)


def filter_and_sort_catalog_pages(program_pages, course_pages, external_course_pages):
def filter_and_sort_catalog_pages(
program_pages, course_pages, external_course_pages, external_program_pages
):
"""
Filters program and course pages to only include those that should be visible in the catalog, then returns a tuple
of sorted lists of pages
Expand All @@ -23,10 +25,11 @@ def filter_and_sort_catalog_pages(program_pages, course_pages, external_course_p
program_pages (iterable of ProgramPage): ProgramPages to filter and sort
course_pages (iterable of CoursePage): CoursePages to filter and sort
external_course_pages (iterable of ExternalCoursePage): ExternalCoursePages to filter and sort
external_program_pages (iterable of ExternalProgramPage): ExternalProgramPages to filter and sort
Returns:
tuple of (list of Pages): A tuple containing a list of combined ProgramPages, CoursePages an ExternalCoursePages, a list of
ProgramPages, and a list of CoursePages and ExternalCoursePages, all sorted by the next course run date and title
tuple of (list of Pages): A tuple containing a list of combined ProgramPages, CoursePages, ExternalCoursePages and ExternalProgramPages, a list of
ProgramPages and ExternalProgramPages, and a list of CoursePages and ExternalCoursePages, all sorted by the next course/program run date and title
"""
valid_program_pages = [
page for page in program_pages if page.product.is_catalog_visible
Expand All @@ -37,20 +40,26 @@ def filter_and_sort_catalog_pages(program_pages, course_pages, external_course_p

valid_external_course_pages = list(external_course_pages)

valid_external_program_pages = list(external_program_pages)

page_run_dates = {
page: (
page.next_run_date
if page.is_external_course_page
else page.product.next_run_date
page.next_run_date if page.is_external_page else page.product.next_run_date
)
or datetime(year=MINYEAR, month=1, day=1, tzinfo=pytz.UTC)
for page in itertools.chain(
valid_program_pages, valid_course_pages, valid_external_course_pages
valid_program_pages,
valid_course_pages,
valid_external_course_pages,
valid_external_program_pages,
)
}
return (
sorted(
valid_program_pages + valid_course_pages + valid_external_course_pages,
valid_program_pages
+ valid_external_program_pages
+ valid_course_pages
+ valid_external_course_pages,
# ProgramPages with the same next run date as a CoursePage should be sorted first
key=lambda page: (
page_run_dates[page],
Expand All @@ -59,7 +68,8 @@ def filter_and_sort_catalog_pages(program_pages, course_pages, external_course_p
),
),
sorted(
valid_program_pages, key=lambda page: (page_run_dates[page], page.title)
valid_program_pages + valid_external_program_pages,
key=lambda page: (page_run_dates[page], page.title),
),
sorted(
valid_course_pages + valid_external_course_pages,
Expand Down
30 changes: 28 additions & 2 deletions cms/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from cms.factories import (
CoursePageFactory,
ExternalCoursePageFactory,
ExternalProgramPageFactory,
ProgramPageFactory,
)
from cms.models import ExternalCoursePage
Expand All @@ -24,6 +25,9 @@ def test_filter_and_sort_catalog_pages(): # pylint:disable=too-many-locals
now = now_in_utc()

earlier_external_course_page = ExternalCoursePageFactory.create(start_date=now)
earlier_external_program_page = ExternalProgramPageFactory.create(
start_date=now, course_count=2
)
non_program_run = CourseRunFactory.create(
course__no_program=True, start_date=(now + timedelta(days=1))
)
Expand All @@ -32,6 +36,10 @@ def test_filter_and_sort_catalog_pages(): # pylint:disable=too-many-locals
later_external_course_page = ExternalCoursePageFactory.create(
start_date=now + timedelta(days=4)
)

later_external_program_page = ExternalProgramPageFactory.create(
start_date=now + timedelta(days=4)
)
# Create course run with past start_date and future enrollment_end, which should appear in the catalog
future_enrollment_end_run = CourseRunFactory.create(
past_start=True,
Expand All @@ -52,6 +60,11 @@ def test_filter_and_sort_catalog_pages(): # pylint:disable=too-many-locals

external_course_pages = [earlier_external_course_page, later_external_course_page]

external_program_pages = [
earlier_external_program_page,
later_external_program_page,
]

initial_course_pages = CoursePageFactory.create_batch(
len(all_runs), course=factory.Iterator(run.course for run in all_runs)
)
Expand All @@ -63,29 +76,42 @@ def test_filter_and_sort_catalog_pages(): # pylint:disable=too-many-locals
)

all_pages, program_pages, course_pages = filter_and_sort_catalog_pages(
initial_program_pages, initial_course_pages, external_course_pages
initial_program_pages,
initial_course_pages,
external_course_pages,
external_program_pages,
)

# Combined pages and course pages should not include the past course run
assert len(all_pages) == (
len(initial_program_pages)
+ len(initial_course_pages)
+ len(external_course_pages)
+ len(external_program_pages)
- 1
)
assert len(course_pages) == (
len(initial_course_pages) + len(external_course_pages) - 1
)

assert len(program_pages) == (
len(initial_program_pages) + len(external_program_pages)
)

# Filtered out external course page because it does not have a `course` attribute
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] == [
assert [
page if page.is_external_program_page else page.program
for page in program_pages
] == [
earlier_external_program_page,
first_program_run.course.program,
second_program_run.course.program,
later_external_program_page,
]
expected_course_run_sort = [
future_enrollment_end_run,
Expand Down
34 changes: 34 additions & 0 deletions cms/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
CoursePage,
CoursesInProgramPage,
ExternalCoursePage,
ExternalProgramPage,
FacultyMembersPage,
ForTeamsPage,
FrequentlyAskedQuestion,
Expand Down Expand Up @@ -135,6 +136,39 @@ def post_gen(obj, create, extracted, **kwargs): # pylint:disable=unused-argumen
return obj


class ExternalProgramPageFactory(wagtail_factories.PageFactory):
"""ExternalProgramPage factory class"""

title = factory.Sequence("Test page - External Program {0}".format)
start_date = factory.Faker(
"date_time_this_month", before_now=True, after_now=False, tzinfo=pytz.utc
)
price = factory.fuzzy.FuzzyDecimal(low=1, high=123)
external_url = factory.Faker("uri")
readable_id = factory.Sequence(
lambda number: "external-course:/v{}/{}".format(number, FAKE.slug())
)
subhead = factory.fuzzy.FuzzyText(prefix="Subhead ")
thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory)
background_image = factory.SubFactory(wagtail_factories.ImageFactory)
course_count = factory.fuzzy.FuzzyInteger(1)

class Meta:
model = ExternalProgramPage

@factory.post_generation
def post_gen(obj, create, extracted, **kwargs): # pylint:disable=unused-argument
"""Post-generation hook"""
if create:
# Move the created page to be a child of the program index page
index_page = ProgramIndexPage.objects.first()
if not index_page:
raise ObjectDoesNotExist
obj.move(index_page, "last-child")
obj.refresh_from_db()
return obj


class LearningOutcomesPageFactory(wagtail_factories.PageFactory):
"""LearningOutcomesPage factory class"""

Expand Down
Loading

0 comments on commit e951abc

Please sign in to comment.