|
|
|
|
@@ -3,6 +3,7 @@ Progress Tab Views
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from django.http.response import Http404
|
|
|
|
|
from django.contrib.auth.models import User
|
|
|
|
|
from edx_django_utils import monitoring as monitoring_utils
|
|
|
|
|
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
|
|
|
|
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
|
|
|
|
|
@@ -15,10 +16,11 @@ from xmodule.modulestore.django import modulestore
|
|
|
|
|
from common.djangoapps.student.models import CourseEnrollment
|
|
|
|
|
from lms.djangoapps.course_home_api.progress.v1.serializers import ProgressTabSerializer
|
|
|
|
|
from lms.djangoapps.course_home_api.toggles import course_home_mfe_progress_tab_is_active
|
|
|
|
|
from lms.djangoapps.courseware.access import has_access
|
|
|
|
|
from lms.djangoapps.courseware.access import has_access, has_ccx_coach_role
|
|
|
|
|
from lms.djangoapps.course_blocks.api import get_course_blocks
|
|
|
|
|
from lms.djangoapps.course_blocks.transformers import start_date
|
|
|
|
|
|
|
|
|
|
from lms.djangoapps.ccx.custom_exception import CCXLocatorValidationException
|
|
|
|
|
from lms.djangoapps.courseware.courses import get_course_blocks_completion_summary, get_course_with_access, get_studio_url
|
|
|
|
|
from lms.djangoapps.courseware.masquerade import setup_masquerade
|
|
|
|
|
from lms.djangoapps.courseware.views.views import get_cert_data
|
|
|
|
|
@@ -30,6 +32,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.enterprise_support.utils import get_enterprise_learner_generic_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
@@ -41,6 +44,7 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
**Example Requests**
|
|
|
|
|
|
|
|
|
|
GET api/course_home/v1/progress/{course_key}
|
|
|
|
|
GET api/course_home/v1/progress/{course_key}/{student_id}/
|
|
|
|
|
|
|
|
|
|
**Response Values**
|
|
|
|
|
|
|
|
|
|
@@ -48,6 +52,7 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
@@ -113,6 +118,12 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
|
course_key_string = kwargs.get('course_key_string')
|
|
|
|
|
course_key = CourseKey.from_string(course_key_string)
|
|
|
|
|
student_id = kwargs.get('student_id')
|
|
|
|
|
if student_id:
|
|
|
|
|
try:
|
|
|
|
|
student_id = int(student_id)
|
|
|
|
|
except ValueError:
|
|
|
|
|
raise Http404
|
|
|
|
|
|
|
|
|
|
if not course_home_mfe_progress_tab_is_active(course_key):
|
|
|
|
|
raise Http404
|
|
|
|
|
@@ -123,17 +134,36 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff)
|
|
|
|
|
is_staff = bool(has_access(request.user, 'staff', course_key))
|
|
|
|
|
|
|
|
|
|
_, request.user = setup_masquerade(
|
|
|
|
|
request,
|
|
|
|
|
course_key,
|
|
|
|
|
staff_access=is_staff,
|
|
|
|
|
reset_masquerade_data=True
|
|
|
|
|
)
|
|
|
|
|
if student_id is None or student_id == request.user.id:
|
|
|
|
|
_, student = setup_masquerade(
|
|
|
|
|
request,
|
|
|
|
|
course_key,
|
|
|
|
|
staff_access=is_staff,
|
|
|
|
|
reset_masquerade_data=True
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
# When a student_id is passed in, we display the progress page for the user
|
|
|
|
|
# with the provided user id, rather than the requesting user
|
|
|
|
|
try:
|
|
|
|
|
coach_access = has_ccx_coach_role(request.user, course_key)
|
|
|
|
|
except CCXLocatorValidationException:
|
|
|
|
|
coach_access = False
|
|
|
|
|
|
|
|
|
|
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False)
|
|
|
|
|
has_access_on_students_profiles = is_staff or coach_access
|
|
|
|
|
# Requesting access to a different student's profile
|
|
|
|
|
if not has_access_on_students_profiles:
|
|
|
|
|
raise Http404
|
|
|
|
|
try:
|
|
|
|
|
student = User.objects.get(id=student_id)
|
|
|
|
|
except User.DoesNotExist as exc:
|
|
|
|
|
raise Http404 from exc
|
|
|
|
|
|
|
|
|
|
username = get_enterprise_learner_generic_name(request) or student.username
|
|
|
|
|
|
|
|
|
|
course = get_course_with_access(student, 'load', course_key, check_if_enrolled=False)
|
|
|
|
|
|
|
|
|
|
course_overview = CourseOverview.get_from_id(course_key)
|
|
|
|
|
enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
|
|
|
|
|
enrollment = CourseEnrollment.get_enrollment(student, course_key)
|
|
|
|
|
enrollment_mode = getattr(enrollment, 'mode', None)
|
|
|
|
|
|
|
|
|
|
if not (enrollment and enrollment.is_active) and not is_staff:
|
|
|
|
|
@@ -142,14 +172,14 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
# The block structure is used for both the course_grade and has_scheduled content fields
|
|
|
|
|
# So it is called upfront and reused for optimization purposes
|
|
|
|
|
collected_block_structure = get_block_structure_manager(course_key).get_collected()
|
|
|
|
|
course_grade = CourseGradeFactory().read(request.user, collected_block_structure=collected_block_structure)
|
|
|
|
|
course_grade = CourseGradeFactory().read(student, collected_block_structure=collected_block_structure)
|
|
|
|
|
|
|
|
|
|
# Get has_scheduled_content data
|
|
|
|
|
transformers = BlockStructureTransformers()
|
|
|
|
|
transformers += [start_date.StartDateTransformer(), ContentTypeGateTransformer()]
|
|
|
|
|
usage_key = collected_block_structure.root_block_usage_key
|
|
|
|
|
course_blocks = get_course_blocks(
|
|
|
|
|
request.user,
|
|
|
|
|
student,
|
|
|
|
|
usage_key,
|
|
|
|
|
transformers=transformers,
|
|
|
|
|
collected_block_structure=collected_block_structure,
|
|
|
|
|
@@ -159,13 +189,13 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
|
|
|
|
|
# Get user_has_passing_grade data
|
|
|
|
|
user_has_passing_grade = False
|
|
|
|
|
if not request.user.is_anonymous:
|
|
|
|
|
if not student.is_anonymous:
|
|
|
|
|
user_grade = course_grade.percent
|
|
|
|
|
user_has_passing_grade = user_grade >= course.lowest_passing_grade
|
|
|
|
|
|
|
|
|
|
descriptor = modulestore().get_course(course_key)
|
|
|
|
|
grading_policy = descriptor.grading_policy
|
|
|
|
|
verification_status = IDVerificationService.user_status(request.user)
|
|
|
|
|
verification_status = IDVerificationService.user_status(student)
|
|
|
|
|
verification_link = None
|
|
|
|
|
if verification_status['status'] is None or verification_status['status'] == 'expired':
|
|
|
|
|
verification_link = IDVerificationService.get_verify_location(course_id=course_key)
|
|
|
|
|
@@ -178,10 +208,11 @@ class ProgressTabView(RetrieveAPIView):
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
'username': username,
|
|
|
|
|
'end': course.end,
|
|
|
|
|
'user_has_passing_grade': user_has_passing_grade,
|
|
|
|
|
'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade),
|
|
|
|
|
'completion_summary': get_course_blocks_completion_summary(course_key, request.user),
|
|
|
|
|
'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': course_grade.chapter_grades.values(),
|
|
|
|
|
|