Пример #1
0
    def chapter_grades(self):
        """
        Returns a list of chapters, each containing its subsection grades,
        display name, and url name.
        """
        chapter_grades = []
        for chapter_key in self.course_structure.get_children(
                self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            children = self.course_structure.get_children(chapter_key)
            for subsection_key in children:
                chapter_subsection_grades.append(
                    self._subsection_grade_factory.create(
                        self.course_structure[subsection_key], read_only=True))

            chapter_grades.append({
                'display_name':
                block_metadata_utils.display_name_with_default_escaped(
                    chapter),
                'url_name':
                block_metadata_utils.url_name_for_block(chapter),
                'sections':
                chapter_subsection_grades
            })
        return chapter_grades
Пример #2
0
    def chapter_grades(self):
        """
        Returns a dictionary of dictionaries.
        The primary dictionary is keyed by the chapter's usage_key.
        The secondary dictionary contains the chapter's
        subsection grades, display name, and url name.
        """
        chapter_grades = OrderedDict()
        for chapter_key in self.course_structure.get_children(
                self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            children = self.course_structure.get_children(chapter_key)
            for subsection_key in children:
                chapter_subsection_grades.append(
                    self._subsection_grade_factory.create(
                        self.course_structure[subsection_key], read_only=True))

            chapter_grades[chapter_key] = {
                'display_name':
                block_metadata_utils.display_name_with_default_escaped(
                    chapter),
                'url_name':
                block_metadata_utils.url_name_for_block(chapter),
                'sections':
                chapter_subsection_grades
            }
        return chapter_grades
Пример #3
0
    def compute_and_update(self, read_only=False):
        """
        Computes the grade for the given student and course.

        If read_only is True, doesn't save any updates to the grades.
        """
        self._log_event(log.warning, u"compute_and_update, read_only: {}".format(read_only))
        subsection_grade_factory = SubsectionGradeFactory(self.student, self.course, self.course_structure)
        for chapter_key in self.course_structure.get_children(self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            for subsection_key in self.course_structure.get_children(chapter_key):
                chapter_subsection_grades.append(
                    subsection_grade_factory.create(self.course_structure[subsection_key], read_only=True)
                )

            self.chapter_grades.append({
                'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
                'url_name': block_metadata_utils.url_name_for_block(chapter),
                'sections': chapter_subsection_grades
            })

        if not read_only:
            subsection_grade_factory.bulk_create_unsaved()

        self._signal_listeners_when_grade_computed()
Пример #4
0
    def compute(self):
        """
        Computes the grade for the given student and course.
        """
        subsection_grade_factory = SubsectionGradeFactory(self.student)
        for chapter_key in self.course_structure.get_children(
                self.course.location):
            chapter = self.course_structure[chapter_key]
            subsection_grades = []
            for subsection_key in self.course_structure.get_children(
                    chapter_key):
                subsection_grades.append(
                    subsection_grade_factory.create(
                        self.course_structure[subsection_key],
                        self.course_structure, self.course))

            self.chapter_grades.append({
                'display_name':
                block_metadata_utils.display_name_with_default_escaped(
                    chapter),
                'url_name':
                block_metadata_utils.url_name_for_block(chapter),
                'sections':
                subsection_grades
            })

        self._signal_listeners_when_grade_computed()
Пример #5
0
    def compute_and_update(self, read_only=False):
        """
        Computes the grade for the given student and course.

        If read_only is True, doesn't save any updates to the grades.
        """
        self._log_event(log.warning,
                        u"compute_and_update, read_only: {}".format(read_only))
        subsection_grade_factory = SubsectionGradeFactory(
            self.student, self.course, self.course_structure)
        for chapter_key in self.course_structure.get_children(
                self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            for subsection_key in self.course_structure.get_children(
                    chapter_key):
                chapter_subsection_grades.append(
                    subsection_grade_factory.create(
                        self.course_structure[subsection_key], read_only=True))

            self.chapter_grades.append({
                'display_name':
                block_metadata_utils.display_name_with_default_escaped(
                    chapter),
                'url_name':
                block_metadata_utils.url_name_for_block(chapter),
                'sections':
                chapter_subsection_grades
            })

        if not read_only:
            subsection_grade_factory.bulk_create_unsaved()

        self._signal_listeners_when_grade_computed()
Пример #6
0
    def _verify_grades(self, raw_earned, raw_possible, weight, expected_score):
        """
        Verifies the computed grades are as expected.
        """
        with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred):
            # pylint: disable=no-member
            for problem in self.problems:
                problem.weight = weight
                self.store.update_item(problem, self.user.id)
            self.store.publish(self.course.location, self.user.id)

        course_structure = get_course_blocks(self.request.user, self.course.location)

        # answer all problems
        for problem in self.problems:
            answer_problem(self.course, self.request, problem, score=raw_earned, max_value=raw_possible)

        # get grade
        subsection_grade = SubsectionGradeFactory(
            self.request.user, self.course, course_structure
        ).update(self.sequential)

        # verify all problem grades
        for problem in self.problems:
            problem_score = subsection_grade.locations_to_scores[problem.location]
            expected_score.display_name = display_name_with_default_escaped(problem)
            expected_score.module_id = problem.location
            self.assertEquals(problem_score, expected_score)

        # verify subsection grades
        self.assertEquals(subsection_grade.all_total.earned, expected_score.earned * len(self.problems))
        self.assertEquals(subsection_grade.all_total.possible, expected_score.possible * len(self.problems))
Пример #7
0
 def _get_chapter_grade_info(self, chapter, course_structure):
     """
     Helper that returns a dictionary of chapter grade information.
     """
     chapter_subsection_grades = self._get_subsection_grades(course_structure, chapter.location)
     return {
         'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
         'url_name': block_metadata_utils.url_name_for_block(chapter),
         'sections': chapter_subsection_grades,
     }
Пример #8
0
 def _get_chapter_grade_info(self, chapter, course_structure):
     """
     Helper that returns a dictionary of chapter grade information.
     """
     chapter_subsection_grades = self._get_subsection_grades(course_structure, chapter.location)
     return {
         'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
         'url_name': block_metadata_utils.url_name_for_block(chapter),
         'sections': chapter_subsection_grades,
     }
Пример #9
0
    def __init__(self, subsection):
        self.location = subsection.location
        self.display_name = block_metadata_utils.display_name_with_default_escaped(subsection)
        self.url_name = block_metadata_utils.url_name_for_block(subsection)

        self.format = getattr(subsection, 'format', '')
        self.due = getattr(subsection, 'due', None)
        self.graded = getattr(subsection, 'graded', False)

        self.graded_total = None  # aggregated grade for all graded problems
        self.all_total = None  # aggregated grade for all problems, regardless of whether they are graded
        self.locations_to_weighted_scores = OrderedDict()  # dict of problem locations to (Score, weight) tuples
Пример #10
0
    def display_name_with_default_escaped(self):
        """
        DEPRECATED: use display_name_with_default

        Return html escaped reasonable display name for the course.

        Note: This newly introduced method should not be used.  It was only
        introduced to enable a quick search/replace and the ability to slowly
        migrate and test switching to display_name_with_default, which is no
        longer escaped.
        """
        return block_metadata_utils.display_name_with_default_escaped(self)
Пример #11
0
    def display_name_with_default_escaped(self):
        """
        DEPRECATED: use display_name_with_default

        Return html escaped reasonable display name for the course.

        Note: This newly introduced method should not be used.  It was only
        introduced to enable a quick search/replace and the ability to slowly
        migrate and test switching to display_name_with_default, which is no
        longer escaped.
        """
        return block_metadata_utils.display_name_with_default_escaped(self)
Пример #12
0
    def compute_and_update(self, read_only=False):
        """
        Computes the grade for the given student and course.

        If read_only is True, doesn't save any updates to the grades.
        """
        subsection_grade_factory = SubsectionGradeFactory(
            self.student, self.course, self.course_structure)
        subsections_total = 0
        for chapter_key in self.course_structure.get_children(
                self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            children = self.course_structure.get_children(chapter_key)
            subsections_total += len(children)
            for subsection_key in children:
                chapter_subsection_grades.append(
                    subsection_grade_factory.create(
                        self.course_structure[subsection_key], read_only=True))

            self.chapter_grades.append({
                'display_name':
                block_metadata_utils.display_name_with_default_escaped(
                    chapter),
                'url_name':
                block_metadata_utils.url_name_for_block(chapter),
                'sections':
                chapter_subsection_grades
            })

        total_graded_subsections = sum(
            len(x)
            for x in self.subsection_grade_totals_by_format.itervalues())
        subsections_created = len(
            subsection_grade_factory._unsaved_subsection_grades)  # pylint: disable=protected-access
        subsections_read = subsections_total - subsections_created
        blocks_total = len(self.locations_to_scores)
        if not read_only:
            subsection_grade_factory.bulk_create_unsaved()

        self._signal_listeners_when_grade_computed()
        self._log_event(
            log.warning,
            u"compute_and_update, read_only: {0}, subsections read/created: {1}/{2}, blocks accessed: {3}, total "
            u"graded subsections: {4}".format(
                read_only,
                subsections_read,
                subsections_created,
                blocks_total,
                total_graded_subsections,
            ))
Пример #13
0
    def _compute_block_score(
        self,
        student,
        block_key,
        course_structure,
        scores_client,
        submissions_scores,
        persisted_values,
    ):
        """
        Compute score for the given block. If persisted_values
        is provided, it is used for possible and weight.
        """
        block = course_structure[block_key]

        if getattr(block, 'has_score', False):

            possible = persisted_values.get('possible', None)
            weight = persisted_values.get('weight',
                                          getattr(block, 'weight', None))

            (earned, possible) = get_score(
                student,
                block,
                scores_client,
                submissions_scores,
                weight,
                possible,
            )

            if earned is not None or possible is not None:
                # There's a chance that the value of graded is not the same
                # value when the problem was scored. Since we get the value
                # from the block_structure.
                #
                # Cannot grade a problem with a denominator of 0.
                # TODO: None > 0 is not python 3 compatible.
                block_graded = self._get_explicit_graded(
                    block, course_structure) if possible > 0 else False

                self.locations_to_weighted_scores[block.location] = (
                    Score(
                        earned,
                        possible,
                        block_graded,
                        block_metadata_utils.display_name_with_default_escaped(
                            block),
                        block.location,
                    ),
                    weight,
                )
Пример #14
0
    def __init__(self, subsection):
        self.location = subsection.location
        self.display_name = block_metadata_utils.display_name_with_default_escaped(subsection)
        self.url_name = block_metadata_utils.url_name_for_block(subsection)

        self.format = getattr(subsection, 'format', '')
        self.due = getattr(subsection, 'due', None)
        self.graded = getattr(subsection, 'graded', False)

        self.course_version = getattr(subsection, 'course_version', None)
        self.subtree_edited_timestamp = getattr(subsection, 'subtree_edited_on', None)

        self.graded_total = None  # aggregated grade for all graded problems
        self.all_total = None  # aggregated grade for all problems, regardless of whether they are graded
Пример #15
0
    def __init__(self, subsection):
        self.location = subsection.location
        self.display_name = block_metadata_utils.display_name_with_default_escaped(
            subsection)
        self.url_name = block_metadata_utils.url_name_for_block(subsection)

        self.format = getattr(subsection, 'format', '')
        self.due = getattr(subsection, 'due', None)
        self.graded = getattr(subsection, 'graded', False)
        self.show_correctness = getattr(subsection, 'show_correctness', '')

        self.course_version = getattr(subsection, 'course_version', None)
        self.subtree_edited_timestamp = getattr(subsection,
                                                'subtree_edited_on', None)
Пример #16
0
    def __init__(self, subsection):
        self.location = subsection.location
        self.display_name = block_metadata_utils.display_name_with_default_escaped(subsection)
        self.url_name = block_metadata_utils.url_name_for_block(subsection)

        self.format = getattr(subsection, 'format', '')
        self.due = getattr(subsection, 'due', None)
        self.graded = getattr(subsection, 'graded', False)
        self.show_correctness = getattr(subsection, 'show_correctness', '')

        self.course_version = getattr(subsection, 'course_version', None)
        self.subtree_edited_timestamp = getattr(subsection, 'subtree_edited_on', None)

        self.override = None
Пример #17
0
    def __init__(self, subsection):
        self.location = subsection.location
        self.display_name = block_metadata_utils.display_name_with_default_escaped(subsection)
        self.url_name = block_metadata_utils.url_name_for_block(subsection)

        self.format = getattr(subsection, 'format', '')
        self.due = getattr(subsection, 'due', None)
        self.graded = getattr(subsection, 'graded', False)

        self.course_version = getattr(subsection, 'course_version', None)
        self.subtree_edited_timestamp = getattr(subsection, 'subtree_edited_on', None)

        self.graded_total = None  # aggregated grade for all graded problems
        self.all_total = None  # aggregated grade for all problems, regardless of whether they are graded
        self.locations_to_scores = OrderedDict()  # dict of problem locations to ProblemScore
Пример #18
0
    def _compute_block_score(
            self,
            student,
            block_key,
            course_structure,
            scores_client,
            submissions_scores,
            persisted_values,
    ):
        """
        Compute score for the given block. If persisted_values
        is provided, it is used for possible and weight.
        """
        block = course_structure[block_key]

        if getattr(block, 'has_score', False):

            possible = persisted_values.get('possible', None)
            weight = persisted_values.get('weight', getattr(block, 'weight', None))

            (earned, possible) = get_score(
                student,
                block,
                scores_client,
                submissions_scores,
                weight,
                possible,
            )

            if earned is not None or possible is not None:
                # There's a chance that the value of graded is not the same
                # value when the problem was scored. Since we get the value
                # from the block_structure.
                #
                # Cannot grade a problem with a denominator of 0.
                # TODO: None > 0 is not python 3 compatible.
                block_graded = self._get_explicit_graded(block, course_structure) if possible > 0 else False

                self.locations_to_weighted_scores[block.location] = (
                    Score(
                        earned,
                        possible,
                        block_graded,
                        block_metadata_utils.display_name_with_default_escaped(block),
                        block.location,
                    ),
                    weight,
                )
    def _compute_block_score(
        self,
        student,
        block_key,
        course_structure,
        scores_client,
        submissions_scores,
        persisted_values=None,
    ):
        """
        Compute score for the given block. If persisted_values is provided, it will be used for possible and weight.
        """
        block = course_structure[block_key]

        if getattr(block, 'has_score', False):
            (earned, possible) = get_score(
                student,
                block,
                scores_client,
                submissions_scores,
            )

            # There's a chance that the value of weight is not the same value used when the problem was scored,
            # since we can get the value from either block_structure or CSM/submissions.
            weight = getattr(block, 'weight', None)
            if persisted_values:
                possible = persisted_values.get('possible', possible)
                weight = persisted_values.get('weight', weight)

            if earned is not None or possible is not None:
                # cannot grade a problem with a denominator of 0
                block_graded = block.graded if possible > 0 else False

                self.locations_to_weighted_scores[block.location] = (
                    Score(
                        earned,
                        possible,
                        block_graded,
                        block_metadata_utils.display_name_with_default_escaped(
                            block),
                        block.location,
                    ),
                    weight,
                )
Пример #20
0
    def _compute_block_score(
            self,
            student,
            block_key,
            course_structure,
            scores_client,
            submissions_scores,
            persisted_values=None,
    ):
        """
        Compute score for the given block. If persisted_values is provided, it will be used for possible and weight.
        """
        block = course_structure[block_key]

        if getattr(block, 'has_score', False):
            (earned, possible) = get_score(
                student,
                block,
                scores_client,
                submissions_scores,
            )

            # There's a chance that the value of weight is not the same value used when the problem was scored,
            # since we can get the value from either block_structure or CSM/submissions.
            weight = block.weight
            if persisted_values:
                possible = persisted_values.get('possible', possible)
                weight = persisted_values.get('weight', weight)

            if earned is not None or possible is not None:
                # cannot grade a problem with a denominator of 0
                block_graded = block.graded if possible > 0 else False

                self.locations_to_weighted_scores[block.location] = (
                    Score(
                        earned,
                        possible,
                        block_graded,
                        block_metadata_utils.display_name_with_default_escaped(block),
                        block.location,
                    ),
                    weight,
                )
Пример #21
0
    def chapter_grades(self):
        """
        Returns a list of chapters, each containing its subsection grades,
        display name, and url name.
        """
        chapter_grades = []
        for chapter_key in self.course_structure.get_children(self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            children = self.course_structure.get_children(chapter_key)
            for subsection_key in children:
                chapter_subsection_grades.append(
                    self._subsection_grade_factory.create(self.course_structure[subsection_key], read_only=True)
                )

            chapter_grades.append({
                'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
                'url_name': block_metadata_utils.url_name_for_block(chapter),
                'sections': chapter_subsection_grades
            })
        return chapter_grades
Пример #22
0
    def compute_and_update(self, read_only=False):
        """
        Computes the grade for the given student and course.

        If read_only is True, doesn't save any updates to the grades.
        """
        subsection_grade_factory = SubsectionGradeFactory(self.student, self.course, self.course_structure)
        for chapter_key in self.course_structure.get_children(self.course.location):
            chapter = self.course_structure[chapter_key]
            chapter_subsection_grades = []
            for subsection_key in self.course_structure.get_children(chapter_key):
                chapter_subsection_grades.append(
                    subsection_grade_factory.create(self.course_structure[subsection_key], read_only=True)
                )

            self.chapter_grades.append({
                'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
                'url_name': block_metadata_utils.url_name_for_block(chapter),
                'sections': chapter_subsection_grades
            })

        subsections_total = sum(len(x) for x in self.subsection_grade_totals_by_format.itervalues())
        subsections_read = len(subsection_grade_factory._unsaved_subsection_grades)  # pylint: disable=protected-access
        subsections_created = subsections_total - subsections_read
        blocks_total = len(self.locations_to_scores)
        if not read_only:
            subsection_grade_factory.bulk_create_unsaved()

        self._signal_listeners_when_grade_computed()
        self._log_event(
            log.warning,
            u"compute_and_update, read_only: {0}, subsections read/created: {1}/{2}, blocks accessed: {3}".format(
                read_only,
                subsections_read,
                subsections_created,
                blocks_total,
            )
        )
Пример #23
0
    def compute(self):
        """
        Computes the grade for the given student and course.
        """
        subsection_grade_factory = SubsectionGradeFactory(self.student)
        for chapter_key in self.course_structure.get_children(self.course.location):
            chapter = self.course_structure[chapter_key]
            subsection_grades = []
            for subsection_key in self.course_structure.get_children(chapter_key):
                subsection_grades.append(
                    subsection_grade_factory.create(
                        self.course_structure[subsection_key],
                        self.course_structure, self.course
                    )
                )

            self.chapter_grades.append({
                'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
                'url_name': block_metadata_utils.url_name_for_block(chapter),
                'sections': subsection_grades
            })

        self._signal_listeners_when_grade_computed()
Пример #24
0
    def compute(self, student, course_structure, scores_client, submissions_scores):
        """
        Compute the grade of this subsection for the given student and course.
        """
        for descendant_key in course_structure.post_order_traversal(
                filter_func=possibly_scored,
                start_node=self.location,
        ):
            descendant = course_structure[descendant_key]

            if not getattr(descendant, 'has_score', False):
                continue

            (earned, possible) = get_score(
                student,
                descendant,
                scores_client,
                submissions_scores,
            )
            if earned is None and possible is None:
                continue

            # cannot grade a problem with a denominator of 0
            descendant_graded = descendant.graded if possible > 0 else False

            self.locations_to_scores[descendant.location] = Score(
                earned,
                possible,
                descendant_graded,
                block_metadata_utils.display_name_with_default_escaped(descendant),
                descendant.location,
            )

        self.all_total, self.graded_total = graders.aggregate_scores(
            self.scores, self.display_name,
        )
Пример #25
0
def _calculate_totaled_scores(
        student,
        grading_context_result,
        submissions_scores,
        scores_client,
        keep_raw_scores,
):
    """
    Returns a tuple of totaled scores and raw scores, which can be passed to the grader.
    """
    raw_scores = []
    totaled_scores = {}
    for section_format, sections in grading_context_result['all_graded_sections'].iteritems():
        format_scores = []
        for section_info in sections:
            section = section_info['section_block']
            section_name = block_metadata_utils.display_name_with_default(section)

            with outer_atomic():
                # 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.
                should_grade_section = any(
                    unicode(descendant.location) in submissions_scores
                    for descendant in section_info['scored_descendants']
                )

                if not should_grade_section:
                    should_grade_section = any(
                        descendant.location in scores_client
                        for descendant in section_info['scored_descendants']
                    )

                # 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 = []

                    for descendant in section_info['scored_descendants']:

                        (correct, total) = get_score(
                            student,
                            descendant,
                            scores_client,
                            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 = descendant.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,
                                block_metadata_utils.display_name_with_default_escaped(descendant),
                                descendant.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.location)
                    )

        totaled_scores[section_format] = format_scores

    return totaled_scores, raw_scores
Пример #26
0
def _progress_summary(student, course, course_structure=None):
    """
    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.
    - None if the student does not have access to load the course module.

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

    """
    if course_structure is None:
        course_structure = get_course_blocks(student, course.location)
    if not len(course_structure):
        return None
    scorable_locations = [block_key for block_key in course_structure if possibly_scored(block_key)]

    with outer_atomic():
        scores_client = ScoresClient.create_for_locations(course.id, student.id, scorable_locations)

    # We need to import this here to avoid a circular dependency of the form:
    # XBlock --> submissions --> Django Rest Framework error strings -->
    # Django translation --> ... --> courseware --> submissions
    from submissions import api as sub_api  # installed from the edx-submissions repository
    with outer_atomic():
        submissions_scores = sub_api.get_scores(
            unicode(course.id), anonymous_id_for_user(student, course.id)
        )

    # Check for gated content
    gated_content = gating_api.get_gated_content(course, student)

    chapters = []
    locations_to_weighted_scores = {}

    for chapter_key in course_structure.get_children(course_structure.root_block_usage_key):
        chapter = course_structure[chapter_key]
        sections = []
        for section_key in course_structure.get_children(chapter_key):
            if unicode(section_key) in gated_content:
                continue

            section = course_structure[section_key]

            graded = getattr(section, 'graded', False)
            scores = []

            for descendant_key in course_structure.post_order_traversal(
                    filter_func=possibly_scored,
                    start_node=section_key,
            ):
                descendant = course_structure[descendant_key]

                (correct, total) = get_score(
                    student,
                    descendant,
                    scores_client,
                    submissions_scores,
                )
                if correct is None and total is None:
                    continue

                weighted_location_score = Score(
                    correct,
                    total,
                    graded,
                    block_metadata_utils.display_name_with_default_escaped(descendant),
                    descendant.location
                )

                scores.append(weighted_location_score)
                locations_to_weighted_scores[descendant.location] = weighted_location_score

            escaped_section_name = block_metadata_utils.display_name_with_default_escaped(section)
            section_total, _ = graders.aggregate_scores(scores, escaped_section_name)

            sections.append({
                'display_name': escaped_section_name,
                'url_name': block_metadata_utils.url_name_for_block(section),
                'scores': scores,
                'section_total': section_total,
                'format': getattr(section, 'format', ''),
                'due': getattr(section, 'due', None),
                'graded': graded,
            })

        chapters.append({
            'course': course.display_name_with_default_escaped,
            'display_name': block_metadata_utils.display_name_with_default_escaped(chapter),
            'url_name': block_metadata_utils.url_name_for_block(chapter),
            'sections': sections
        })

    return ProgressSummary(chapters, locations_to_weighted_scores, course_structure.get_children)
Пример #27
0
def _calculate_totaled_scores(
        student,
        grading_context_result,
        submissions_scores,
        scores_client,
        keep_raw_scores,
):
    """
    Returns the totaled scores, which can be passed to the grader.
    """
    raw_scores = []
    totaled_scores = {}
    for section_format, sections in grading_context_result['all_graded_sections'].iteritems():
        format_scores = []
        for section_info in sections:
            section = section_info['section_block']
            section_name = block_metadata_utils.display_name_with_default(section)

            with outer_atomic():
                # 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.
                should_grade_section = any(
                    unicode(descendant.location) in submissions_scores
                    for descendant in section_info['scored_descendants']
                )

                if not should_grade_section:
                    should_grade_section = any(
                        descendant.location in scores_client
                        for descendant in section_info['scored_descendants']
                    )

                # 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 = []

                    for descendant in section_info['scored_descendants']:

                        (correct, total) = get_score(
                            student,
                            descendant,
                            scores_client,
                            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 = descendant.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,
                                block_metadata_utils.display_name_with_default_escaped(descendant),
                                descendant.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.location)
                    )

        totaled_scores[section_format] = format_scores

    return totaled_scores, raw_scores
Пример #28
0
def summary(student, course, course_structure=None):
    """
    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.
    - None if the student does not have access to load the course module.

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

    """
    if course_structure is None:
        course_structure = get_course_blocks(student, course.location)
    if not len(course_structure):
        return ProgressSummary()
    scorable_locations = [
        block_key for block_key in course_structure
        if possibly_scored(block_key)
    ]

    with outer_atomic():
        scores_client = ScoresClient.create_for_locations(
            course.id, student.id, scorable_locations)

    # We need to import this here to avoid a circular dependency of the form:
    # XBlock --> submissions --> Django Rest Framework error strings -->
    # Django translation --> ... --> courseware --> submissions
    from submissions import api as sub_api  # installed from the edx-submissions repository
    with outer_atomic():
        submissions_scores = sub_api.get_scores(
            unicode(course.id), anonymous_id_for_user(student, course.id))

    # Check for gated content
    gated_content = gating_api.get_gated_content(course, student)

    chapters = []
    locations_to_weighted_scores = {}

    for chapter_key in course_structure.get_children(
            course_structure.root_block_usage_key):
        chapter = course_structure[chapter_key]
        sections = []
        for section_key in course_structure.get_children(chapter_key):
            if unicode(section_key) in gated_content:
                continue

            section = course_structure[section_key]

            graded = getattr(section, 'graded', False)
            scores = []

            for descendant_key in course_structure.post_order_traversal(
                    filter_func=possibly_scored,
                    start_node=section_key,
            ):
                descendant = course_structure[descendant_key]

                (correct, total) = get_score(
                    student,
                    descendant,
                    scores_client,
                    submissions_scores,
                )
                if correct is None and total is None:
                    continue

                weighted_location_score = Score(
                    correct, total, graded,
                    block_metadata_utils.display_name_with_default_escaped(
                        descendant), descendant.location)

                scores.append(weighted_location_score)
                locations_to_weighted_scores[
                    descendant.location] = weighted_location_score

            escaped_section_name = block_metadata_utils.display_name_with_default_escaped(
                section)
            section_total, _ = graders.aggregate_scores(
                scores, escaped_section_name)

            sections.append({
                'display_name':
                escaped_section_name,
                'url_name':
                block_metadata_utils.url_name_for_block(section),
                'scores':
                scores,
                'section_total':
                section_total,
                'format':
                getattr(section, 'format', ''),
                'due':
                getattr(section, 'due', None),
                'graded':
                graded,
            })

        chapters.append({
            'course':
            course.display_name_with_default_escaped,
            'display_name':
            block_metadata_utils.display_name_with_default_escaped(chapter),
            'url_name':
            block_metadata_utils.url_name_for_block(chapter),
            'sections':
            sections
        })

    return ProgressSummary(chapters, locations_to_weighted_scores,
                           course_structure.get_children)
Пример #29
0
def get_score(submissions_scores, csm_scores, persisted_block, block):
    """
    Returns the score for a problem, as a ProblemScore object.  It is
    assumed that the provided storages have already been filtered for
    a single user in question and have user-specific values.

    The score is retrieved from the provided storages in the following
    order of precedence.  If no value for the block is found in a
    given storage, the next storage is checked.

    submissions_scores (dict of {unicode(usage_key): (earned, possible)}):

        A python dictionary of serialized UsageKeys to (earned, possible)
        tuples. These values, retrieved using the Submissions API by the
        caller (already filtered for the user and course), take precedence
        above all other score storages.

        When the score is found in this storage, it implies the user's score
        for the block was persisted via the submissions API. Typically, this API
        is used by ORA.

        The returned score includes valid values for:
            weighted_earned
            weighted_possible
            graded - retrieved from the persisted block, if found, else from
                the latest block content.

        Note: raw_earned and raw_possible are not required when submitting scores
        via the submissions API, so those values (along with the unused weight)
        are invalid and irrelevant.

    csm_scores (ScoresClient):

        The ScoresClient object (already filtered for the user and course),
        from which a courseware.models.StudentModule object can be retrieved for
        the block.

        When the score is found from this storage, it implies the user's score
        for the block was persisted in the Courseware Student Module. Typically,
        this storage is used for all CAPA problems, including scores calculated
        by external graders.

        The returned score includes valid values for:
            raw_earned, raw_possible - retrieved from CSM
            weighted_earned, weighted_possible - calculated from the raw scores and weight
            weight, graded - retrieved from the persisted block, if found,
                else from the latest block content

    persisted_block (.models.BlockRecord):
        The block values as found in the grades persistence layer. These values
        are used only if not found from an earlier storage, and take precedence
        over values stored within the latest content-version of the block.

        When the score is found from this storage, it implies the user has not
        yet attempted this problem, but the user's grade _was_ persisted.

        The returned score includes valid values for:
            raw_earned - will equal 0.0 since the user's score was not found from
                earlier storages
            raw_possible - retrieved from the persisted block
            weighted_earned, weighted_possible - calculated from the raw scores and weight
            weight, graded - retrieved from the persisted block

    block (block_structure.BlockData):
        Values from the latest content-version of the block are used only if
        they were not available from a prior storage.

        When the score is found from this storage, it implies the user has not
        yet attempted this problem and the user's grade was _not_ yet persisted.

        The returned score includes valid values for:
            raw_earned - will equal 0.0 since the user's score was not found from
                earlier storages
            raw_possible - retrieved from the latest block content
            weighted_earned, weighted_possible - calculated from the raw scores and weight
            weight, graded - retrieved from the latest block content
    """
    weight = _get_weight_from_block(persisted_block, block)

    # Priority order for retrieving the scores:
    # submissions API -> CSM -> grades persisted block -> latest block content
    raw_earned, raw_possible, weighted_earned, weighted_possible, attempted = (
        _get_score_from_submissions(submissions_scores, block) or
        _get_score_from_csm(csm_scores, block, weight) or
        _get_score_from_persisted_or_latest_block(persisted_block, block, weight)
    )

    if weighted_possible is None or weighted_earned is None:
        return None

    else:
        has_valid_denominator = weighted_possible > 0.0
        graded = _get_graded_from_block(persisted_block, block) if has_valid_denominator else False

        return ProblemScore(
            raw_earned,
            raw_possible,
            weighted_earned,
            weighted_possible,
            weight,
            graded,
            display_name=display_name_with_default_escaped(block),
            module_id=block.location,
            attempted=attempted,
        )