Skip to content

Commit c82cf83

Browse files
authored
Fix Featured Items on Homepage (#2232)
* update to use Q filter and start writing test to verify the number is correct * Test update
1 parent c39fdfe commit c82cf83

File tree

4 files changed

+91
-6
lines changed

4 files changed

+91
-6
lines changed

cms/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from cms.constants import CERTIFICATE_INDEX_SLUG, INSTRUCTOR_INDEX_SLUG
1919
from cms.exceptions import WagtailSpecificPageError
2020
from cms.models import Page
21+
from courses.constants import HOMEPAGE_CACHE_AGE
2122
from courses.models import Course, Program
2223
from courses.utils import (
2324
get_enrollable_courseruns_qs,
@@ -39,7 +40,6 @@
3940
]
4041
RESOURCE_PAGE_SLUGS = [slugify(title) for title in RESOURCE_PAGE_TITLES]
4142
PROGRAM_INDEX_PAGE_PROPERTIES = dict(title="Programs") # noqa: C408
42-
HOMEPAGE_CACHE_AGE = 86400 # 24 hours
4343

4444

4545
def get_home_page(raise_if_missing=True, check_specific=False) -> Page: # noqa: FBT002

cms/models.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -728,18 +728,20 @@ def get_cached_featured_products(self):
728728
Retrieves teh featured products that were generated using cms/api/create_featured_items either from the
729729
management command or the daily cron job. This is used to display the featured products on the home page.
730730
"""
731-
start_of_day = now_in_utc() - timedelta(days=1)
732-
end_of_day = now_in_utc() + timedelta(days=1)
731+
now = now_in_utc()
733732
redis_cache = caches["redis"]
734733
cached_featured_products = redis_cache.get("CMS_homepage_featured_courses")
735734
if len(cached_featured_products) > 0:
736735
featured_product_ids = [course.id for course in cached_featured_products]
737736
relevant_run_course_ids = (
738737
CourseRun.objects.filter(live=True)
739738
.filter(
740-
course__id__in=featured_product_ids,
741-
enrollment_start__lte=start_of_day,
742-
enrollment_end__gte=end_of_day,
739+
models.Q(course__id__in=featured_product_ids)
740+
& models.Q(enrollment_start__lte=now)
741+
& (
742+
models.Q(enrollment_end__gte=now)
743+
| models.Q(enrollment_end__isnull=True)
744+
)
743745
)
744746
.values_list("course__id", flat=True)
745747
)

cms/models_test.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
import pytest
99
from django.contrib.auth.models import AnonymousUser, Group
1010
from django.contrib.sessions.middleware import SessionMiddleware
11+
from django.core.cache import caches
1112
from django.test.client import RequestFactory
1213
from django.urls import resolve
1314
from mitol.common.factories import UserFactory
1415
from mitol.common.utils.datetime import now_in_utc
1516
from mitol.olposthog.features import is_enabled
1617

18+
from cms.api import create_featured_items
1719
from cms.constants import CMS_EDITORS_GROUP_NAME
1820
from cms.factories import (
1921
CertificatePageFactory,
2022
CoursePageFactory,
2123
FlexiblePricingFormFactory,
24+
HomePageFactory,
2225
InstructorPageFactory,
2326
ProgramPageFactory,
2427
ResourcePageFactory,
@@ -45,6 +48,7 @@
4548
from flexiblepricing.constants import FlexiblePriceStatus
4649
from flexiblepricing.factories import FlexiblePriceFactory, FlexiblePriceTierFactory
4750
from flexiblepricing.models import FlexiblePrice
51+
from main import features
4852

4953
pytestmark = [pytest.mark.django_db]
5054

@@ -681,3 +685,80 @@ def test_flexible_pricing_request_form_context(flex_form_for_course):
681685
else:
682686
assert context["product"] is None
683687
assert context["product_page"] == course_page.url
688+
689+
690+
def test_homepage__featured_products(settings, mocker):
691+
settings.FEATURES[features.ENABLE_NEW_DESIGN] = True
692+
now = now_in_utc()
693+
future_date = now + timedelta(days=1)
694+
past_date = now - timedelta(days=1)
695+
further_future_date = future_date + timedelta(days=1)
696+
further_past_date = past_date - timedelta(days=1)
697+
furthest_future_date = further_future_date + timedelta(days=1)
698+
redis_cache = caches["redis"]
699+
# Ensure the key is empty since pytest doesn't clear the cache between tests
700+
featured_courses = redis_cache.get("CMS_homepage_featured_courses")
701+
if featured_courses is not None:
702+
redis_cache.delete("CMS_homepage_featured_courses")
703+
assert redis_cache.get("CMS_homepage_featured_courses") is None
704+
705+
enrollable_future_course = CourseFactory.create(page=None, live=True)
706+
enrollable_future_course_page = CoursePageFactory.create(
707+
course=enrollable_future_course, live=True
708+
)
709+
enrollable_future_courserun = CourseRunFactory.create(
710+
course=enrollable_future_course,
711+
live=True,
712+
start_date=future_date,
713+
enrollment_start=further_past_date,
714+
enrollment_end=further_future_date,
715+
end_date=furthest_future_date,
716+
)
717+
create_featured_items()
718+
assert len(redis_cache.get("CMS_homepage_featured_courses")) == 1
719+
hf = HomePageFactory.create()
720+
assert hf.get_cached_featured_products == [
721+
{
722+
"title": enrollable_future_course_page.title,
723+
"description": enrollable_future_course_page.description,
724+
"feature_image": enrollable_future_course_page.feature_image,
725+
"start_date": enrollable_future_courserun.start_date,
726+
"url_path": enrollable_future_course_page.get_url(),
727+
"is_program": enrollable_future_course_page.is_program_page,
728+
"is_self_paced": False,
729+
"program_type": None,
730+
}
731+
]
732+
# Remove the previous course from the potential courses to be featured, this will also test a course as unenrollable
733+
enrollable_future_course.live = False
734+
enrollable_future_course.save()
735+
736+
enrollable_future_course_with_no_enrollment_end = CourseFactory.create(
737+
page=None, live=True
738+
)
739+
enrollable_future_course_with_no_enrollment_end_page = CoursePageFactory.create(
740+
course=enrollable_future_course_with_no_enrollment_end, live=True
741+
)
742+
enrollable_future_courserun_with_no_enrollment_end = CourseRunFactory.create(
743+
course=enrollable_future_course_with_no_enrollment_end,
744+
live=True,
745+
start_date=future_date,
746+
enrollment_start=further_past_date,
747+
enrollment_end=None,
748+
end_date=furthest_future_date,
749+
)
750+
create_featured_items()
751+
assert len(redis_cache.get("CMS_homepage_featured_courses")) == 1
752+
hf = HomePageFactory.create()
753+
assert hf.get_cached_featured_products == [
754+
{
755+
"title": enrollable_future_course_with_no_enrollment_end_page.title,
756+
"description": enrollable_future_course_with_no_enrollment_end_page.description,
757+
"feature_image": enrollable_future_course_with_no_enrollment_end_page.feature_image,
758+
"start_date": enrollable_future_courserun_with_no_enrollment_end.start_date,
759+
"url_path": enrollable_future_course_with_no_enrollment_end_page.get_url(),
760+
"is_program": enrollable_future_course_with_no_enrollment_end_page.is_program_page,
761+
"is_self_paced": False,
762+
"program_type": None,
763+
}
764+
]

courses/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@
2828
zip(ALL_ENROLL_CHANGE_STATUSES, ALL_ENROLL_CHANGE_STATUSES)
2929
)
3030

31+
HOMEPAGE_CACHE_AGE = 86400 # 24 hours
32+
3133
SYNCED_COURSE_RUN_FIELD_MSG = "This value is synced automatically with edX studio."

0 commit comments

Comments
 (0)