feat: [VAN-1260] Program recomendation based on course (#31663)
This commit is contained in:
@@ -45,7 +45,7 @@ class RecommendedProgramSerializer(serializers.Serializer):
|
||||
marketingUrl = serializers.URLField(source="marketing_url")
|
||||
coursesCount = serializers.IntegerField(source="courses_count")
|
||||
pacingType = serializers.CharField(source="pacing_type")
|
||||
weeksToComplete = serializers.IntegerField(source="weeks_to_complete")
|
||||
weeksToComplete = serializers.CharField(source="weeks_to_complete")
|
||||
minHours = serializers.IntegerField(source="min_hours")
|
||||
maxHours = serializers.IntegerField(source="max_hours")
|
||||
type = serializers.CharField()
|
||||
@@ -57,7 +57,7 @@ class RecommendationsSerializer(serializers.Serializer):
|
||||
courses = serializers.ListField(
|
||||
child=RecommendedCourseSerializer(), allow_empty=True
|
||||
)
|
||||
# programUpsell = RecommendedProgramSerializer(source="program_upsell") use this for VAN-1260
|
||||
programUpsell = RecommendedProgramSerializer(source="program_upsell")
|
||||
isControl = serializers.BooleanField(
|
||||
source="is_control",
|
||||
default=None
|
||||
|
||||
@@ -9,8 +9,8 @@ from algoliasearch.search_client import SearchClient
|
||||
from django.conf import settings
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from openedx.core.djangoapps.catalog.utils import get_course_data
|
||||
|
||||
from openedx.core.djangoapps.catalog.utils import get_course_data, get_programs
|
||||
from lms.djangoapps.program_enrollments.api import fetch_program_enrollments_by_student
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -85,6 +85,34 @@ def _has_country_restrictions(product, user_country):
|
||||
return user_country in block_list or (bool(allow_list) and user_country not in allow_list)
|
||||
|
||||
|
||||
def _get_program_duration(weeks):
|
||||
"""
|
||||
Helper method that returns the program duration in textual form.
|
||||
"""
|
||||
total_months = round(weeks / 4)
|
||||
|
||||
if total_months < 1:
|
||||
return f'{total_months} weeks'
|
||||
|
||||
if 1 <= total_months < 12:
|
||||
return f'{total_months} months'
|
||||
|
||||
total_years = round(total_months / 12)
|
||||
total_remainder_months = round(total_months % 12)
|
||||
|
||||
if total_remainder_months == 0:
|
||||
return f'{total_years} years'
|
||||
|
||||
if total_years == 1 and total_remainder_months == 1:
|
||||
return f'1 year {total_remainder_months} months'
|
||||
|
||||
if total_remainder_months == 1:
|
||||
return f'{total_years} years 1 months'
|
||||
|
||||
else:
|
||||
return f'{total_years} years {total_remainder_months} months'
|
||||
|
||||
|
||||
def get_active_course_run(course_data):
|
||||
"""Helper method to get course active run"""
|
||||
active_course_runs = [
|
||||
@@ -214,3 +242,57 @@ def filter_recommended_courses(
|
||||
filtered_recommended_courses.append(course_data)
|
||||
|
||||
return filtered_recommended_courses
|
||||
|
||||
|
||||
def get_programs_based_on_course(request_course, country_code, user):
|
||||
"""
|
||||
Returns a program for the course. If a course is part of multiple programs,
|
||||
this function returns the program with the highest price.
|
||||
"""
|
||||
max_price, max_price_program = 0, {}
|
||||
programs = get_programs(course=request_course)
|
||||
|
||||
if not programs:
|
||||
return None
|
||||
|
||||
for program in programs:
|
||||
if program.get('status') != 'active' or _has_country_restrictions(program, country_code):
|
||||
continue
|
||||
|
||||
price = program['price_ranges'][0]['total']
|
||||
if price > max_price:
|
||||
if fetch_program_enrollments_by_student(program_uuids=[program.get('uuid')], user=user).exists():
|
||||
continue
|
||||
|
||||
course_keys = [
|
||||
course['key']
|
||||
for course in program.get('courses', [])
|
||||
if course.get('key') and course.get('key') != request_course
|
||||
]
|
||||
if _remove_user_enrolled_course_keys(user, course_keys):
|
||||
max_price_program = program
|
||||
max_price = price
|
||||
|
||||
if not max_price_program:
|
||||
return None
|
||||
|
||||
course_pacing_type, total_weeks_to_complete = '', 0
|
||||
for course in max_price_program.get('courses'):
|
||||
for course_run in course.get('course_runs'):
|
||||
if course_run.get('status') == 'published':
|
||||
if not course_pacing_type:
|
||||
course_pacing_type = course_run.get("pacing_type")
|
||||
total_weeks_to_complete += int(course_run.get("weeks_to_complete"))
|
||||
|
||||
program_upsell = {
|
||||
"title": max_price_program.get('title'),
|
||||
"marketing_url": max_price_program.get('marketing_url'),
|
||||
"courses_count": len(max_price_program.get('courses')),
|
||||
"pacing_type": course_pacing_type,
|
||||
"weeks_to_complete": _get_program_duration(total_weeks_to_complete),
|
||||
"min_hours": max_price_program.get('min_hours_effort_per_week'),
|
||||
"max_hours": max_price_program.get('max_hours_effort_per_week'),
|
||||
"type": max_price_program.get('type'),
|
||||
}
|
||||
|
||||
return program_upsell
|
||||
|
||||
@@ -122,7 +122,7 @@ class AmplitudeRecommendationsView(APIView):
|
||||
return Response(
|
||||
RecommendationsSerializer(
|
||||
{
|
||||
# "program_upsell": program_upsell, // pass program_upsell here for VAN-1260
|
||||
"program_upsell": None,
|
||||
"courses": recommended_courses,
|
||||
"is_control": is_control,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user