Пример #1
0
def get_entrance_exam_score(request, course):
    """
    Gather the set of modules which comprise the entrance exam
    Note that 'request' may not actually be a genuine request, due to the
    circular nature of module_render calling entrance_exams and get_module_for_descriptor
    being used here.  In some use cases, the caller is actually mocking a request, although
    in these scenarios the 'user' child object can be trusted and used as expected.
    It's a much larger refactoring job to break this legacy mess apart, unfortunately.
    """
    exam_key = UsageKey.from_string(course.entrance_exam_id)
    exam_descriptor = modulestore().get_item(exam_key)

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor (imported here to avoid circular reference)
        """
        from courseware.module_render import get_module_for_descriptor
        field_data_cache = FieldDataCache([descriptor], course.id,
                                          request.user)
        return get_module_for_descriptor(request.user, request, descriptor,
                                         field_data_cache, course.id)

    exam_module_generators = yield_dynamic_descriptor_descendents(
        exam_descriptor, inner_get_module)
    exam_modules = [module for module in exam_module_generators]
    return _calculate_entrance_exam_score(request.user, course, exam_modules)
Пример #2
0
def get_entrance_exam_score(request, course):
    """
    Gather the set of modules which comprise the entrance exam
    Note that 'request' may not actually be a genuine request, due to the
    circular nature of module_render calling entrance_exams and get_module_for_descriptor
    being used here.  In some use cases, the caller is actually mocking a request, although
    in these scenarios the 'user' child object can be trusted and used as expected.
    It's a much larger refactoring job to break this legacy mess apart, unfortunately.
    """
    exam_key = UsageKey.from_string(course.entrance_exam_id)
    exam_descriptor = modulestore().get_item(exam_key)

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor (imported here to avoid circular reference)
        """
        from courseware.module_render import get_module_for_descriptor
        field_data_cache = FieldDataCache([descriptor], course.id, request.user)
        return get_module_for_descriptor(
            request.user,
            request,
            descriptor,
            field_data_cache,
            course.id
        )

    exam_module_generators = yield_dynamic_descriptor_descendents(
        exam_descriptor,
        inner_get_module
    )
    exam_modules = [module for module in exam_module_generators]
    return _calculate_entrance_exam_score(request.user, course, exam_modules)
Пример #3
0
 def _calculate_entrance_exam_score(user, course_descriptor):
     """
     Internal helper to calculate a user's score for a course's entrance exam
     """
     exam_key = UsageKey.from_string(course_descriptor.entrance_exam_id)
     exam_descriptor = modulestore().get_item(exam_key)
     exam_module_generators = yield_dynamic_descriptor_descendents(exam_descriptor, inner_get_module)
     exam_modules = [module for module in exam_module_generators]
     exam_score = calculate_entrance_exam_score(user, course_descriptor, exam_modules)
     return exam_score
Пример #4
0
    def get_grades(self, target_block_id, limit_hint=None):
        """
        Get the grades as a percentage for the top students who have started or
        completed target_block_id and its descendants.

        The current implementation is as follows:

           This block 'grade_source' class calls the get_score() grading
           computation built into edX which returns the grade for the current
           student only, which is then cached in the grade_leaderboard xblock
           in a user_state_summary field. The block will then use the cached
           grades from all students to compute the leaderboard.

           Pros:
             * Fairly efficient - generally uses cached grades, making one MySQL query per block
             * Grading should match edX grading very well since it uses the same method
             * Supports all block types
           Cons:
             * Requires students to view the block before their grades appear
               in the leaderboard - could result in missing/outdated data, depending
               on where the block appears in the course and how often students view
               the leaderboard.
             * Storing data in Scope.user_state_summary will lead to read write conflicts.
               e.g. if two students view the courseware simultaneously and their requests
               are handled by different gunicorn processes.

        TODO: We will need to replace this implementation with something that uses celery
        to update student grades asynchronously and stores them in a more robust cache format.
        """
        total_correct, total_possible = 0, 0
        course_id = target_block_id.course_key.for_branch(None).version_agnostic()
        target_block = self.host_block.runtime.get_block(target_block_id)
        student = self.host_block.runtime.get_real_user(self.host_block.runtime.anonymous_student_id)

        def create_module(descriptor):
            return target_block.runtime.get_block(descriptor.location)

        for module_descriptor in yield_dynamic_descriptor_descendents(target_block, create_module):

            (correct, total) = get_score(course_id, student, module_descriptor, create_module)
            if (correct is None and total is None) or (not total > 0):
                continue

            # Note we ignore the 'graded' flag since authors may wish to use a leaderboard for non-graded content
            total_correct += correct
            total_possible += total

        percent = int(round(float(total_correct) / total_possible * 100))

        with self.update_grades_cache(target_block_id) as cache:
            cache[unicode(student.id)] = (percent, {"name": student.profile.name})
            result = cache.values()

        return [entry for entry in result if entry[0] > 0]  # Return all non-zero grades
