Merge pull request #24650 from edx/dlichen/AA-211

Dlichen/AA-211
This commit is contained in:
daphneli-chen
2020-08-04 13:11:43 -04:00
committed by GitHub
5 changed files with 54 additions and 27 deletions

View File

@@ -2,8 +2,8 @@
Progress Tab Serializers
"""
from rest_framework import serializers
from lms.djangoapps.course_home_api.outline.v1.serializers import CourseBlockSerializer
from rest_framework.reverse import reverse
from lms.djangoapps.certificates.models import CertificateStatuses
class GradedTotalSerializer(serializers.Serializer):
@@ -51,11 +51,41 @@ class ChapterSerializer(serializers.Serializer):
subsections = SubsectionSerializer(source='sections', many=True)
class CertificateDataSerializer(serializers.Serializer):
cert_web_view_url = serializers.CharField()
download_url = serializers.CharField()
is_downloadable = serializers.SerializerMethodField()
is_requestable = serializers.SerializerMethodField()
msg = serializers.CharField()
title = serializers.CharField()
def get_is_downloadable(self, cert_data):
return cert_data.cert_status == CertificateStatuses.downloadable and cert_data.download_url is not None
def get_is_requestable(self, cert_data):
return cert_data.cert_status == CertificateStatuses.requesting and cert_data.request_cert_url is not None
class CreditCourseRequirementsSerializer(serializers.Serializer):
"""
Serializer for credit_course_requirements
"""
display_name = serializers.CharField()
namespace = serializers.CharField()
min_grade = serializers.SerializerMethodField()
status = serializers.CharField()
status_date = serializers.DateTimeField()
def get_min_grade(self, req):
return req['criteria']['min_grade'] * 100
class ProgressTabSerializer(serializers.Serializer):
"""
Serializer for progress tab
"""
course_blocks = CourseBlockSerializer()
certificate_data = CertificateDataSerializer()
courseware_summary = ChapterSerializer(many=True)
enrollment_mode = serializers.CharField()
studio_url = serializers.CharField()
user_timezone = serializers.CharField()

View File

@@ -35,6 +35,7 @@ class ProgressTabTestViews(BaseCourseHomeTests):
self.assertIsNotNone(response.data['courseware_summary'])
for chapter in response.data['courseware_summary']:
self.assertIsNotNone(chapter)
self.assertIn('settings/grading/' + str(self.course.id), response.data['studio_url'])
def test_get_authenticated_user_not_enrolled(self):
response = self.client.get(self.url)

View File

@@ -14,13 +14,13 @@ from lms.djangoapps.course_home_api.progress.v1.serializers import ProgressTabSe
from student.models import CourseEnrollment
from lms.djangoapps.course_api.blocks.transformers.blocks_api import BlocksAPITransformer
from lms.djangoapps.courseware.context_processor import user_timezone_locale_prefs
from lms.djangoapps.courseware.courses import get_course_with_access
from lms.djangoapps.courseware.courses import get_course_with_access, get_studio_url
from lms.djangoapps.courseware.masquerade import setup_masquerade
from lms.djangoapps.courseware.access import has_access
from xmodule.modulestore.django import modulestore
from lms.djangoapps.course_blocks.api import get_course_blocks
import lms.djangoapps.course_blocks.api as course_blocks_api
from lms.djangoapps.courseware.views.views import credit_course_requirements, get_cert_data
from lms.djangoapps.grades.api import CourseGradeFactory
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers
@@ -39,18 +39,13 @@ class ProgressTabView(RetrieveAPIView):
Body consists of the following fields:
course_blocks:
blocks: List of serialized Course Block objects. Each serialization has the following fields:
id: (str) The usage ID of the block.
type: (str) The type of block. Possible values the names of any
XBlock type in the system, including custom blocks. Examples are
course, chapter, sequential, vertical, html, problem, video, and
discussion.
display_name: (str) The display name of the block.
lms_web_url: (str) The URL to the navigational container of the
xBlock on the web LMS.
children: (list) If the block has child blocks, a list of IDs of
the child blocks.
certificate_data: Object containing information about the user's certificate status
cert_web_view_url: (str) the url to view the certificate
download_url: (str) the url to download the certificate
is_downloadable: (bool) true if the status is downloadable and the download url is not None
is_requestable: (bool) true if status is requesting and request_cert_url is not None
msg: (str) message for the certificate status
title: (str) title of the certificate status
courseware_summary: 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:
@@ -66,6 +61,7 @@ class ProgressTabView(RetrieveAPIView):
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
enrollment_mode: (str) a str representing the enrollment the user has ('audit', 'verified', ...)
studio_url: (str) a str of the link to the grading in studio for the course
user_timezone: (str) The user's preferred timezone
@@ -107,7 +103,6 @@ class ProgressTabView(RetrieveAPIView):
BlocksAPITransformer(None, None, depth=3),
]
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True)
enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)
@@ -115,9 +110,10 @@ class ProgressTabView(RetrieveAPIView):
courseware_summary = course_grade.chapter_grades.values()
data = {
'course_blocks': course_blocks,
'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade),
'courseware_summary': courseware_summary,
'enrollment_mode': enrollment_mode,
'studio_url': get_studio_url(course, 'settings/grading'),
'user_timezone': user_timezone,
}
context = self.get_serializer_context()

View File

@@ -1707,7 +1707,7 @@ class ProgressPageTests(ProgressPageBaseTests):
)
# Invalidate user certificate
generated_certificate.invalidate()
response = views._get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
self.assertEqual(response.cert_status, 'invalidated')
self.assertEqual(response.title, 'Your certificate has been invalidated')
@@ -1720,7 +1720,7 @@ class ProgressPageTests(ProgressPageBaseTests):
self.generate_certificate(
"http://www.example.com/certificate.pdf", "honor"
)
response = views._get_cert_data(
response = views.get_cert_data(
self.user, self.course, CourseMode.HONOR, MagicMock(passed=True)
)
@@ -1736,7 +1736,7 @@ class ProgressPageTests(ProgressPageBaseTests):
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status(is_generating=True)):
response = views._get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
self.assertEqual(response.cert_status, 'generating')
self.assertEqual(response.title, "We're working on it...")
@@ -1750,7 +1750,7 @@ class ProgressPageTests(ProgressPageBaseTests):
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status(is_unverified=True)):
response = views._get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
self.assertEqual(response.cert_status, 'unverified')
self.assertEqual(response.title, "Certificate unavailable")
@@ -1764,7 +1764,7 @@ class ProgressPageTests(ProgressPageBaseTests):
)
with patch('lms.djangoapps.certificates.api.certificate_downloadable_status',
return_value=self.mock_certificate_downloadable_status()):
response = views._get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
response = views.get_cert_data(self.user, self.course, CourseMode.HONOR, MagicMock(passed=True))
self.assertEqual(response.cert_status, 'requesting')
self.assertEqual(response.title, "Congratulations, you qualified for a certificate!")

View File

@@ -1170,9 +1170,9 @@ def _progress(request, course_key, student_id):
'masquerade': masquerade,
'supports_preview_menu': True,
'student': student,
'credit_course_requirements': _credit_course_requirements(course_key, student),
'credit_course_requirements': credit_course_requirements(course_key, student),
'course_expiration_fragment': course_expiration_fragment,
'certificate_data': _get_cert_data(student, course, enrollment_mode, course_grade)
'certificate_data': get_cert_data(student, course, enrollment_mode, course_grade)
}
context.update(
@@ -1230,7 +1230,7 @@ def _certificate_message(student, course, enrollment_mode):
return REQUESTING_CERT_DATA
def _get_cert_data(student, course, enrollment_mode, course_grade=None):
def get_cert_data(student, course, enrollment_mode, course_grade=None):
"""Returns students course certificate related data.
Arguments:
student (User): Student for whom certificate to retrieve.
@@ -1257,7 +1257,7 @@ def _get_cert_data(student, course, enrollment_mode, course_grade=None):
return cert_data
def _credit_course_requirements(course_key, student):
def credit_course_requirements(course_key, student):
"""Return information about which credit requirements a user has satisfied.
Arguments:
course_key (CourseKey): Identifier for the course.