feat: add learner_has_access field to progress tab data
In order to allow the learning MFE's progress tab to show a different UX for FBE exceptions (where some exams can still be completed by audit learners), this commit adds access information to each exam. AA-829
This commit is contained in:
@@ -27,10 +27,12 @@ class DateSummarySerializer(serializers.Serializer):
|
||||
first_component_block_id = serializers.SerializerMethodField()
|
||||
|
||||
def get_learner_has_access(self, block):
|
||||
learner_is_full_access = self.context.get('learner_is_full_access', False)
|
||||
block_is_verified = (getattr(block, 'contains_gated_content', False) or
|
||||
isinstance(block, VerificationDeadlineDate))
|
||||
return (not block_is_verified) or learner_is_full_access
|
||||
"""Whether the learner is blocked (gated) from this content or not"""
|
||||
if isinstance(block, VerificationDeadlineDate):
|
||||
# This date block isn't an assignment, so doesn't have contains_gated_content set for it
|
||||
return self.context.get('learner_is_full_access', False)
|
||||
|
||||
return not getattr(block, 'contains_gated_content', False)
|
||||
|
||||
def get_link(self, block):
|
||||
if block.link:
|
||||
|
||||
@@ -17,7 +17,6 @@ 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.experiments.testutils import override_experiment_waffle_flag
|
||||
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
|
||||
@@ -354,7 +353,6 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
response = self.client.get(self.url)
|
||||
assert response.data['verified_mode'] == {'access_expiration_date': (enrollment.created + MIN_DURATION), 'currency': 'USD', 'currency_symbol': '$', 'price': 149, 'sku': 'ABCD1234', 'upgrade_url': '/dashboard'}
|
||||
|
||||
@override_experiment_waffle_flag(COURSE_HOME_MICROFRONTEND, active=True)
|
||||
def test_hide_learning_sequences(self):
|
||||
"""
|
||||
Check that Learning Sequences filters out sequences.
|
||||
|
||||
@@ -23,6 +23,7 @@ class SubsectionScoresSerializer(serializers.Serializer):
|
||||
display_name = serializers.CharField()
|
||||
block_key = serializers.SerializerMethodField()
|
||||
has_graded_assignment = serializers.BooleanField(source='graded')
|
||||
learner_has_access = serializers.SerializerMethodField()
|
||||
num_points_earned = serializers.IntegerField(source='graded_total.earned')
|
||||
num_points_possible = serializers.IntegerField(source='graded_total.possible')
|
||||
percent_graded = serializers.FloatField()
|
||||
@@ -41,6 +42,10 @@ class SubsectionScoresSerializer(serializers.Serializer):
|
||||
def get_show_grades(self, subsection):
|
||||
return subsection.show_grades(self.context['staff_access'])
|
||||
|
||||
def get_learner_has_access(self, subsection):
|
||||
course_blocks = self.context['course_blocks']
|
||||
return not course_blocks.get_xblock_field(subsection.location, 'contains_gated_content', False)
|
||||
|
||||
|
||||
class SectionScoresSerializer(serializers.Serializer):
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,6 @@ Tests for Progress Tab API in the Course Home API
|
||||
|
||||
import dateutil
|
||||
import ddt
|
||||
import mock
|
||||
from datetime import datetime, timedelta
|
||||
from pytz import UTC
|
||||
from unittest.mock import patch
|
||||
@@ -18,16 +17,14 @@ from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
|
||||
from lms.djangoapps.course_home_api.models import DisableProgressPageStackedConfig
|
||||
from lms.djangoapps.course_home_api.toggles import COURSE_HOME_MICROFRONTEND_PROGRESS_TAB
|
||||
from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag
|
||||
from lms.djangoapps.verify_student.models import ManualVerification
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.course_date_signals.utils import MIN_DURATION
|
||||
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
|
||||
from openedx.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
|
||||
from xmodule.modulestore.tests.factories import ItemFactory
|
||||
|
||||
CREDIT_SUPPORT_URL = 'https://support.edx.org/hc/en-us/sections/115004154688-Purchasing-Academic-Credit'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ProgressTabTestViews(BaseCourseHomeTests):
|
||||
@@ -159,3 +156,30 @@ class ProgressTabTestViews(BaseCourseHomeTests):
|
||||
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 404
|
||||
|
||||
@override_waffle_flag(COURSE_HOME_MICROFRONTEND_PROGRESS_TAB, active=True)
|
||||
def test_learner_has_access(self):
|
||||
chapter = ItemFactory(parent=self.course, category='chapter')
|
||||
gated = ItemFactory(parent=chapter, category='sequential')
|
||||
ItemFactory.create(parent=gated, category='problem', graded=True, has_score=True)
|
||||
ungated = ItemFactory(parent=chapter, category='sequential')
|
||||
ItemFactory.create(parent=ungated, category='problem', graded=True, has_score=True,
|
||||
group_access={
|
||||
CONTENT_GATING_PARTITION_ID: [CONTENT_TYPE_GATE_GROUP_IDS['full_access'],
|
||||
CONTENT_TYPE_GATE_GROUP_IDS['limited_access']],
|
||||
})
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
|
||||
sections = response.data['section_scores']
|
||||
ungraded_score = sections[0]['subsections'][0] # default sequence that parent class gives us
|
||||
gated_score = sections[1]['subsections'][0]
|
||||
ungated_score = sections[1]['subsections'][1]
|
||||
assert ungraded_score['learner_has_access']
|
||||
assert not gated_score['learner_has_access']
|
||||
assert ungated_score['learner_has_access']
|
||||
|
||||
@@ -29,6 +29,7 @@ from openedx.core.djangoapps.content.block_structure.transformers import BlockSt
|
||||
from openedx.core.djangoapps.content.block_structure.api import get_block_structure_manager
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
|
||||
from openedx.features.content_type_gating.block_transformers import ContentTypeGateTransformer
|
||||
|
||||
|
||||
class ProgressTabView(RetrieveAPIView):
|
||||
@@ -69,6 +70,7 @@ class ProgressTabView(RetrieveAPIView):
|
||||
block_key: (str) the key of the given subsection block
|
||||
display_name: (str) a str of what the name of the Subsection is for displaying on the site
|
||||
has_graded_assignment: (bool) whether or not the Subsection is a graded assignment
|
||||
learner_has_access: (bool) whether the learner has access to the subsection (could be FBE gated)
|
||||
num_points_earned: (int) the amount of points the user has earned for the given subsection
|
||||
num_points_possible: (int) the total amount of points possible for the given subsection
|
||||
percent_graded: (float) the percentage of total points the user has received a grade for in a given subsection
|
||||
@@ -144,7 +146,7 @@ class ProgressTabView(RetrieveAPIView):
|
||||
|
||||
# Get has_scheduled_content data
|
||||
transformers = BlockStructureTransformers()
|
||||
transformers += [start_date.StartDateTransformer()]
|
||||
transformers += [start_date.StartDateTransformer(), ContentTypeGateTransformer()]
|
||||
usage_key = collected_block_structure.root_block_usage_key
|
||||
course_blocks = get_course_blocks(
|
||||
request.user,
|
||||
@@ -190,6 +192,7 @@ class ProgressTabView(RetrieveAPIView):
|
||||
}
|
||||
context = self.get_serializer_context()
|
||||
context['staff_access'] = is_staff
|
||||
context['course_blocks'] = course_blocks
|
||||
context['course_key'] = course_key
|
||||
# course_overview and enrollment will be used by VerifiedModeSerializerMixin
|
||||
context['course_overview'] = course_overview
|
||||
|
||||
@@ -2790,9 +2790,14 @@ YOUTUBE_API_KEY = 'PUT_YOUR_API_KEY_HERE'
|
||||
|
||||
################################### APPS ######################################
|
||||
|
||||
# The order of INSTALLED_APPS is important, when adding new apps here
|
||||
# remember to check that you are not creating new
|
||||
# The order of INSTALLED_APPS is important, when adding new apps here remember to check that you are not creating new
|
||||
# RemovedInDjango19Warnings in the test logs.
|
||||
#
|
||||
# If you want to add a new djangoapp that isn't suitable for everyone, you have some options:
|
||||
# - Add it to OPTIONAL_APPS below (registered if importable)
|
||||
# - Add it to the ADDL_INSTALLED_APPS configuration variable (acts like EXTRA_APPS in other IDAs)
|
||||
# - Make it a plugin (which are auto-registered) and add it to the EDXAPP_PRIVATE_REQUIREMENTS configuration variable
|
||||
# (See https://github.com/edx/edx-django-utils/tree/master/edx_django_utils/plugins)
|
||||
INSTALLED_APPS = [
|
||||
# Standard ones that are always installed...
|
||||
'django.contrib.auth',
|
||||
|
||||
Reference in New Issue
Block a user