Esempio n. 1
0
    def iter(self, course, students):
        """
        Given a course and an iterable of students (User), yield a GradeResult
        for every student enrolled in the course.  GradeResult is a named tuple of:

            (student, course_grade, err_msg)

        If an error occurred, course_grade will be None and err_msg will be an
        exception message. If there was no error, err_msg is an empty string.
        """
        # Pre-fetch the collected course_structure so:
        # 1. Correctness: the same version of the course is used to
        #    compute the grade for all students.
        # 2. Optimization: the collected course_structure is not
        #    retrieved from the data store multiple times.

        collected_block_structure = get_block_structure_manager(course.id).get_collected()
        for student in students:
            with dog_stats_api.timer('lms.grades.CourseGradeFactory.iter', tags=[u'action:{}'.format(course.id)]):
                try:
                    course_grade = CourseGradeFactory().create(student, course, collected_block_structure)
                    yield self.GradeResult(student, course_grade, "")

                except Exception as exc:  # pylint: disable=broad-except
                    # Keep marching on even if this student couldn't be graded for
                    # some reason, but log it for future reference.
                    log.exception(
                        'Cannot grade student %s (%s) in course %s because of exception: %s',
                        student.username,
                        student.id,
                        course.id,
                        exc.message
                    )
                    yield self.GradeResult(student, None, exc.message)
    def iter(self, course, students, force_update=False):
        """
        Given a course and an iterable of students (User), yield a GradeResult
        for every student enrolled in the course.  GradeResult is a named tuple of:

            (student, course_grade, err_msg)

        If an error occurred, course_grade will be None and err_msg will be an
        exception message. If there was no error, err_msg is an empty string.
        """
        # Pre-fetch the collected course_structure so:
        # 1. Correctness: the same version of the course is used to
        #    compute the grade for all students.
        # 2. Optimization: the collected course_structure is not
        #    retrieved from the data store multiple times.

        collected_block_structure = get_block_structure_manager(
            course.id).get_collected()
        for student in students:
            with dog_stats_api.timer('lms.grades.CourseGradeFactory.iter',
                                     tags=[u'action:{}'.format(course.id)]):
                try:
                    operation = CourseGradeFactory(
                    ).update if force_update else CourseGradeFactory().create
                    course_grade = operation(student, course,
                                             collected_block_structure)
                    yield self.GradeResult(student, course_grade, "")

                except Exception as exc:  # pylint: disable=broad-except
                    # Keep marching on even if this student couldn't be graded for
                    # some reason, but log it for future reference.
                    log.exception(
                        'Cannot grade student %s (%s) in course %s because of exception: %s',
                        student.username, student.id, course.id, exc.message)
                    yield self.GradeResult(student, None, exc.message)