Пример #5
0
 def _calculate_entrance_exam_score(user, course_descriptor):
     """
     Internal helper to calculate a user's score for a course's entrance exam
     """
     exam_key = UsageKey.from_string(course_descriptor.entrance_exam_id)
     exam_descriptor = modulestore().get_item(exam_key)
     exam_module_generators = yield_dynamic_descriptor_descendents(
         exam_descriptor, inner_get_module)
     exam_modules = [module for module in exam_module_generators]
     exam_score = milestones_helpers.calculate_entrance_exam_score(
         user, course_descriptor, exam_modules)
     return exam_score
Пример #6
0
def get_entrance_exam_score(request, course):
    """
    Get entrance exam score
    """
    exam_key = UsageKey.from_string(course.entrance_exam_id)
    exam_descriptor = modulestore().get_item(exam_key)

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor.
        """
        field_data_cache = FieldDataCache([descriptor], course.id, request.user)
        return get_module_for_descriptor(request.user, request, descriptor, field_data_cache, course.id)

    exam_module_generators = yield_dynamic_descriptor_descendents(exam_descriptor, inner_get_module)
    exam_modules = [module for module in exam_module_generators]
    return calculate_entrance_exam_score(request.user, course, exam_modules)
Пример #7
0
def get_entrance_exam_score(request, course):
    """
    Get entrance exam score
    """
    exam_key = UsageKey.from_string(course.entrance_exam_id)
    exam_descriptor = modulestore().get_item(exam_key)

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor.
        """
        field_data_cache = FieldDataCache([descriptor], course.id,
                                          request.user)
        return get_module_for_descriptor(request.user, request, descriptor,
                                         field_data_cache, course.id)

    exam_module_generators = yield_dynamic_descriptor_descendents(
        exam_descriptor, inner_get_module)
    exam_modules = [module for module in exam_module_generators]
    return calculate_entrance_exam_score(request.user, course, exam_modules)
Пример #8
0
def _progress_summary(student, request, course):
    """
    Unwrapped version of "progress_summary".

    This pulls a summary of all problems in the course.

    Returns
    - courseware_summary is a summary of all sections with problems in the course.
    It is organized as an array of chapters, each containing an array of sections,
    each containing an array of scores. This contains information for graded and
    ungraded problems, and is good for displaying a course summary with due dates,
    etc.

    Arguments:
        student: A User object for the student to grade
        course: A Descriptor containing the course to grade

    If the student does not have access to load the course module, this function
    will return None.

    """
    with manual_transaction():
        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            course.id, student, course, depth=None
        )
        # TODO: We need the request to pass into here. If we could
        # forego that, our arguments would be simpler
        course_module = get_module_for_descriptor(student, request, course, field_data_cache, course.id)
        if not course_module:
            # This student must not have access to the course.
            return None

        course_module = getattr(course_module, '_x_module', course_module)

    submissions_scores = sub_api.get_scores(course.id.to_deprecated_string(), anonymous_id_for_user(student, course.id))

    chapters = []
    # Don't include chapters that aren't displayable (e.g. due to error)
    for chapter_module in course_module.get_display_items():
        # Skip if the chapter is hidden
        if chapter_module.hide_from_toc:
            continue

        sections = []

        for section_module in chapter_module.get_display_items():
            # Skip if the section is hidden
            with manual_transaction():
                if section_module.hide_from_toc:
                    continue

                graded = section_module.graded
                scores = []

                module_creator = section_module.xmodule_runtime.get_module

                for module_descriptor in yield_dynamic_descriptor_descendents(section_module, module_creator):
                    course_id = course.id
                    (correct, total) = get_score(
                        course_id, student, module_descriptor, module_creator, scores_cache=submissions_scores
                    )
                    if correct is None and total is None:
                        continue

                    scores.append(
                        Score(
                            correct,
                            total,
                            graded,
                            module_descriptor.display_name_with_default,
                            module_descriptor.location
                        )
                    )

                scores.reverse()
                section_total, _ = graders.aggregate_scores(
                    scores, section_module.display_name_with_default)

                module_format = section_module.format if section_module.format is not None else ''
                sections.append({
                    'display_name': section_module.display_name_with_default,
                    'url_name': section_module.url_name,
                    'scores': scores,
                    'section_total': section_total,
                    'format': module_format,
                    'due': section_module.due,
                    'graded': graded,
                })

        chapters.append({
            'course': course.display_name_with_default,
            'display_name': chapter_module.display_name_with_default,
            'url_name': chapter_module.url_name,
            'sections': sections
        })

    return chapters
