diff --git a/lms/djangoapps/experiments/utils.py b/lms/djangoapps/experiments/utils.py index 7fe29f495a..4648166761 100644 --- a/lms/djangoapps/experiments/utils.py +++ b/lms/djangoapps/experiments/utils.py @@ -21,7 +21,7 @@ from openedx.core.djangoapps.catalog.utils import get_programs from openedx.core.djangoapps.django_comment_common.models import Role from openedx.core.djangoapps.schedules.models import Schedule from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace -from openedx.features.course_duration_limits.access import get_user_course_expiration_date +from openedx.features.course_duration_limits.access import get_user_course_expiration_date, get_user_course_duration from openedx.features.course_duration_limits.models import CourseDurationLimitConfig from student.models import CourseEnrollment from xmodule.partitions.partitions_service import get_all_partitions_for_course, get_user_partition_groups @@ -324,6 +324,8 @@ def get_base_experiment_metadata_context(course, user, enrollment, user_enrollme user, enrollment, course ) + deadline, duration = get_audit_access_expiration(user, course) + return { 'upgrade_link': upgrade_link, 'upgrade_price': six.text_type(get_cosmetic_verified_display_price(course)), @@ -333,7 +335,8 @@ def get_base_experiment_metadata_context(course, user, enrollment, user_enrollme 'pacing_type': 'self_paced' if course.self_paced else 'instructor_paced', 'dynamic_upgrade_deadline': dynamic_upgrade_deadline, 'course_upgrade_deadline': course_upgrade_deadline, - 'audit_access_deadline': get_audit_access_expiration(user, course), + 'audit_access_deadline': deadline, + 'course_duration': duration, 'course_key': course.id, 'course_start': course.start, 'course_end': course.end, @@ -345,12 +348,12 @@ def get_base_experiment_metadata_context(course, user, enrollment, user_enrollme def get_audit_access_expiration(user, course): """ - Return the expiration date for the user's audit access to this course. + Return the expiration date and course duration for the user's audit access to this course. """ if not CourseDurationLimitConfig.enabled_for_enrollment(user=user, course_key=course.id): - return None + return None, None - return get_user_course_expiration_date(user, course) + return get_user_course_expiration_date(user, course), get_user_course_duration(user, course) # TODO: clean up as part of REVEM-199 (START) diff --git a/lms/templates/experiments/user_metadata.html b/lms/templates/experiments/user_metadata.html index 94d4f07198..a650f6f935 100644 --- a/lms/templates/experiments/user_metadata.html +++ b/lms/templates/experiments/user_metadata.html @@ -16,6 +16,7 @@ user_metadata = { 'upgrade_link', 'upgrade_price', 'audit_access_deadline', + 'course_duration', 'pacing_type', 'has_staff_access', 'forum_roles', @@ -42,11 +43,19 @@ for datekey in ( 'course_end', 'dynamic_upgrade_deadline', 'course_upgrade_deadline', + 'audit_access_deadline', ): user_metadata[datekey] = ( context.get(datekey).isoformat() if context.get(datekey) else None ) +for timedeltakey in ( + 'course_duration', +): + user_metadata[timedeltakey] = ( + context.get(timedeltakey).total_seconds() if context.get(timedeltake) else None + ) + course_key = context.get('course_key') if course and not course_key: course_key = course.id diff --git a/openedx/features/course_duration_limits/access.py b/openedx/features/course_duration_limits/access.py index 1e3515382c..07a5c6310f 100644 --- a/openedx/features/course_duration_limits/access.py +++ b/openedx/features/course_duration_limits/access.py @@ -54,6 +54,39 @@ class AuditExpiredError(AccessError): additional_context_user_message) +def get_user_course_duration(user, course): + """ + Return a timedelta measuring the duration of the course for a particular user. + + Business Logic: + - Course access duration is bounded by the min and max duration. + - If course fields are missing, default course access duration to MIN_DURATION. + """ + + access_duration = MIN_DURATION + + verified_mode = CourseMode.verified_mode_for_course(course=course, include_expired=True) + + if not verified_mode: + return None + + enrollment = CourseEnrollment.get_enrollment(user, course.id) + if enrollment is None or enrollment.mode != CourseMode.AUDIT: + return None + + # The user course expiration date is the content availability date + # plus the weeks_to_complete field from course-discovery. + discovery_course_details = get_course_run_details(course.id, ['weeks_to_complete']) + expected_weeks = discovery_course_details.get('weeks_to_complete') + if expected_weeks: + access_duration = timedelta(weeks=expected_weeks) + + # Course access duration is bounded by the min and max duration. + access_duration = max(MIN_DURATION, min(MAX_DURATION, access_duration)) + + return access_duration + + def get_user_course_expiration_date(user, course): """ Return expiration date for given user course pair. @@ -63,11 +96,8 @@ def get_user_course_expiration_date(user, course): - Course access duration is bounded by the min and max duration. - If course fields are missing, default course access duration to MIN_DURATION. """ - access_duration = MIN_DURATION - - verified_mode = CourseMode.verified_mode_for_course(course=course, include_expired=True) - - if not verified_mode: + access_duration = get_user_course_duration(user, course) + if access_duration is None: return None enrollment = CourseEnrollment.get_enrollment(user, course.id) @@ -90,16 +120,6 @@ def get_user_course_expiration_date(user, course): except CourseEnrollment.schedule.RelatedObjectDoesNotExist: content_availability_date = max(enrollment.created, course.start) - # The user course expiration date is the content availability date - # plus the weeks_to_complete field from course-discovery. - discovery_course_details = get_course_run_details(course.id, ['weeks_to_complete']) - expected_weeks = discovery_course_details.get('weeks_to_complete') - if expected_weeks: - access_duration = timedelta(weeks=expected_weeks) - - # Course access duration is bounded by the min and max duration. - access_duration = max(MIN_DURATION, min(MAX_DURATION, access_duration)) - return content_availability_date + access_duration