Skip to content

Commit 3d302bf

Browse files
committed
feat: Adding weeks to complete filed into Algolia facet
1 parent fa03d89 commit 3d302bf

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

course_discovery/apps/course_metadata/algolia_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def delegate_attributes(cls):
8383
'secondary_description', 'tertiary_description']
8484
facet_fields = ['availability_level', 'subject_names', 'levels', 'active_languages', 'staff_slugs',
8585
'product_allowed_in', 'product_blocked_in', 'learning_type', 'learning_type_exp',
86-
'product_ai_languages']
86+
'product_ai_languages', 'product_weeks_to_complete']
8787
ranking_fields = ['availability_rank', 'product_recent_enrollment_count', 'promoted_in_spanish_index',
8888
'product_value_per_click_usa', 'product_value_per_click_international',
8989
'product_value_per_lead_usa', 'product_value_per_lead_international']

course_discovery/apps/course_metadata/index.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class EnglishProductIndex(BaseProductIndex):
8080
('active_languages', 'language'), ('product_type', 'product'), ('program_types', 'program_type'),
8181
('staff_slugs', 'staff'), ('product_allowed_in', 'allowed_in'),
8282
('product_blocked_in', 'blocked_in'), 'subscription_eligible',
83-
'subscription_prices', 'learning_type', 'learning_type_exp',
83+
'subscription_prices', 'learning_type', 'learning_type_exp', 'weeks_to_complete',
8484
('product_ai_languages', 'ai_languages'))
8585
ranking_fields = ('availability_rank', ('product_recent_enrollment_count', 'recent_enrollment_count'),
8686
('product_value_per_click_usa', 'value_per_click_usa'),
@@ -117,7 +117,7 @@ class EnglishProductIndex(BaseProductIndex):
117117
'partner', 'availability', 'subject', 'level', 'language', 'product', 'program_type',
118118
'filterOnly(staff)', 'filterOnly(allowed_in)', 'filterOnly(blocked_in)', 'skills.skill',
119119
'skills.category', 'skills.subcategory', 'tags', 'subscription_eligible', 'subscription_prices',
120-
'learning_type', 'learning_type_exp', 'ai_languages.translation_languages',
120+
'learning_type', 'learning_type_exp', 'ai_languages.translation_languages', 'weeks_to_complete',
121121
'ai_languages.transcription_languages',
122122
],
123123
'customRanking': ['asc(availability_rank)', 'desc(recent_enrollment_count)']
@@ -135,7 +135,7 @@ class SpanishProductIndex(BaseProductIndex):
135135
('active_languages', 'language'), ('product_type', 'product'), ('program_types', 'program_type'),
136136
('staff_slugs', 'staff'), ('product_allowed_in', 'allowed_in'),
137137
('product_blocked_in', 'blocked_in'), 'subscription_eligible',
138-
'subscription_prices', 'learning_type', 'learning_type_exp',
138+
'subscription_prices', 'learning_type', 'learning_type_exp', 'weeks_to_complete',
139139
('product_ai_languages', 'ai_languages'))
140140
ranking_fields = ('availability_rank', ('product_recent_enrollment_count', 'recent_enrollment_count'),
141141
('product_value_per_click_usa', 'value_per_click_usa'),
@@ -171,7 +171,7 @@ class SpanishProductIndex(BaseProductIndex):
171171
'contentful_fields.faq_items, contentful_fields.featured_products'
172172
],
173173
'attributesForFaceting': [
174-
'partner', 'availability', 'subject', 'level', 'language', 'product', 'program_type',
174+
'partner', 'availability', 'weeks_to_complete', 'subject', 'level', 'language', 'product', 'program_type',
175175
'filterOnly(staff)', 'filterOnly(allowed_in)', 'filterOnly(blocked_in)',
176176
'skills.skill', 'skills.category', 'skills.subcategory', 'tags', 'subscription_eligible',
177177
'subscription_prices', 'learning_type', 'learning_type_exp', 'ai_languages.translation_languages',

course_discovery/apps/course_metadata/tests/factories.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,19 @@ def staff(self, create, extracted, **kwargs):
496496
class Meta:
497497
model = CourseRun
498498

499+
@factory.post_generation
500+
def make_marketable(self, create, extracted, **kwargs):
501+
if not create:
502+
return
503+
if extracted:
504+
self.type.is_marketable = True
505+
self.type.save()
506+
if not self.seats.exists():
507+
verified_type = SeatTypeFactory(slug="verified", name="Verified")
508+
SeatFactory(course_run=self, type=verified_type, price=100)
509+
self.status = CourseRunStatus.Published
510+
self.save()
511+
499512
@factory.post_generation
500513
def transcript_languages(self, create, extracted, **kwargs):
501514
if create: # pragma: no cover

course_discovery/apps/course_metadata/tests/test_algolia_models.py

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import datetime
22
from collections import ChainMap
3+
from datetime import timedelta
34

45
import ddt
56
import factory
67
import pytest
78
from django.conf import settings
89
from django.contrib.sites.models import Site
910
from django.test import TestCase, override_settings
11+
from django.utils import timezone
1012
from pytz import UTC
1113

1214
from conftest import TEST_DOMAIN
@@ -16,10 +18,10 @@
1618
from course_discovery.apps.course_metadata.choices import ExternalProductStatus, ProgramStatus
1719
from course_discovery.apps.course_metadata.models import CourseRunStatus, CourseType, ProductValue, ProgramType
1820
from course_discovery.apps.course_metadata.tests.factories import (
19-
AdditionalMetadataFactory, CourseFactory, CourseRunFactory, CourseTypeFactory, DegreeAdditionalMetadataFactory,
20-
DegreeFactory, GeoLocationFactory, LevelTypeFactory, OrganizationFactory, ProductMetaFactory, ProgramFactory,
21-
ProgramSubscriptionFactory, ProgramSubscriptionPriceFactory, ProgramTypeFactory, RestrictedCourseRunFactory,
22-
SeatFactory, SeatTypeFactory, SourceFactory, SubjectFactory, VideoFactory
21+
AdditionalMetadataFactory, CourseFactory, CourseRunFactory, CourseRunTypeFactory, CourseTypeFactory,
22+
DegreeAdditionalMetadataFactory, DegreeFactory, GeoLocationFactory, LevelTypeFactory, OrganizationFactory,
23+
ProductMetaFactory, ProgramFactory, ProgramSubscriptionFactory, ProgramSubscriptionPriceFactory, ProgramTypeFactory,
24+
RestrictedCourseRunFactory, SeatFactory, SeatTypeFactory, SourceFactory, SubjectFactory, VideoFactory
2325
)
2426
from course_discovery.apps.ietf_language_tags.models import LanguageTag
2527

@@ -599,6 +601,68 @@ def test_course_ai_languages__no_advertised_run(self):
599601
'transcription_languages': []
600602
}
601603