Пример #9
0
def _grade(student, request, course, keep_raw_scores):
    """
    Unwrapped version of "grade"

    This grades a student as quickly as possible. It returns the
    output from the course grader, augmented with the final letter
    grade. The keys in the output are:

    course: a CourseDescriptor

    - grade : A final letter grade.
    - percent : The final percent for the class (rounded up).
    - section_breakdown : A breakdown of each section that makes
      up the grade. (For display)
    - grade_breakdown : A breakdown of the major components that
      make up the final grade. (For display)
    - keep_raw_scores : if True, then value for key 'raw_scores' contains scores
      for every graded module

    More information on the format is in the docstring for CourseGrader.
    """
    grading_context = course.grading_context
    raw_scores = []

    # Dict of item_ids -> (earned, possible) point tuples. This *only* grabs
    # scores that were registered with the submissions API, which for the moment
    # means only openassessment (edx-ora2)
    submissions_scores = sub_api.get_scores(
        course.id.to_deprecated_string(), anonymous_id_for_user(student, course.id)
    )

    totaled_scores = {}
    # This next complicated loop is just to collect the totaled_scores, which is
    # passed to the grader
    for section_format, sections in grading_context['graded_sections'].iteritems():
        format_scores = []
        for section in sections:
            section_descriptor = section['section_descriptor']
            section_name = section_descriptor.display_name_with_default

            # some problems have state that is updated independently of interaction
            # with the LMS, so they need to always be scored. (E.g. foldit.,
            # combinedopenended)
            should_grade_section = any(
                descriptor.always_recalculate_grades for descriptor in section['xmoduledescriptors']
            )

            # If there are no problems that always have to be regraded, check to
            # see if any of our locations are in the scores from the submissions
            # API. If scores exist, we have to calculate grades for this section.
            if not should_grade_section:
                should_grade_section = any(
                    descriptor.location.to_deprecated_string() in submissions_scores
                    for descriptor in section['xmoduledescriptors']
                )

            if not should_grade_section:
                with manual_transaction():
                    should_grade_section = StudentModule.objects.filter(
                        student=student,
                        module_state_key__in=[
                            descriptor.location for descriptor in section['xmoduledescriptors']
                        ]
                    ).exists()

            # If we haven't seen a single problem in the section, we don't have
            # to grade it at all! We can assume 0%
            if should_grade_section:
                scores = []

                def create_module(descriptor):
                    '''creates an XModule instance given a descriptor'''
                    # TODO: We need the request to pass into here. If we could forego that, our arguments
                    # would be simpler
                    with manual_transaction():
                        field_data_cache = FieldDataCache([descriptor], course.id, student)
                    return get_module_for_descriptor(student, request, descriptor, field_data_cache, course.id)

                for module_descriptor in yield_dynamic_descriptor_descendents(section_descriptor, create_module):

                    (correct, total) = get_score(
                        course.id, student, module_descriptor, create_module, scores_cache=submissions_scores
                    )
                    if correct is None and total is None:
                        continue

                    if settings.GENERATE_PROFILE_SCORES:  	# for debugging!
                        if total > 1:
                            correct = random.randrange(max(total - 2, 1), total + 1)
                        else:
                            correct = total

                    graded = module_descriptor.graded
                    if not total > 0:
                        # We simply cannot grade a problem that is 12/0, because we might need it as a percentage
                        graded = False

                    scores.append(
                        Score(
                            correct,
                            total,
                            graded,
                            module_descriptor.display_name_with_default,
                            module_descriptor.location
                        )
                    )

                _, graded_total = graders.aggregate_scores(scores, section_name)
                if keep_raw_scores:
                    raw_scores += scores
            else:
                graded_total = Score(0.0, 1.0, True, section_name, None)

            #Add the graded total to totaled_scores
            if graded_total.possible > 0:
                format_scores.append(graded_total)
            else:
                log.info(
                    "Unable to grade a section with a total possible score of zero. " +
                    str(section_descriptor.location)
                )

        totaled_scores[section_format] = format_scores

    # Grading policy might be overriden by a CCX, need to reset it
    course.set_grading_policy(course.grading_policy)
    grade_summary = course.grader.grade(totaled_scores, generate_random_scores=settings.GENERATE_PROFILE_SCORES)

    # We round the grade here, to make sure that the grade is an whole percentage and
    # doesn't get displayed differently than it gets grades
    grade_summary['percent'] = round(grade_summary['percent'] * 100 + 0.05) / 100

    letter_grade = grade_for_percentage(course.grade_cutoffs, grade_summary['percent'])
    grade_summary['grade'] = letter_grade
    grade_summary['totaled_scores'] = totaled_scores  	# make this available, eg for instructor download & debugging
    if keep_raw_scores:
        # way to get all RAW scores out to instructor
        # so grader can be double-checked
        grade_summary['raw_scores'] = raw_scores
    return grade_summary
