Merge pull request #28320 from edx/AA-902
feat: update course goals with new fields and a waffle flag to toggle the new behavior
This commit is contained in:
@@ -3,7 +3,6 @@ Course Goals Python API
|
||||
"""
|
||||
|
||||
|
||||
from django.conf import settings
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
@@ -12,10 +11,12 @@ from lms.djangoapps.course_goals.models import CourseGoal, GOAL_KEY_CHOICES
|
||||
from openedx.features.course_experience import ENABLE_COURSE_GOALS
|
||||
|
||||
|
||||
def add_course_goal(user, course_id, goal_key):
|
||||
def add_course_goal_deprecated(user, course_id, goal_key):
|
||||
"""
|
||||
Add a new course goal for the provided user and course. If the goal
|
||||
already exists, simply update and save the goal.
|
||||
This method is for the deprecated version of course goals and will be removed as soon
|
||||
as the newer number of days version of course goals is fully implemented.
|
||||
|
||||
Arguments:
|
||||
user: The user that is setting the goal
|
||||
@@ -28,6 +29,27 @@ def add_course_goal(user, course_id, goal_key):
|
||||
)
|
||||
|
||||
|
||||
def add_course_goal(user, course_id, days_per_week,
|
||||
subscribed_to_reminders):
|
||||
"""
|
||||
Add a new course goal for the provided user and course. If the goal
|
||||
already exists, simply update and save the goal.
|
||||
|
||||
Arguments:
|
||||
user: The user that is setting the goal
|
||||
course_id (string): The id for the course the goal refers to
|
||||
days_per_week (int): number of days learner wants to learn per week
|
||||
subscribed_to_reminders (bool): whether the learner wants to receive email reminders about their goal
|
||||
"""
|
||||
course_key = CourseKey.from_string(str(course_id))
|
||||
CourseGoal.objects.update_or_create(
|
||||
user=user, course_key=course_key, defaults={
|
||||
'days_per_week': days_per_week,
|
||||
'subscribed_to_reminders': subscribed_to_reminders,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_course_goal(user, course_key):
|
||||
"""
|
||||
Given a user and a course_key, return their course goal.
|
||||
@@ -56,8 +78,7 @@ def has_course_goal_permission(request, course_id, user_access):
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
has_verified_mode = CourseMode.has_verified_mode(CourseMode.modes_for_course_dict(course_key))
|
||||
return user_access['is_enrolled'] and has_verified_mode and ENABLE_COURSE_GOALS.is_enabled(course_key) \
|
||||
and settings.FEATURES.get('ENABLE_COURSE_GOALS')
|
||||
return user_access['is_enrolled'] and has_verified_mode and ENABLE_COURSE_GOALS.is_enabled(course_key)
|
||||
|
||||
|
||||
def get_course_goal_options():
|
||||
|
||||
@@ -18,6 +18,8 @@ def emit_course_goal_event(sender, instance, **kwargs): # lint-amnesty, pylint:
|
||||
properties = {
|
||||
'courserun_key': str(instance.course_key),
|
||||
'goal_key': instance.goal_key,
|
||||
'days_per_week': instance.days_per_week,
|
||||
'subscribed_to_reminders': instance.subscribed_to_reminders,
|
||||
}
|
||||
tracker.emit(name, properties)
|
||||
segment.track(instance.user.id, name, properties)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.2.24 on 2021-08-06 01:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('course_goals', '0003_historicalcoursegoal'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='coursegoal',
|
||||
name='days_per_week',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='coursegoal',
|
||||
name='subscribed_to_reminders',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalcoursegoal',
|
||||
name='days_per_week',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalcoursegoal',
|
||||
name='subscribed_to_reminders',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -33,12 +33,16 @@ class CourseGoal(models.Model):
|
||||
|
||||
user = models.ForeignKey(User, blank=False, on_delete=models.CASCADE)
|
||||
course_key = CourseKeyField(max_length=255, db_index=True)
|
||||
# The goal a user has set for the number of days they want to learn per week
|
||||
days_per_week = models.PositiveIntegerField(default=0)
|
||||
# Controls whether a user will receive emails reminding them to stay on track with their learning goal
|
||||
subscribed_to_reminders = models.BooleanField(default=False)
|
||||
goal_key = models.CharField(max_length=100, choices=GOAL_KEY_CHOICES, default=GOAL_KEY_CHOICES.unsure)
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __str__(self):
|
||||
return 'CourseGoal: {user} set goal to {goal} for course {course}'.format(
|
||||
return 'CourseGoal: {user} set goal to {goal} days per week for course {course}'.format(
|
||||
user=self.user.username,
|
||||
goal=self.goal_key,
|
||||
goal=self.days_per_week,
|
||||
course=self.course_key,
|
||||
)
|
||||
|
||||
@@ -46,7 +46,10 @@ class TestCourseGoalsAPI(SharedModuleStoreTestCase):
|
||||
""" Ensures a correctly formatted post succeeds."""
|
||||
response = self.post_course_goal(valid=True, goal_key='certify')
|
||||
segment_call.assert_called_once_with(self.user.id, EVENT_NAME_ADDED, {
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'certify'
|
||||
'courserun_key': str(self.course.id),
|
||||
'goal_key': 'certify',
|
||||
'days_per_week': 0,
|
||||
'subscribed_to_reminders': False,
|
||||
})
|
||||
assert response.status_code == 201
|
||||
|
||||
@@ -79,13 +82,19 @@ class TestCourseGoalsAPI(SharedModuleStoreTestCase):
|
||||
self.post_course_goal(valid=True, goal_key='unsure')
|
||||
|
||||
segment_call.assert_any_call(self.user.id, EVENT_NAME_ADDED, {
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'explore'
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'explore',
|
||||
'days_per_week': 0,
|
||||
'subscribed_to_reminders': False,
|
||||
})
|
||||
segment_call.assert_any_call(self.user.id, EVENT_NAME_UPDATED, {
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'certify'
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'certify',
|
||||
'days_per_week': 0,
|
||||
'subscribed_to_reminders': False,
|
||||
})
|
||||
segment_call.assert_any_call(self.user.id, EVENT_NAME_UPDATED, {
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'unsure'
|
||||
'courserun_key': str(self.course.id), 'goal_key': 'unsure',
|
||||
'days_per_week': 0,
|
||||
'subscribed_to_reminders': False,
|
||||
})
|
||||
current_goals = CourseGoal.objects.filter(user=self.user, course_key=self.course.id)
|
||||
assert len(current_goals) == 1
|
||||
|
||||
128
lms/djangoapps/course_goals/tests/test_goals.py
Normal file
128
lms/djangoapps/course_goals/tests/test_goals.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
Unit tests for course_goals djangoapp
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
from lms.djangoapps.course_goals.models import CourseGoal
|
||||
from lms.djangoapps.course_goals.toggles import COURSE_GOALS_NUMBER_OF_DAYS_GOALS
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from openedx.features.course_experience import ENABLE_COURSE_GOALS
|
||||
|
||||
EVENT_NAME_ADDED = 'edx.course.goal.added'
|
||||
EVENT_NAME_UPDATED = 'edx.course.goal.updated'
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
@override_waffle_flag(ENABLE_COURSE_GOALS, active=True)
|
||||
@override_waffle_flag(COURSE_GOALS_NUMBER_OF_DAYS_GOALS, active=True)
|
||||
class TestCourseGoalsAPI(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Testing the Course Goals API.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.course = CourseFactory.create(emit_signals=True)
|
||||
|
||||
self.user = User.objects.create_user('john', 'lennon@thebeatles.com', 'password')
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
|
||||
self.client = APIClient(enforce_csrf_checks=True)
|
||||
self.client.login(username=self.user.username, password=self.user.password)
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
self.apiUrl = reverse('course-home-save-course-goal')
|
||||
|
||||
def save_course_goal(self, number, subscribed):
|
||||
"""
|
||||
Sends a post request to set a course goal and returns the response.
|
||||
"""
|
||||
post_data = {
|
||||
'course_id': str(self.course.id),
|
||||
'user': self.user.username,
|
||||
}
|
||||
if number is not None:
|
||||
post_data['days_per_week'] = number
|
||||
if subscribed is not None:
|
||||
post_data['subscribed_to_reminders'] = subscribed
|
||||
|
||||
response = self.client.post(self.apiUrl, json.dumps(post_data), content_type='application/json')
|
||||
return response
|
||||
|
||||
@mock.patch('lms.djangoapps.course_goals.handlers.segment.track')
|
||||
@override_settings(LMS_SEGMENT_KEY="foobar")
|
||||
def test_add_goal(self, segment_call):
|
||||
""" Ensures a correctly formatted post succeeds."""
|
||||
self.save_course_goal(1, True)
|
||||
segment_call.assert_called_once_with(self.user.id, EVENT_NAME_ADDED, {
|
||||
'courserun_key': str(self.course.id),
|
||||
'days_per_week': 1,
|
||||
'subscribed_to_reminders': True,
|
||||
'goal_key': 'unsure',
|
||||
})
|
||||
|
||||
current_goals = CourseGoal.objects.filter(user=self.user, course_key=self.course.id)
|
||||
assert len(current_goals) == 1
|
||||
assert current_goals[0].days_per_week == 1
|
||||
assert current_goals[0].subscribed_to_reminders is True
|
||||
|
||||
@mock.patch('lms.djangoapps.course_goals.handlers.segment.track')
|
||||
@override_settings(LMS_SEGMENT_KEY="foobar")
|
||||
def test_update_goal(self, segment_call):
|
||||
""" Ensures that repeatedly saving a course goal does not create new instances of the goal. """
|
||||
self.save_course_goal(1, True)
|
||||
segment_call.assert_called_with(self.user.id, EVENT_NAME_ADDED, {
|
||||
'courserun_key': str(self.course.id),
|
||||
'days_per_week': 1,
|
||||
'subscribed_to_reminders': True,
|
||||
'goal_key': 'unsure',
|
||||
})
|
||||
|
||||
self.save_course_goal(3, True)
|
||||
segment_call.assert_called_with(self.user.id, EVENT_NAME_UPDATED, {
|
||||
'courserun_key': str(self.course.id),
|
||||
'days_per_week': 3,
|
||||
'subscribed_to_reminders': True,
|
||||
'goal_key': 'unsure',
|
||||
})
|
||||
|
||||
self.save_course_goal(5, False)
|
||||
segment_call.assert_called_with(self.user.id, EVENT_NAME_UPDATED, {
|
||||
'courserun_key': str(self.course.id),
|
||||
'days_per_week': 5,
|
||||
'subscribed_to_reminders': False,
|
||||
'goal_key': 'unsure',
|
||||
})
|
||||
|
||||
current_goals = CourseGoal.objects.filter(user=self.user, course_key=self.course.id)
|
||||
assert len(current_goals) == 1
|
||||
assert current_goals[0].days_per_week == 5
|
||||
assert current_goals[0].subscribed_to_reminders is False
|
||||
|
||||
def test_add_without_required_arguments(self):
|
||||
""" Ensures if required arguments are not provided, post does not succeed. """
|
||||
response = self.save_course_goal(None, None)
|
||||
assert len(CourseGoal.objects.filter(user=self.user, course_key=self.course.id)) == 0
|
||||
self.assertContains(
|
||||
response=response,
|
||||
text="'days_per_week' and 'subscribed_to_reminders' are required.",
|
||||
status_code=400
|
||||
)
|
||||
|
||||
def test_add_invalid_goal(self):
|
||||
""" Ensures an incorrectly formatted post does not succeed. """
|
||||
response = self.save_course_goal('notnumber', False)
|
||||
assert response.status_code == 400
|
||||
assert len(CourseGoal.objects.filter(user=self.user, course_key=self.course.id)) == 0
|
||||
21
lms/djangoapps/course_goals/toggles.py
Normal file
21
lms/djangoapps/course_goals/toggles.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Toggles for course goals
|
||||
"""
|
||||
|
||||
from edx_toggles.toggles import LegacyWaffleFlagNamespace
|
||||
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
|
||||
WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='course_goals')
|
||||
|
||||
# .. toggle_name: course_goals.number_of_days_goals
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: This flag enables the use of the new version of course goals where users
|
||||
# .. set a goal for the number of days they want to learn
|
||||
# .. toggle_warnings: None
|
||||
# .. toggle_use_cases: temporary
|
||||
# .. toggle_creation_date: 2021-07-27
|
||||
# .. toggle_target_removal_date: 2021-09-01
|
||||
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-859
|
||||
COURSE_GOALS_NUMBER_OF_DAYS_GOALS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'number_of_days_goals', __name__)
|
||||
@@ -53,6 +53,7 @@ class CourseGoalViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = CourseGoalSerializer
|
||||
|
||||
# Another version of this endpoint exists in ../course_home_api/outline/v1/views.py
|
||||
# This version is used by the legacy frontend and is deprecated
|
||||
def create(self, post_data): # lint-amnesty, pylint: disable=arguments-differ
|
||||
""" Create a new goal if one does not exist, otherwise update the existing goal. """
|
||||
# Ensure goal_key is valid
|
||||
|
||||
@@ -66,7 +66,7 @@ class CourseGoalsSerializer(serializers.Serializer):
|
||||
"""
|
||||
Serializer for Course Goal data
|
||||
"""
|
||||
goal_options = serializers.ListField()
|
||||
goal_options = serializers.ListField(default=[])
|
||||
selected_goal = serializers.DictField()
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import ddt
|
||||
import json
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
@@ -18,6 +19,7 @@ from common.djangoapps.student.roles import CourseInstructorRole
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
|
||||
from lms.djangoapps.course_home_api.toggles import COURSE_HOME_USE_LEGACY_FRONTEND
|
||||
from lms.djangoapps.course_goals.toggles import COURSE_GOALS_NUMBER_OF_DAYS_GOALS
|
||||
from openedx.core.djangoapps.content.learning_sequences.api import replace_course_outline
|
||||
from openedx.core.djangoapps.content.learning_sequences.data import CourseOutlineData, CourseVisibility
|
||||
from openedx.core.djangoapps.content.learning_sequences.toggles import USE_FOR_OUTLINES
|
||||
@@ -219,7 +221,7 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
assert response.data['access_expiration']['expiration_date'] == deadline
|
||||
|
||||
@override_waffle_flag(ENABLE_COURSE_GOALS, active=True)
|
||||
def test_post_course_goal(self):
|
||||
def test_post_course_goal_deprecated(self):
|
||||
CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)
|
||||
|
||||
post_data = {
|
||||
@@ -237,6 +239,37 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
assert selected_goal is not None
|
||||
assert selected_goal['key'] == 'certify'
|
||||
|
||||
@override_waffle_flag(ENABLE_COURSE_GOALS, active=True)
|
||||
@override_waffle_flag(COURSE_GOALS_NUMBER_OF_DAYS_GOALS, active=True)
|
||||
def test_post_course_goal(self):
|
||||
""" Test that the api returns the correct response when saving a goal """
|
||||
CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)
|
||||
|
||||
post_data = json.dumps({
|
||||
'course_id': str(self.course.id),
|
||||
'days_per_week': 1,
|
||||
'subscribed_to_reminders': True,
|
||||
})
|
||||
post_course_goal_response = self.client.post(
|
||||
reverse('course-home-save-course-goal'),
|
||||
post_data,
|
||||
content_type='application/json',
|
||||
)
|
||||
assert post_course_goal_response.status_code == 200
|
||||
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
|
||||
course_goals = response.json()['course_goals']
|
||||
expected_course_goals = {
|
||||
'goal_options': [],
|
||||
'selected_goal': {
|
||||
'days_per_week': 1,
|
||||
'subscribed_to_reminders': True
|
||||
}
|
||||
}
|
||||
assert course_goals == expected_course_goals
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_SPECIAL_EXAMS': True})
|
||||
@patch('lms.djangoapps.course_api.blocks.transformers.milestones.get_attempt_status_summary')
|
||||
def test_proctored_exam(self, mock_summary):
|
||||
|
||||
@@ -6,6 +6,7 @@ from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory
|
||||
|
||||
from completion.exceptions import UnavailableCompletionData
|
||||
from completion.utilities import get_key_to_last_completed_block
|
||||
from django.conf import settings
|
||||
from django.http.response import Http404
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
@@ -21,14 +22,17 @@ from rest_framework.response import Response
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.util.json_request import expect_json
|
||||
from common.djangoapps.util.views import expose_header
|
||||
from lms.djangoapps.course_goals.api import (
|
||||
add_course_goal,
|
||||
add_course_goal_deprecated,
|
||||
get_course_goal,
|
||||
get_course_goal_text,
|
||||
has_course_goal_permission,
|
||||
valid_course_goals_ordered
|
||||
)
|
||||
from lms.djangoapps.course_goals.toggles import COURSE_GOALS_NUMBER_OF_DAYS_GOALS
|
||||
from lms.djangoapps.course_home_api.outline.v1.serializers import OutlineTabSerializer
|
||||
from lms.djangoapps.course_home_api.toggles import (
|
||||
course_home_legacy_is_active,
|
||||
@@ -46,7 +50,7 @@ from openedx.core.djangoapps.content.learning_sequences.api import (
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
|
||||
from openedx.features.course_duration_limits.access import get_access_expiration_data
|
||||
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG
|
||||
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, ENABLE_COURSE_GOALS
|
||||
from openedx.features.course_experience.course_tools import CourseToolsPluginManager
|
||||
from openedx.features.course_experience.course_updates import (
|
||||
dismiss_current_update_for_user,
|
||||
@@ -105,10 +109,9 @@ class OutlineTabView(RetrieveAPIView):
|
||||
resume_block: (bool) Whether the block is the resume block
|
||||
has_scheduled_content: (bool) Whether the block has more content scheduled for the future
|
||||
course_goals:
|
||||
goal_options: (list) A list of goals where each goal is represented as a tuple (goal_key, goal_string)
|
||||
selected_goal:
|
||||
key: (str) The unique id given to the user's selected goal.
|
||||
text: (str) The display text for the user's selected goal.
|
||||
days_per_week: (int) The number of days the learner wants to learn per week
|
||||
subscribed_to_reminders: (bool) Whether the learner wants email reminders about their goal
|
||||
course_tools: List of serialized Course Tool objects. Each serialization has the following fields:
|
||||
analytics_id: (str) The unique id given to the tool.
|
||||
title: (str) The display title of the tool.
|
||||
@@ -246,23 +249,37 @@ class OutlineTabView(RetrieveAPIView):
|
||||
access_expiration = get_access_expiration_data(request.user, course_overview)
|
||||
cert_data = get_cert_data(request.user, course, enrollment.mode) if is_enrolled else None
|
||||
|
||||
# Only show the set course goal message for enrolled, unverified
|
||||
# users in a course that allows for verified statuses.
|
||||
is_already_verified = CourseEnrollment.is_enrolled_as_verified(request.user, course_key)
|
||||
if not is_already_verified and has_course_goal_permission(request, course_key_string,
|
||||
{'is_enrolled': is_enrolled}):
|
||||
course_goals = {
|
||||
'goal_options': valid_course_goals_ordered(include_unsure=True),
|
||||
'selected_goal': None
|
||||
}
|
||||
if COURSE_GOALS_NUMBER_OF_DAYS_GOALS.is_enabled():
|
||||
if (is_enrolled and ENABLE_COURSE_GOALS.is_enabled(course_key)):
|
||||
|
||||
selected_goal = get_course_goal(request.user, course_key)
|
||||
if selected_goal:
|
||||
course_goals['selected_goal'] = {
|
||||
'key': selected_goal.goal_key,
|
||||
'text': get_course_goal_text(selected_goal.goal_key),
|
||||
course_goals = {
|
||||
'selected_goal': None
|
||||
}
|
||||
|
||||
selected_goal = get_course_goal(request.user, course_key)
|
||||
if selected_goal:
|
||||
course_goals['selected_goal'] = {
|
||||
'days_per_week': selected_goal.days_per_week,
|
||||
'subscribed_to_reminders': selected_goal.subscribed_to_reminders,
|
||||
}
|
||||
else:
|
||||
# Only show the set course goal message for enrolled, unverified
|
||||
# users in a course that allows for verified statuses.
|
||||
is_already_verified = CourseEnrollment.is_enrolled_as_verified(request.user, course_key)
|
||||
if not is_already_verified and has_course_goal_permission(request, course_key_string,
|
||||
{'is_enrolled': is_enrolled}):
|
||||
course_goals = {
|
||||
'goal_options': valid_course_goals_ordered(include_unsure=True),
|
||||
'selected_goal': None
|
||||
}
|
||||
|
||||
selected_goal = get_course_goal(request.user, course_key)
|
||||
if selected_goal:
|
||||
course_goals['selected_goal'] = {
|
||||
'key': selected_goal.goal_key,
|
||||
'text': get_course_goal_text(selected_goal.goal_key),
|
||||
}
|
||||
|
||||
try:
|
||||
resume_block = get_key_to_last_completed_block(request.user, course.id)
|
||||
resume_course['has_visited_course'] = True
|
||||
@@ -390,22 +407,39 @@ def dismiss_welcome_message(request):
|
||||
@authentication_classes((JwtAuthentication, SessionAuthenticationAllowInactiveUser,))
|
||||
@permission_classes((IsAuthenticated,))
|
||||
def save_course_goal(request):
|
||||
course_id = request.data.get('course_id', None)
|
||||
goal_key = request.data.get('goal_key', None)
|
||||
course_id = request.data.get('course_id')
|
||||
goal_key = request.data.get('goal_key')
|
||||
days_per_week = request.data.get('days_per_week')
|
||||
subscribed_to_reminders = request.data.get('subscribed_to_reminders')
|
||||
|
||||
# If body doesn't contain 'course_id', return 400 to client.
|
||||
if not course_id:
|
||||
raise ParseError(_("'course_id' is required."))
|
||||
raise ParseError("'course_id' is required.")
|
||||
|
||||
# If body doesn't contain 'goal', return 400 to client.
|
||||
if not goal_key:
|
||||
raise ParseError(_("'goal_key' is required."))
|
||||
if COURSE_GOALS_NUMBER_OF_DAYS_GOALS.is_enabled():
|
||||
# If body doesn't contain the required goals fields, return 400 to client.
|
||||
if days_per_week is None or subscribed_to_reminders is None:
|
||||
raise ParseError("'days_per_week' and 'subscribed_to_reminders' are required.")
|
||||
|
||||
try:
|
||||
add_course_goal(request.user, course_id, goal_key)
|
||||
return Response({
|
||||
'header': _('Your course goal has been successfully set.'),
|
||||
'message': _('Course goal updated successfully.'),
|
||||
})
|
||||
except Exception:
|
||||
raise UnableToSaveCourseGoal
|
||||
try:
|
||||
add_course_goal(request.user, course_id, days_per_week, subscribed_to_reminders)
|
||||
return Response({
|
||||
'header': _('Your course goal has been successfully set.'),
|
||||
'message': _('Course goal updated successfully.'),
|
||||
})
|
||||
except Exception:
|
||||
raise UnableToSaveCourseGoal
|
||||
|
||||
else:
|
||||
# If body doesn't contain 'goal', return 400 to client.
|
||||
if not goal_key:
|
||||
raise ParseError("'goal_key' is required.")
|
||||
|
||||
try:
|
||||
add_course_goal_deprecated(request.user, course_id, goal_key)
|
||||
return Response({
|
||||
'header': _('Your course goal has been successfully set.'),
|
||||
'message': _('Course goal updated successfully.'),
|
||||
})
|
||||
except Exception:
|
||||
raise UnableToSaveCourseGoal
|
||||
|
||||
@@ -801,9 +801,6 @@ FEATURES = {
|
||||
# .. toggle_tickets: https://github.com/edx/edx-platform/pull/15006
|
||||
'ENABLE_BULK_ENROLLMENT_VIEW': False,
|
||||
|
||||
# Whether course goals is enabled.
|
||||
'ENABLE_COURSE_GOALS': True,
|
||||
|
||||
# Set to enable Enterprise integration
|
||||
'ENABLE_ENTERPRISE_INTEGRATION': False,
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from common.djangoapps.student.tests.factories import OrgStaffFactory
|
||||
from common.djangoapps.student.tests.factories import StaffFactory
|
||||
from lms.djangoapps.commerce.models import CommerceConfiguration
|
||||
from lms.djangoapps.commerce.utils import EcommerceService
|
||||
from lms.djangoapps.course_goals.api import add_course_goal, get_course_goal
|
||||
from lms.djangoapps.course_goals.api import add_course_goal_deprecated, get_course_goal
|
||||
from lms.djangoapps.course_home_api.toggles import COURSE_HOME_USE_LEGACY_FRONTEND
|
||||
from lms.djangoapps.courseware.tests.helpers import get_expiration_banner_text
|
||||
from lms.djangoapps.discussion.django_comment_client.tests.factories import RoleFactory
|
||||
@@ -755,7 +755,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
self.assertContains(response, TEST_COURSE_GOAL_OPTIONS)
|
||||
|
||||
# Verify that enrolled users that have set a course goal are not shown the set course goal message.
|
||||
add_course_goal(user, verifiable_course.id, COURSE_GOAL_DISMISS_OPTION)
|
||||
add_course_goal_deprecated(user, verifiable_course.id, COURSE_GOAL_DISMISS_OPTION)
|
||||
response = self.client.get(course_home_url(verifiable_course))
|
||||
self.assertNotContains(response, TEST_COURSE_GOAL_OPTIONS)
|
||||
|
||||
@@ -797,7 +797,7 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
self.assertContains(response, TEST_COURSE_GOAL_UPDATE_FIELD_HIDDEN)
|
||||
|
||||
# Verify that enrolled users that have set a course goal are shown a visible update goal selection field.
|
||||
add_course_goal(user, verifiable_course.id, COURSE_GOAL_DISMISS_OPTION)
|
||||
add_course_goal_deprecated(user, verifiable_course.id, COURSE_GOAL_DISMISS_OPTION)
|
||||
response = self.client.get(course_home_url(verifiable_course))
|
||||
self.assertContains(response, TEST_COURSE_GOAL_UPDATE_FIELD)
|
||||
self.assertNotContains(response, TEST_COURSE_GOAL_UPDATE_FIELD_HIDDEN)
|
||||
|
||||
Reference in New Issue
Block a user