604+
def test_product_weeks_to_complete_returns_value(self):
605+
course = CourseFactory()
606+
run_type = CourseRunTypeFactory(is_marketable=True)
607+
CourseRunFactory(
608+
course=course,
609+
type=run_type,
610+
weeks_to_complete=8,
611+
start=timezone.now() - timedelta(days=1),
612+
end=timezone.now() + timedelta(days=30),
613+
hidden=False,
614+
make_marketable=True,
615+
)
616+
course.refresh_from_db()
617+
proxy_course = AlgoliaProxyCourse.objects.get(pk=course.id)
618+
assert proxy_course.product_weeks_to_complete == 8
619+
620+
def test_product_weeks_to_complete_none_if_no_advertised_run(self):
621+
course = CourseFactory()
622+
proxy_course = AlgoliaProxyCourse.objects.get(pk=course.id)
623+
assert proxy_course.product_weeks_to_complete is None
624+
625+
def test_multiple_runs_only_advertised_run_is_used(self):
626+
course = CourseFactory()
627+
run_type = CourseRunTypeFactory(is_marketable=True)
628+
CourseRunFactory(
629+
course=course,
630+
type=run_type,
631+
weeks_to_complete=4,
632+
start=timezone.now() - timedelta(days=1),
633+
end=timezone.now() + timedelta(days=10),
634+
hidden=False,
635+
make_marketable=True,
636+
)
637+
advertised_run = CourseRunFactory(
638+
course=course,
639+
type=run_type,
640+
weeks_to_complete=12,
641+
start=timezone.now() - timedelta(days=1),
642+
end=timezone.now() + timedelta(days=20),
643+
hidden=False,
644+
make_marketable=True,
645+
)
646+
course.advertised_course_run_id = advertised_run.id
647+
course.save()
648+
course.refresh_from_db()
649+
proxy_course = AlgoliaProxyCourse.objects.get(pk=course.id)
650+
assert proxy_course.product_weeks_to_complete == 12
651+
652+
@ddt.data(6, None)
653+
def test_weeks_to_complete_property_type(self, weeks):
654+
course = CourseFactory()
655+
if weeks is not None:
656+
run = CourseRunFactory(course=course, weeks_to_complete=weeks)
657+
if hasattr(course, 'set_advertised_course_run'):
658+
course.set_advertised_course_run(run)
659+
else:
660+
course.advertised_course_run_id = run.id
661+
course.save()
662+
proxy_course = AlgoliaProxyCourse.objects.get(pk=course.id)
663+
value = proxy_course.product_weeks_to_complete
664+
assert value is None or isinstance(value, int)
665+
602666

603667
@ddt.ddt
604668
@pytest.mark.django_db
@@ -610,6 +674,16 @@ class TestAlgoliaProxyProgram(TestAlgoliaProxyWithEdxPartner):
610674
IN_FIFTEEN_DAYS = datetime.datetime.now(UTC) + datetime.timedelta(days=15)
611675
IN_TWO_MONTHS = datetime.datetime.now(UTC) + datetime.timedelta(days=60)
612676

677+
def test_product_weeks_to_complete_always_none(self):
678+
program = ProgramFactory()
679+
proxy_program = AlgoliaProxyProgram.objects.get(pk=program.id)
680+
assert proxy_program.product_weeks_to_complete is None
681+
682+
def test_product_weeks_to_complete_none_even_with_related_courses(self):
683+
program = ProgramFactory()
684+
proxy_program = AlgoliaProxyProgram.objects.get(pk=program.id)
685+
assert proxy_program.product_weeks_to_complete is None
686+
613687
def attach_course_run(self, course, availability="Archived"):
614688
course_start = course_end = ''
615689
if availability == 'none':

0 commit comments

Comments
 (0)