Пример #10
0
    def get_grades(self, target_block_id, limit_hint=None):
        """
        Get the grades as a percentage for the top students who have started or
        completed target_block_id and its descendants.

        The current implementation is as follows:

           This block 'grade_source' class calls the get_score() grading
           computation built into edX which returns the grade for the current
           student only, which is then cached in the grade_leaderboard xblock
           in a user_state_summary field. The block will then use the cached
           grades from all students to compute the leaderboard.

           Pros:
             * Fairly efficient - generally uses cached grades, making one MySQL query per block
             * Grading should match edX grading very well since it uses the same method
             * Supports all block types
           Cons:
             * Requires students to view the block before their grades appear
               in the leaderboard - could result in missing/outdated data, depending
               on where the block appears in the course and how often students view
               the leaderboard.
             * Storing data in Scope.user_state_summary will lead to read write conflicts.
               e.g. if two students view the courseware simultaneously and their requests
               are handled by different gunicorn processes.

        TODO: We will need to replace this implementation with something that uses celery
        to update student grades asynchronously and stores them in a more robust cache format.
        """
        total_correct, total_possible = 0, 0
        course_id = target_block_id.course_key.for_branch(
            None).version_agnostic()
        target_block = self.host_block.runtime.get_block(target_block_id)
        student = self.host_block.runtime.get_real_user(
            self.host_block.runtime.anonymous_student_id)

        def create_module(descriptor):
            return target_block.runtime.get_block(descriptor.location)

        for module_descriptor in yield_dynamic_descriptor_descendents(
                target_block, create_module):

            (correct, total) = get_score(course_id, student, module_descriptor,
                                         create_module)
            if (correct is None and total is None) or (not total > 0):
                continue

            # Note we ignore the 'graded' flag since authors may wish to use a leaderboard for non-graded content
            total_correct += correct
            total_possible += total

        percent = int(round(float(total_correct) / total_possible * 100))

        with self.update_grades_cache(target_block_id) as cache:
            cache[unicode(student.id)] = (percent, {
                "name": student.profile.name
            })
            result = cache.values()

        return [entry for entry in result
                if entry[0] > 0]  # Return all non-zero grades