diff --git a/lms/djangoapps/course_home_api/course_metadata/serializers.py b/lms/djangoapps/course_home_api/course_metadata/serializers.py index e000f0579c9b77028e87020d4ece5cc403f4bbb3..44f43cc507ee68c5a1d10c1b6686882ce7aae421 100644 --- a/lms/djangoapps/course_home_api/course_metadata/serializers.py +++ b/lms/djangoapps/course_home_api/course_metadata/serializers.py @@ -47,3 +47,4 @@ class CourseHomeMetadataSerializer(VerifiedModeSerializer): tabs = CourseTabSerializer(many=True) title = serializers.CharField() username = serializers.CharField() + user_timezone = serializers.CharField() diff --git a/lms/djangoapps/course_home_api/course_metadata/views.py b/lms/djangoapps/course_home_api/course_metadata/views.py index 07b121f830d1aea9f390104697fea7afaae9bc41..58428864997ecd7f28ecb1b02d1ce3f66c312bb0 100644 --- a/lms/djangoapps/course_home_api/course_metadata/views.py +++ b/lms/djangoapps/course_home_api/course_metadata/views.py @@ -15,6 +15,7 @@ from common.djangoapps.student.models import CourseEnrollment from lms.djangoapps.course_api.api import course_detail from lms.djangoapps.course_home_api.course_metadata.serializers import CourseHomeMetadataSerializer from lms.djangoapps.courseware.access import has_access +from lms.djangoapps.courseware.context_processor import user_timezone_locale_prefs from lms.djangoapps.courseware.courses import check_course_access from lms.djangoapps.courseware.masquerade import setup_masquerade from lms.djangoapps.courseware.tabs import get_course_tab_list @@ -52,6 +53,7 @@ class CourseHomeMetadataView(RetrieveAPIView): url: (str) The url to view the tab title: (str) The Course's display title celebrations: (dict) a dict of celebration data + user_timezone: (str) The timezone of the given user **Returns** @@ -103,8 +105,14 @@ class CourseHomeMetadataView(RetrieveAPIView): is_course_staff=original_user_is_staff ) + # User locale settings + user_timezone_locale = user_timezone_locale_prefs(request) + user_timezone = user_timezone_locale['user_timezone'] + browser_timezone = self.request.query_params.get('browser_timezone', None) - celebrations = get_celebrations_dict(request.user, enrollment, course, browser_timezone) + celebrations = get_celebrations_dict( + request.user, enrollment, course, user_timezone if not None else browser_timezone + ) data = { 'course_id': course.id, @@ -121,6 +129,7 @@ class CourseHomeMetadataView(RetrieveAPIView): 'course_access': load_access.to_json(), 'can_load_courseware': can_load_courseware, 'celebrations': celebrations, + 'user_timezone': user_timezone, } context = self.get_serializer_context() context['course'] = course diff --git a/lms/djangoapps/course_home_api/progress/serializers.py b/lms/djangoapps/course_home_api/progress/serializers.py index fd9f97682ae1dc69408772127f8d1d6a03da7b10..af4bcda94f9cff4fb93d2666b21d8ba8eba0cbe6 100644 --- a/lms/djangoapps/course_home_api/progress/serializers.py +++ b/lms/djangoapps/course_home_api/progress/serializers.py @@ -120,15 +120,16 @@ class ProgressTabSerializer(VerifiedModeSerializer): """ Serializer for progress tab """ - username = serializers.CharField() + access_expiration = serializers.DictField() certificate_data = CertificateDataSerializer() completion_summary = serializers.DictField() course_grade = CourseGradeSerializer() end = serializers.DateTimeField() - user_has_passing_grade = serializers.BooleanField() - has_scheduled_content = serializers.BooleanField() - section_scores = SectionScoresSerializer(many=True) enrollment_mode = serializers.CharField() grading_policy = GradingPolicySerializer() + has_scheduled_content = serializers.BooleanField() + section_scores = SectionScoresSerializer(many=True) studio_url = serializers.CharField() + username = serializers.CharField() + user_has_passing_grade = serializers.BooleanField() verification_data = VerificationDataSerializer() diff --git a/lms/djangoapps/course_home_api/progress/views.py b/lms/djangoapps/course_home_api/progress/views.py index 77dabe195624f8c61295f8764ebcbe2d4b012b51..8eee7ba30cb5b5ecb9e9edf6f354f0e52d3c2b5a 100644 --- a/lms/djangoapps/course_home_api/progress/views.py +++ b/lms/djangoapps/course_home_api/progress/views.py @@ -34,6 +34,7 @@ from openedx.core.djangoapps.content.block_structure.api import get_block_struct 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 +from openedx.features.course_duration_limits.access import get_access_expiration_data from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name User = get_user_model() @@ -54,10 +55,11 @@ class ProgressTabView(RetrieveAPIView): Body consists of the following fields: - end: (date) end date of the course - user_has_passing_grade: (bool) boolean on if the user has a passing grade in the course - username: (str) username of the student whose progress information is being displayed. - has_scheduled_content: (bool) boolean on if the course has content scheduled with a release date in the future + access_expiration: An object detailing when access to this course will expire + expiration_date: (str) When the access expires, in ISO 8601 notation + masquerading_expired_course: (bool) Whether this course is expired for the masqueraded user + upgrade_deadline: (str) Last chance to upgrade, in ISO 8601 notation (or None if can't upgrade anymore) + upgrade_url: (str) Upgrade link (or None if can't upgrade anymore) certificate_data: Object containing information about the user's certificate status cert_status: (str) the status of a user's certificate (full list of statuses are defined in lms/djangoapps/certificates/models.py) @@ -72,6 +74,19 @@ class ProgressTabView(RetrieveAPIView): letter_grade: (str) the user's letter grade based on the set grade range. If user is passing, value may be 'A', 'B', 'C', 'D', 'Pass', otherwise none percent: (float) the user's total graded percent in the course + end: (date) end date of the course + 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: + num_droppable: (int) the number of lowest scored assignments that will not be counted towards the final + grade + short_label: (str) the abbreviated name given to the assignment type + type: (str) the assignment type + weight: (float) the percent weight the given assigment type has on the overall grade + grade_range: an object containing the grade range cutoffs. The exact keys in the object can vary, but they + range from just 'Pass', to a combination of 'A', 'B', 'C', and 'D'. If a letter grade is + present, 'Pass' is not included. + has_scheduled_content: (bool) boolean on if the course has content scheduled with a release date in the future section_scores: List of serialized Chapters. Each Chapter has the following fields: display_name: (str) a str of what the name of the Chapter is for displaying on the site subsections: List of serialized Subsections, each has the following fields: @@ -93,18 +108,9 @@ class ProgressTabView(RetrieveAPIView): show_grades: (bool) a bool for whether to show grades based on the access the user has 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: - num_droppable: (int) the number of lowest scored assignments that will not be counted towards the final - grade - short_label: (str) the abbreviated name given to the assignment type - type: (str) the assignment type - weight: (float) the percent weight the given assigment type has on the overall grade - grade_range: an object containing the grade range cutoffs. The exact keys in the object can vary, but they - range from just 'Pass', to a combination of 'A', 'B', 'C', and 'D'. If a letter grade is - present, 'Pass' is not included. studio_url: (str) a str of the link to the grading in studio for the course + user_has_passing_grade: (bool) boolean on if the user has a passing grade in the course + username: (str) username of the student whose progress information is being displayed. verification_data: an object containing link: (str) the link to either start or retry ID verification status: (str) the status of the ID verification @@ -223,18 +229,21 @@ class ProgressTabView(RetrieveAPIView): 'status_date': verification_status['status_date'], } + access_expiration = get_access_expiration_data(request.user, course_overview) + data = { - 'username': username, - 'end': course.end, - 'user_has_passing_grade': user_has_passing_grade, + 'access_expiration': access_expiration, 'certificate_data': get_cert_data(student, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, student), 'course_grade': course_grade, - 'has_scheduled_content': has_scheduled_content, - 'section_scores': list(course_grade.chapter_grades.values()), + 'end': course.end, 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, + 'has_scheduled_content': has_scheduled_content, + 'section_scores': list(course_grade.chapter_grades.values()), 'studio_url': get_studio_url(course, 'settings/grading'), + 'username': username, + 'user_has_passing_grade': user_has_passing_grade, 'verification_data': verification_data, } context = self.get_serializer_context()