Merge pull request #28233 from edx/ddumesnil/hide-content-after-due-progress-aa-912

fix: AA-912: Hide URL after due date if subsection is marked as hide …
This commit is contained in:
Dillon Dumesnil
2021-07-22 07:17:35 -07:00
committed by GitHub
6 changed files with 70 additions and 18 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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):