def get_course_blocks(
    user,
    starting_block_usage_key,
    transformers=None,
    collected_block_structure=None,
    allow_start_dates_in_future=False,
    include_completion=False,
    include_has_scheduled_content=False,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at starting_block_usage_key.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        starting_block_usage_key (UsageKey) - Specifies the starting block
            of the block structure that is to be transformed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, get_course_block_access_transformers() is used.

        collected_block_structure (BlockStructureBlockData) - A
            block structure retrieved from a prior call to
            BlockStructureManager.get_collected.  Can be optionally
            provided if already available, for optimization.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at starting_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if not transformers:
        transformers = BlockStructureTransformers(
            get_course_block_access_transformers(user))
    if include_completion:
        transformers += [BlockCompletionTransformer()]
    transformers.usage_info = CourseUsageInfo(
        starting_block_usage_key.course_key, user, allow_start_dates_in_future,
        include_has_scheduled_content)

    return get_block_structure_manager(
        starting_block_usage_key.course_key).get_transformed(
            transformers,
            starting_block_usage_key,
            collected_block_structure,
        )
Esempio n. 4
0
def get_course_blocks(
        user,
        starting_block_usage_key,
        transformers=None,
        collected_block_structure=None,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at starting_block_usage_key.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        starting_block_usage_key (UsageKey) - Specifies the starting block
            of the block structure that is to be transformed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used.

        collected_block_structure (BlockStructureBlockData) - A
            block structure retrieved from a prior call to
            BlockStructureManager.get_collected.  Can be optionally
            provided if already available, for optimization.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at starting_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if not transformers:
        transformers = BlockStructureTransformers(COURSE_BLOCK_ACCESS_TRANSFORMERS)
    transformers.usage_info = CourseUsageInfo(starting_block_usage_key.course_key, user)

    return get_block_structure_manager(starting_block_usage_key.course_key).get_transformed(
        transformers,
        starting_block_usage_key,
        collected_block_structure,
    )
Esempio n. 5
0
def get_course_blocks_tma(
    user,
    course_id,
    transformers=None,
    collected_block_structure=None,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at starting_block_usage_key.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        starting_block_usage_key (UsageKey) - Specifies the starting block
            of the block structure that is to be transformed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used.

        collected_block_structure (BlockStructureBlockData) - A
            block structure retrieved from a prior call to
            BlockStructureManager.get_collected.  Can be optionally
            provided if already available, for optimization.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at starting_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if not transformers:
        transformers = BlockStructureTransformers(
            COURSE_BLOCK_ACCESS_TRANSFORMERS)
    transformers.usage_info = CourseUsageInfo(course_id, user)

    return get_block_structure_manager(course_id)
def get_course_blocks(
    user,
    starting_block_usage_key,
    transformers=None,
):
    """
    A higher order function implemented on top of the
    block_structure.get_blocks function returning a transformed block
    structure for the given user starting at starting_block_usage_key.

    Arguments:
        user (django.contrib.auth.models.User) - User object for
            which the block structure is to be transformed.

        starting_block_usage_key (UsageKey) - Specifies the starting block
            of the block structure that is to be transformed.

        transformers (BlockStructureTransformers) - A collection of
            transformers whose transform methods are to be called.
            If None, COURSE_BLOCK_ACCESS_TRANSFORMERS is used.

    Returns:
        BlockStructureBlockData - A transformed block structure,
            starting at starting_block_usage_key, that has undergone the
            transform methods for the given user and the course
            associated with the block structure.  If using the default
            transformers, the transformed block structure will be
            exactly equivalent to the blocks that the given user has
            access.
    """
    if not transformers:
        transformers = BlockStructureTransformers(
            COURSE_BLOCK_ACCESS_TRANSFORMERS)
    transformers.usage_info = CourseUsageInfo(
        starting_block_usage_key.course_key, user)

    return get_block_structure_manager(
        starting_block_usage_key.course_key).get_transformed(
            transformers,
            starting_block_usage_key,
        )
Esempio n. 7
0
 def collected_structure(self):
     if self._collected_block_structure is None:
         self._collected_block_structure = get_block_structure_manager(self.course_key).get_collected()
     return self._collected_block_structure
Esempio n. 8
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)

        if not course_home_mfe_progress_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)

        _, request.user = setup_masquerade(request,
                                           course_key,
                                           staff_access=has_access(
                                               request.user, 'staff',
                                               course_key),
                                           reset_masquerade_data=True)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=True)

        enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user(
            request.user, course_key)

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

        # Get has_scheduled_content data
        transformers = BlockStructureTransformers()
        transformers += [start_date.StartDateTransformer()]
        usage_key = collected_block_structure.root_block_usage_key
        course_blocks = get_course_blocks(
            request.user,
            usage_key,
            transformers=transformers,
            collected_block_structure=collected_block_structure,
            include_has_scheduled_content=True)
        has_scheduled_content = course_blocks.get_xblock_field(
            usage_key, 'has_scheduled_content')

        # Get user_has_passing_grade data
        user_has_passing_grade = False
        if not request.user.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_link = None
        if verification_status['status'] is None or verification_status[
                'status'] == 'expired':
            verification_link = IDVerificationService.get_verify_location(
                course_id=course_key)
        elif verification_status['status'] == 'must_reverify':
            verification_link = IDVerificationService.get_verify_location(
                course_id=course_key)
        verification_data = {
            'link': verification_link,
            'status': verification_status['status'],
            'status_date': verification_status['status_date'],
        }

        data = {
            '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),
            'course_grade':
            course_grade,
            'has_scheduled_content':
            has_scheduled_content,
            'section_scores':
            course_grade.chapter_grades.values(),
            'enrollment_mode':
            enrollment_mode,
            'grading_policy':
            grading_policy,
            'studio_url':
            get_studio_url(course, 'settings/grading'),
            'verification_data':
            verification_data,
        }
        context = self.get_serializer_context()
        context['staff_access'] = bool(
            has_access(request.user, 'staff', course))
        context['course_key'] = course_key
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Esempio n. 9
0
    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

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)
        is_staff = bool(has_access(request.user, 'staff', course_key))

        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

            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(student, course_key)
        enrollment_mode = getattr(enrollment, 'mode', None)

        if not (enrollment and enrollment.is_active) and not is_staff:
            return Response('User not enrolled.', status=401)

        # 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(
            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(
            student,
            usage_key,
            transformers=transformers,
            collected_block_structure=collected_block_structure,
            include_has_scheduled_content=True)
        has_scheduled_content = course_blocks.get_xblock_field(
            usage_key, 'has_scheduled_content')

        # Get user_has_passing_grade data
        user_has_passing_grade = False
        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(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)
        elif verification_status['status'] == 'must_reverify':
            verification_link = IDVerificationService.get_verify_location(
                course_id=course_key)
        verification_data = {
            'link': verification_link,
            'status': verification_status['status'],
            'status_date': verification_status['status_date'],
        }

        data = {
            'username':
            username,
            'end':
            course.end,
            'user_has_passing_grade':
            user_has_passing_grade,
            '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(),
            'enrollment_mode':
            enrollment_mode,
            'grading_policy':
            grading_policy,
            'studio_url':
            get_studio_url(course, 'settings/grading'),
            'verification_data':
            verification_data,
        }
        context = self.get_serializer_context()
        context['staff_access'] = is_staff
        context['course_blocks'] = course_blocks
        context['course_key'] = course_key
        # course_overview and enrollment will be used by VerifiedModeSerializerMixin
        context['course_overview'] = course_overview
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
Esempio n. 10
0
 def collected_structure(self):
     if self._collected_block_structure is None:
         self._collected_block_structure = get_block_structure_manager(self.course_key).get_collected()
     return self._collected_block_structure
