fix: AA-912: Hide URL after due date if subsection is marked as hide after due
A bug was reported of a learner accessing content that should have been hidden due to the hide after due setting in Studio by the course team by clicking on the Progress tab. This takes into account that value and will now hide the URL on the Progress tab in that scenario.
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
"""
|
||||
Progress Tab Serializers
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.reverse import reverse
|
||||
from pytz import UTC
|
||||
|
||||
from lms.djangoapps.course_home_api.mixins import VerifiedModeSerializerMixin
|
||||
|
||||
|
||||
@@ -20,8 +24,8 @@ class SubsectionScoresSerializer(serializers.Serializer):
|
||||
Serializer for subsections in section_scores
|
||||
"""
|
||||
assignment_type = serializers.CharField(source='format')
|
||||
display_name = serializers.CharField()
|
||||
block_key = serializers.SerializerMethodField()
|
||||
display_name = serializers.CharField()
|
||||
has_graded_assignment = serializers.BooleanField(source='graded')
|
||||
learner_has_access = serializers.SerializerMethodField()
|
||||
num_points_earned = serializers.FloatField(source='graded_total.earned')
|
||||
@@ -46,6 +50,15 @@ class SubsectionScoresSerializer(serializers.Serializer):
|
||||
return problem_scores
|
||||
|
||||
def get_url(self, subsection):
|
||||
"""
|
||||
Returns the URL for the subsection while taking into account if the course team has
|
||||
marked the subsection's visibility as hide after due.
|
||||
"""
|
||||
hide_url_date = (subsection.self_paced and subsection.end) or subsection.due
|
||||
if (not self.context['staff_access'] and subsection.hide_after_due and hide_url_date
|
||||
and datetime.now(UTC) > hide_url_date):
|
||||
return None
|
||||
|
||||
relative_path = reverse('jump_to', args=[self.context['course_key'], subsection.location])
|
||||
request = self.context['request']
|
||||
return request.build_absolute_uri(relative_path)
|
||||
|
||||
@@ -208,3 +208,22 @@ class ProgressTabTestViews(BaseCourseHomeTests):
|
||||
|
||||
response = self.client.get(self.url)
|
||||
assert response.data['username'] == other_user.username
|
||||
|
||||
@override_waffle_flag(COURSE_HOME_MICROFRONTEND_PROGRESS_TAB, active=True)
|
||||
def test_url_hidden_if_subsection_hide_after_due(self):
|
||||
chapter = ItemFactory(parent=self.course, category='chapter')
|
||||
yesterday = now() - timedelta(days=1)
|
||||
hide_after_due_subsection = ItemFactory(
|
||||
parent=chapter, category='sequential', hide_after_due=True, due=yesterday
|
||||
)
|
||||
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
|
||||
sections = response.data['section_scores']
|
||||
regular_subsection = sections[0]['subsections'][0] # default sequence that parent class gives us
|
||||
hide_after_due_subsection = sections[1]['subsections'][0]
|
||||
assert regular_subsection['url'] is not None
|
||||
assert hide_after_due_subsection['url'] is None
|
||||
|
||||
@@ -86,7 +86,8 @@ class ProgressTabView(RetrieveAPIView):
|
||||
('always', 'never', 'past_due', values defined in
|
||||
common/lib/xmodule/xmodule/modulestore/inheritance.py)
|
||||
show_grades: (bool) a bool for whether to show grades based on the access the user has
|
||||
url: (str) the absolute path url to the Subsection
|
||||
url: (str or None) the absolute path url to the Subsection or None if the Subsection is no longer accessible
|
||||
to the learner due to a hide_after_due course team setting
|
||||
enrollment_mode: (str) a str representing the enrollment the user has ('audit', 'verified', ...)
|
||||
grading_policy:
|
||||
assignment_policies: List of serialized assignment grading policy objects, each has the following fields:
|
||||
|
||||
@@ -27,9 +27,14 @@ class SubsectionGradeBase(metaclass=ABCMeta):
|
||||
self.display_name = block_metadata_utils.display_name_with_default(subsection)
|
||||
self.url_name = block_metadata_utils.url_name_for_block(subsection)
|
||||
|
||||
self.format = getattr(subsection, 'format', '')
|
||||
self.due = getattr(subsection, 'due', None)
|
||||
self.end = getattr(subsection, 'end', None)
|
||||
self.format = getattr(subsection, 'format', '')
|
||||
self.graded = getattr(subsection, 'graded', False)
|
||||
transformer_data = getattr(subsection, 'transformer_data', None)
|
||||
hidden_content_data = transformer_data and subsection.transformer_data.get('hidden_content')
|
||||
self.hide_after_due = hidden_content_data and hidden_content_data.fields.get('merged_hide_after_due')
|
||||
self.self_paced = subsection.self_paced
|
||||
self.show_correctness = getattr(subsection, 'show_correctness', '')
|
||||
|
||||
self.course_version = getattr(subsection, 'course_version', None)
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
<%namespace name='static' file='/static_content.html'/>
|
||||
<%def name="online_help_token()"><% return "progress" %></%def>
|
||||
<%!
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlquote_plus
|
||||
from django.utils.translation import ugettext as _
|
||||
from pytz import UTC
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from lms.djangoapps.certificates.data import CertificateStatuses
|
||||
from lms.djangoapps.grades.api import constants as grades_constants
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.utils.http import urlquote_plus
|
||||
from six import text_type
|
||||
|
||||
from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name
|
||||
%>
|
||||
|
||||
@@ -73,7 +75,7 @@ username = get_enterprise_learner_generic_name(request) or student.username
|
||||
%if certificate_data:
|
||||
<div class="auto-cert-message" id="course-success">
|
||||
<div class="has-actions">
|
||||
<% post_url = reverse('generate_user_cert', args=[text_type(course.id)]) %>
|
||||
<% post_url = reverse('generate_user_cert', args=[str(course.id)]) %>
|
||||
<div class="msg-content">
|
||||
<h4 class="hd hd-4 title">${_(certificate_data.title)}</h4>
|
||||
<p class="copy">${_(certificate_data.msg)}</p>
|
||||
@@ -169,20 +171,33 @@ username = get_enterprise_learner_generic_name(request) or student.username
|
||||
%for section in chapter['sections']:
|
||||
<div>
|
||||
<%
|
||||
hide_url_date = (section.self_paced and section.end) or section.due
|
||||
hide_url = not staff_access and section.hide_after_due and hide_url_date and datetime.now(UTC) > hide_url_date
|
||||
|
||||
earned = section.graded_total.earned
|
||||
total = section.graded_total.possible
|
||||
|
||||
percentageString = "{0:.0%}".format(section.percent_graded) if total > 0 or earned > 0 else ""
|
||||
%>
|
||||
<h4 class="hd hd-4">
|
||||
<a href="${reverse('courseware_section', kwargs=dict(course_id=text_type(course.id), chapter=chapter['url_name'], section=section.url_name))}">
|
||||
${ section.display_name}
|
||||
%if hide_url:
|
||||
<p class="d-inline">${section.display_name}
|
||||
%if (total > 0 or earned > 0) and section.show_grades(staff_access):
|
||||
<span class="sr">
|
||||
${_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total)))}
|
||||
</span>
|
||||
%endif
|
||||
</a>
|
||||
<span class="sr">
|
||||
${_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total)))}
|
||||
</span>
|
||||
%endif
|
||||
</p>
|
||||
%else:
|
||||
<a href="${reverse('courseware_section', kwargs=dict(course_id=str(course.id), chapter=chapter['url_name'], section=section.url_name))}">
|
||||
${ section.display_name}
|
||||
%if (total > 0 or earned > 0) and section.show_grades(staff_access):
|
||||
<span class="sr">
|
||||
${_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total)))}
|
||||
</span>
|
||||
%endif
|
||||
</a>
|
||||
%endif
|
||||
%if (total > 0 or earned > 0) and section.show_grades(staff_access):
|
||||
<span> ${"({0:.3n}/{1:.3n}) {2}".format( float(earned), float(total), percentageString )}</span>
|
||||
%endif
|
||||
|
||||
@@ -327,7 +327,6 @@ class TransformerData(FieldData):
|
||||
"""
|
||||
Data structure to encapsulate collected data for a transformer.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
class TransformerDataMap(dict):
|
||||
|
||||
Reference in New Issue
Block a user