diff --git a/lms/djangoapps/course_home_api/progress/v1/serializers.py b/lms/djangoapps/course_home_api/progress/v1/serializers.py index 977a6b34dd..13586fc0fc 100644 --- a/lms/djangoapps/course_home_api/progress/v1/serializers.py +++ b/lms/djangoapps/course_home_api/progress/v1/serializers.py @@ -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) diff --git a/lms/djangoapps/course_home_api/progress/v1/tests/test_views.py b/lms/djangoapps/course_home_api/progress/v1/tests/test_views.py index 33145249a7..9100299826 100644 --- a/lms/djangoapps/course_home_api/progress/v1/tests/test_views.py +++ b/lms/djangoapps/course_home_api/progress/v1/tests/test_views.py @@ -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 diff --git a/lms/djangoapps/course_home_api/progress/v1/views.py b/lms/djangoapps/course_home_api/progress/v1/views.py index a1b6985d7c..e8e2e31c19 100644 --- a/lms/djangoapps/course_home_api/progress/v1/views.py +++ b/lms/djangoapps/course_home_api/progress/v1/views.py @@ -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: diff --git a/lms/djangoapps/grades/subsection_grade.py b/lms/djangoapps/grades/subsection_grade.py index 72b3f4e7aa..70d7fbfaa5 100644 --- a/lms/djangoapps/grades/subsection_grade.py +++ b/lms/djangoapps/grades/subsection_grade.py @@ -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) diff --git a/lms/templates/courseware/progress.html b/lms/templates/courseware/progress.html index 037a36b3e1..63b340c1e4 100644 --- a/lms/templates/courseware/progress.html +++ b/lms/templates/courseware/progress.html @@ -3,16 +3,18 @@ <%namespace name='static' file='/static_content.html'/> <%def name="online_help_token()"><% return "progress" %> <%! +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:
- <% post_url = reverse('generate_user_cert', args=[text_type(course.id)]) %> + <% post_url = reverse('generate_user_cert', args=[str(course.id)]) %>

${_(certificate_data.title)}

${_(certificate_data.msg)}

@@ -169,20 +171,33 @@ username = get_enterprise_learner_generic_name(request) or student.username %for section in chapter['sections']:
<% + 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 "" %>

- - ${ section.display_name} + %if hide_url: +

${section.display_name} %if (total > 0 or earned > 0) and section.show_grades(staff_access): - - ${_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total)))} - - %endif - + + ${_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total)))} + + %endif +

+ %else: + + ${ section.display_name} + %if (total > 0 or earned > 0) and section.show_grades(staff_access): + + ${_("{earned} of {total} possible points").format(earned='{:.3n}'.format(float(earned)), total='{:.3n}'.format(float(total)))} + + %endif + + %endif %if (total > 0 or earned > 0) and section.show_grades(staff_access): ${"({0:.3n}/{1:.3n}) {2}".format( float(earned), float(total), percentageString )} %endif diff --git a/openedx/core/djangoapps/content/block_structure/block_structure.py b/openedx/core/djangoapps/content/block_structure/block_structure.py index fe7a42b379..a8d10c4d81 100644 --- a/openedx/core/djangoapps/content/block_structure/block_structure.py +++ b/openedx/core/djangoapps/content/block_structure/block_structure.py @@ -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):