Conditionalize track selection based on settings of course duration and content gating
This commit is contained in:
@@ -31,6 +31,7 @@ from openedx.core.djangoapps.embargo import api as embargo_api
|
||||
from openedx.core.djangoapps.programs.utils import ProgramDataExtender, ProgramProgressMeter
|
||||
from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
from student.models import CourseEnrollment
|
||||
from util.db import outer_atomic
|
||||
from xmodule.modulestore.django import modulestore
|
||||
@@ -192,6 +193,10 @@ class ChooseModeView(View):
|
||||
user=request.user,
|
||||
course_key=course_key
|
||||
),
|
||||
"course_duration_limit_enabled": CourseDurationLimitConfig.enabled_for_enrollment(
|
||||
user=request.user,
|
||||
course_key=course_key
|
||||
),
|
||||
}
|
||||
context.update(
|
||||
get_experiment_user_metadata_context(
|
||||
|
||||
@@ -86,7 +86,7 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<div class="wrapper-copy">
|
||||
<span class="deco-ribbon"></span>
|
||||
% if has_credit_upsell:
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4 class="title">${_("Pursue Academic Credit with the Verified Track")}</h4>
|
||||
% else:
|
||||
<h4 class="title">${_("Pursue Academic Credit with a Verified Certificate")}</h4>
|
||||
@@ -97,12 +97,16 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<p>
|
||||
<div class="wrapper-copy-inline">
|
||||
<div class="copy-inline">
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4>${_("Benefits of the Verified Track")}</h4>
|
||||
<ul>
|
||||
<li>${Text(_("{b_start}Eligible for credit:{b_end} Receive academic credit after successfully completing the course")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% if course_duration_limit_enabled:
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
% if content_gating_enabled:
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
<li>${Text(_("{b_start}Easily Sharable: {b_end}Add the certificate to your CV or resume, or post it directly on LinkedIn.")).format(**b_tag_kwargs)}</li>
|
||||
</ul>
|
||||
% else:
|
||||
@@ -126,7 +130,7 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
</p>
|
||||
</div>
|
||||
% else:
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4 class="title">${_("Pursue the Verified Track")}</h4>
|
||||
% else:
|
||||
<h4 class="title">${_("Pursue a Verified Certificate")}</h4>
|
||||
@@ -138,11 +142,15 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<p>
|
||||
<div class="wrapper-copy-inline">
|
||||
<div class="copy-inline">
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4>${_("Benefits of the Verified Track")}</h4>
|
||||
<ul>
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% if course_duration_limit_enabled:
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
% if content_gating_enabled:
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
<li>${Text(_("{b_start}Easily Sharable: {b_end}Add the certificate to your CV or resume, or post it directly on LinkedIn.")).format(**b_tag_kwargs)}</li>
|
||||
</ul>
|
||||
% else:
|
||||
@@ -158,7 +166,7 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<ul class="list-actions">
|
||||
<li class="action action-select">
|
||||
<input type="hidden" name="contribution" value="${min_price}" />
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<input type="submit" name="verified_mode" value="${_('Pursue the Verified Track')} ($${min_price} USD)" />
|
||||
% else:
|
||||
<input type="submit" name="verified_mode" value="${_('Pursue a Verified Certificate')} ($${min_price} USD)" />
|
||||
@@ -205,8 +213,12 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<h4 class="title">${_("Audit This Course (No Certificate)")}</h4>
|
||||
<div class="copy">
|
||||
## Translators: b_start notes the beginning of a section of text bolded for emphasis, and b_end marks the end of the bolded text.
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled and course_duration_limit_enabled:
|
||||
<p>${Text(_("Audit this course for free and have access to course materials and discussions forums. {b_start}This track does not include graded assignments, or unlimited course access.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% elif content_gating_enabled and not course_duration_limit_enabled:
|
||||
<p>${Text(_("Audit this course for free and have access to course materials and discussions forums. {b_start}This track does not include graded assignments.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% elif not content_gating_enabled and course_duration_limit_enabled:
|
||||
<p>${Text(_("Audit this course for free and have access to course materials and discussions forums. {b_start}This track does not include unlimited course access.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% else:
|
||||
<p>${Text(_("Audit this course for free and have complete access to all the course material, activities, tests, and forums. {b_start}Please note that this track does not offer a certificate for learners who earn a passing grade.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% endif
|
||||
|
||||
25
openedx/core/djangoapps/config_model_utils/utils.py
Normal file
25
openedx/core/djangoapps/config_model_utils/utils.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""utils for feature-based enrollments"""
|
||||
from experiments.models import ExperimentData
|
||||
from openedx.features.course_duration_limits.config import (
|
||||
EXPERIMENT_ID,
|
||||
EXPERIMENT_DATA_HOLDBACK_KEY
|
||||
)
|
||||
|
||||
|
||||
def is_in_holdback(user):
|
||||
"""
|
||||
Return true if given user is in holdback expermiment
|
||||
"""
|
||||
in_holdback = False
|
||||
if user and user.is_authenticated:
|
||||
try:
|
||||
holdback_value = ExperimentData.objects.get(
|
||||
user=user,
|
||||
experiment_id=EXPERIMENT_ID,
|
||||
key=EXPERIMENT_DATA_HOLDBACK_KEY,
|
||||
).value
|
||||
in_holdback = holdback_value == 'True'
|
||||
except ExperimentData.DoesNotExist:
|
||||
pass
|
||||
|
||||
return in_holdback
|
||||
@@ -12,14 +12,12 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
||||
from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_specific_student
|
||||
from experiments.models import ExperimentData
|
||||
from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
|
||||
from openedx.core.djangoapps.config_model_utils.utils import is_in_holdback
|
||||
from openedx.features.content_type_gating.helpers import has_staff_roles
|
||||
from openedx.features.course_duration_limits.config import (
|
||||
CONTENT_TYPE_GATING_FLAG,
|
||||
FEATURE_BASED_ENROLLMENT_GLOBAL_KILL_FLAG,
|
||||
EXPERIMENT_ID,
|
||||
EXPERIMENT_DATA_HOLDBACK_KEY
|
||||
)
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
@@ -109,18 +107,7 @@ class ContentTypeGatingConfig(StackedConfigurationModel):
|
||||
return False
|
||||
|
||||
# check if user is in holdback
|
||||
is_in_holdback = False
|
||||
if user and user.is_authenticated and (user_variable_represents_correct_user):
|
||||
try:
|
||||
holdback_value = ExperimentData.objects.get(
|
||||
user=user,
|
||||
experiment_id=EXPERIMENT_ID,
|
||||
key=EXPERIMENT_DATA_HOLDBACK_KEY,
|
||||
).value
|
||||
is_in_holdback = holdback_value == 'True'
|
||||
except ExperimentData.DoesNotExist:
|
||||
pass
|
||||
if is_in_holdback:
|
||||
if user_variable_represents_correct_user and is_in_holdback(user):
|
||||
return False
|
||||
|
||||
# enrollment might be None if the user isn't enrolled. In that case,
|
||||
|
||||
@@ -13,20 +13,18 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from experiments.models import ExperimentData
|
||||
from lms.djangoapps.courseware.masquerade import (
|
||||
get_course_masquerade,
|
||||
get_masquerade_role,
|
||||
is_masquerading_as_specific_student
|
||||
)
|
||||
from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
|
||||
from openedx.core.djangoapps.config_model_utils.utils import is_in_holdback
|
||||
from openedx.features.content_type_gating.helpers import has_staff_roles
|
||||
from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
|
||||
from openedx.features.course_duration_limits.config import (
|
||||
CONTENT_TYPE_GATING_FLAG,
|
||||
FEATURE_BASED_ENROLLMENT_GLOBAL_KILL_FLAG,
|
||||
EXPERIMENT_ID,
|
||||
EXPERIMENT_DATA_HOLDBACK_KEY
|
||||
)
|
||||
from student.models import CourseEnrollment
|
||||
from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID
|
||||
@@ -130,20 +128,10 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
|
||||
return False
|
||||
|
||||
no_masquerade = get_course_masquerade(user, course_key) is None
|
||||
is_in_holdback = False
|
||||
student_masquerade = is_masquerading_as_specific_student(user, course_key)
|
||||
# TODO: clean up as part of REV-100
|
||||
if user and user.username and (no_masquerade or student_masquerade):
|
||||
try:
|
||||
holdback_value = ExperimentData.objects.get(
|
||||
user=user,
|
||||
experiment_id=EXPERIMENT_ID,
|
||||
key=EXPERIMENT_DATA_HOLDBACK_KEY,
|
||||
).value
|
||||
is_in_holdback = holdback_value == 'True'
|
||||
except ExperimentData.DoesNotExist:
|
||||
pass
|
||||
if is_in_holdback:
|
||||
|
||||
# check if user is in holdback
|
||||
if (no_masquerade or student_masquerade) and is_in_holdback(user):
|
||||
return False
|
||||
|
||||
# enrollment might be None if the user isn't enrolled. In that case,
|
||||
|
||||
@@ -80,9 +80,9 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
|
||||
user = self.user
|
||||
course_key = self.course_overview.id
|
||||
|
||||
query_count = 8
|
||||
if not pass_enrollment:
|
||||
query_count = 9
|
||||
query_count = 9
|
||||
if pass_enrollment and already_enrolled:
|
||||
query_count = 8
|
||||
|
||||
with self.assertNumQueries(query_count):
|
||||
enabled = CourseDurationLimitConfig.enabled_for_enrollment(
|
||||
|
||||
@@ -154,7 +154,7 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
|
||||
<div class="wrapper-copy">
|
||||
<span class="deco-ribbon"></span>
|
||||
% if has_credit_upsell:
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4 class="title">${_("Pursue Academic Credit with the Verified Track")}</h4>
|
||||
% else:
|
||||
<h4 class="title">${_("Pursue Academic Credit with a Verified Certificate")}</h4>
|
||||
@@ -165,12 +165,16 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
|
||||
<p>
|
||||
<div class="wrapper-copy-inline">
|
||||
<div class="copy-inline">
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4>${_("Benefits of the Verified Track")}</h4>
|
||||
<ul>
|
||||
<li>${Text(_("{b_start}Eligible for credit:{b_end} Receive academic credit after successfully completing the course")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% if course_duration_limit_enabled:
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
% if content_gating_enabled:
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
<li>${Text(_("{b_start}Easily Sharable: {b_end}Add the certificate to your CV or resume, or post it directly on LinkedIn.")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Support our Mission: {b_end}EdX, a non-profit, relies on verified certificates to help fund affordable education to everyone globally.")).format(**b_tag_kwargs)}</li>
|
||||
</ul>
|
||||
@@ -195,7 +199,7 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
|
||||
</p>
|
||||
</div>
|
||||
% else:
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4 class="title">${_("Pursue the Verified Track")}</h4>
|
||||
% else:
|
||||
<h4 class="title">${_("Pursue a Verified Certificate")}</h4>
|
||||
@@ -205,11 +209,15 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
|
||||
<p>
|
||||
<div class="wrapper-copy-inline">
|
||||
<div class="copy-inline">
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<h4>${_("Benefits of the Verified Track")}</h4>
|
||||
<ul>
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% if course_duration_limit_enabled:
|
||||
<li>${Text(_("{b_start}Unlimited Course Access: {b_end}Learn at your own pace, and access materials anytime to brush up on what you've learned.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
% if content_gating_enabled:
|
||||
<li>${Text(_("{b_start}Graded Assignments: {b_end}Build your skills through graded assignments and projects.")).format(**b_tag_kwargs)}</li>
|
||||
% endif
|
||||
<li>${Text(_("{b_start}Easily Sharable: {b_end}Add the certificate to your CV or resume, or post it directly on LinkedIn.")).format(**b_tag_kwargs)}</li>
|
||||
<li>${Text(_("{b_start}Support our Mission: {b_end}EdX, a non-profit, relies on verified certificates to help fund affordable education to everyone globally.")).format(**b_tag_kwargs)}</li>
|
||||
</ul>
|
||||
@@ -229,7 +237,7 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
|
||||
<ul class="list-actions">
|
||||
<li class="action action-select">
|
||||
<input type="hidden" name="contribution" value="${min_price}" />
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled or course_duration_limit_enabled:
|
||||
<input type="submit" name="verified_mode" value="${_('Pursue the Verified Track')} ($${min_price} USD)" />
|
||||
% else:
|
||||
<input type="submit" name="verified_mode" value="${_('Pursue a Verified Certificate')} ($${min_price} USD)" />
|
||||
@@ -315,8 +323,12 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
|
||||
<h4 class="title">${_("Audit This Course (No Certificate)")}</h4>
|
||||
<div class="copy">
|
||||
## Translators: b_start notes the beginning of a section of text bolded for emphasis, and b_end marks the end of the bolded text.
|
||||
% if content_gating_enabled:
|
||||
% if content_gating_enabled and course_duration_limit_enabled:
|
||||
<p>${Text(_("Audit this course for free and have access to course materials and discussions forums. {b_start}This track does not include graded assignments, or unlimited course access.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% elif content_gating_enabled and not course_duration_limit_enabled:
|
||||
<p>${Text(_("Audit this course for free and have access to course materials and discussions forums. {b_start}This track does not include graded assignments.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% elif not content_gating_enabled and course_duration_limit_enabled:
|
||||
<p>${Text(_("Audit this course for free and have access to course materials and discussions forums. {b_start}This track does not include unlimited course access.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% else:
|
||||
<p>${Text(_("Audit this course for free and have complete access to all the course material, activities, tests, and forums. {b_start}Please note that this track does not offer a certificate for learners who earn a passing grade.{b_end}")).format(**b_tag_kwargs)}</p>
|
||||
% endif
|
||||
|
||||
Reference in New Issue
Block a user