Esempio n. 11
0
    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 not course_home_mfe_progress_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)
        is_staff = bool(has_access(request.user, 'staff', course_key))

        student = self._get_student_user(request, course_key, student_id,
                                         is_staff)
        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(student, course_key)
        enrollment_mode = getattr(enrollment, 'mode', None)

        if not (enrollment and enrollment.is_active) and not is_staff:
            return Response('User not enrolled.', status=401)

        # 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(
            student, collected_block_structure=collected_block_structure)

        # recalculate course grade from visible grades (stored grade was calculated over all grades, visible or not)
        course_grade.update(visible_grades_only=True,
                            has_staff_access=is_staff)

        # 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(
            student,
            usage_key,
            transformers=transformers,
            collected_block_structure=collected_block_structure,
            include_has_scheduled_content=True)
        has_scheduled_content = course_blocks.get_xblock_field(
            usage_key, 'has_scheduled_content')

        # Get user_has_passing_grade data
        user_has_passing_grade = False
        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(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)
        elif verification_status['status'] == 'must_reverify':
            verification_link = IDVerificationService.get_verify_location(
                course_id=course_key)
        verification_data = {
            'link': verification_link,
            'status': verification_status['status'],
            'status_date': verification_status['status_date'],
        }

        access_expiration = get_access_expiration_data(request.user,
                                                       course_overview)

        data = {
            '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,
            'credit_course_requirements':
            credit_course_requirements(course_key, student),
            '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()
        context['staff_access'] = is_staff
        context['course_blocks'] = course_blocks
        context['course_key'] = course_key
        # course_overview and enrollment will be used by VerifiedModeSerializer
        context['course_overview'] = course_overview
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)