Example #1
0
def _fire_score_changed_for_block(course_id, student, block, module_state_key):
    """
    Fires a SCORE_CHANGED event for the given module. The earned points are
    always zero. We must retrieve the possible points from the XModule, as
    noted below.
    """
    if block and block.has_score:
        cache = FieldDataCache.cache_for_descriptor_descendents(
            course_id=course_id, user=student, descriptor=block, depth=0)
        # For implementation reasons, we need to pull the max_score from the XModule,
        # even though the data is not user-specific.  Here we bind the data to the
        # current user.
        request = crum.get_current_request()
        module = get_module_for_descriptor(user=student,
                                           request=request,
                                           descriptor=block,
                                           field_data_cache=cache,
                                           course_key=course_id)
        max_score = module.max_score()
        if max_score is None:
            return
        else:
            points_earned, points_possible = weighted_score(
                0, max_score, getattr(module, 'weight', None))
    else:
        points_earned, points_possible = 0, 0
    SCORE_CHANGED.send(sender=None,
                       points_possible=points_possible,
                       points_earned=points_earned,
                       user=student,
                       course_id=unicode(course_id),
                       usage_id=unicode(module_state_key))
Example #2
0
def _fire_score_changed_for_block(course_id, student, block, module_state_key):
    """
    Fires a PROBLEM_SCORE_CHANGED event for the given module. The earned points are
    always zero. We must retrieve the possible points from the XModule, as
    noted below.
    """
    if block and block.has_score:
        max_score = block.max_score()
        if max_score is None:
            return
        else:
            points_earned, points_possible = weighted_score(
                0, max_score, getattr(block, 'weight', None))
    else:
        points_earned, points_possible = 0, 0

    PROBLEM_SCORE_CHANGED.send(
        sender=None,
        points_possible=points_possible,
        points_earned=points_earned,
        user_id=student.id,
        course_id=unicode(course_id),
        usage_id=unicode(module_state_key),
        score_deleted=True,
    )
Example #3
0
def _fire_score_changed_for_block(course_id, student, block, module_state_key):
    """
    Fires a PROBLEM_SCORE_CHANGED event for the given module. The earned points are
    always zero. We must retrieve the possible points from the XModule, as
    noted below.
    """
    if block and block.has_score:
        cache = FieldDataCache.cache_for_descriptor_descendents(
            course_id=course_id,
            user=student,
            descriptor=block,
            depth=0
        )
        # For implementation reasons, we need to pull the max_score from the XModule,
        # even though the data is not user-specific.  Here we bind the data to the
        # current user.
        request = crum.get_current_request()
        module = get_module_for_descriptor(
            user=student,
            request=request,
            descriptor=block,
            field_data_cache=cache,
            course_key=course_id
        )
        max_score = module.max_score()
        if max_score is None:
            return
        else:
            points_earned, points_possible = weighted_score(0, max_score, getattr(module, 'weight', None))
    else:
        points_earned, points_possible = 0, 0
    PROBLEM_SCORE_CHANGED.send(
        sender=None,
        points_possible=points_possible,
        points_earned=points_earned,
        user_id=student.id,
        course_id=unicode(course_id),
        usage_id=unicode(module_state_key)
    )
Example #4
0
def _fire_score_changed_for_block(course_id, student, block, module_state_key):
    """
    Fires a PROBLEM_SCORE_CHANGED event for the given module. The earned points are
    always zero. We must retrieve the possible points from the XModule, as
    noted below.
    """
    if block and block.has_score:
        max_score = block.max_score()
        if max_score is None:
            return
        else:
            points_earned, points_possible = weighted_score(0, max_score, getattr(block, 'weight', None))
    else:
        points_earned, points_possible = 0, 0

    PROBLEM_SCORE_CHANGED.send(
        sender=None,
        points_possible=points_possible,
        points_earned=points_earned,
        user_id=student.id,
        course_id=unicode(course_id),
        usage_id=unicode(module_state_key),
        score_deleted=True,
    )
Example #5
0
 def test_assert_on_invalid_r_possible(self):
     with self.assertRaises(AssertionError):
         scores.weighted_score(raw_earned=1, raw_possible=None, weight=1)
Example #6
0
 def test_computed(self, raw_earned, raw_possible, weight, expected_score):
     self.assertEquals(
         scores.weighted_score(raw_earned, raw_possible, weight),
         expected_score,
     )
Example #7
0
 def test_cannot_compute(self, raw_earned, raw_possible, weight):
     self.assertEquals(
         scores.weighted_score(raw_earned, raw_possible, weight),
         (raw_earned, raw_possible),
     )
Example #8
0
 def test_computed(self, raw_earned, raw_possible, weight, expected_score):
     assert scores.weighted_score(raw_earned, raw_possible,
                                  weight) == expected_score
Example #9
0
 def test_cannot_compute(self, raw_earned, raw_possible, weight):
     assert scores.weighted_score(raw_earned, raw_possible,
                                  weight) == (raw_earned, raw_possible)
Example #10
0
 def test_assert_on_invalid_r_possible(self):
     with self.assertRaises(AssertionError):
         scores.weighted_score(raw_earned=1, raw_possible=None, weight=1)
Example #11
0
 def test_computed(self, raw_earned, raw_possible, weight, expected_score):
     self.assertEquals(
         scores.weighted_score(raw_earned, raw_possible, weight),
         expected_score,
     )
Example #12
0
 def test_cannot_compute(self, raw_earned, raw_possible, weight):
     self.assertEquals(
         scores.weighted_score(raw_earned, raw_possible, weight),
         (raw_earned, raw_possible),
     )
Example #13
0
def rescore_problem_module_state(xmodule_instance_args, module_descriptor,
                                 student_module, task_input):
    '''
    Takes an XModule descriptor and a corresponding StudentModule object, and
    performs rescoring on the student's problem submission.

    Throws exceptions if the rescoring is fatal and should be aborted if in a loop.
    In particular, raises UpdateProblemModuleStateError if module fails to instantiate,
    or if the module doesn't support rescoring.

    Returns True if problem was successfully rescored for the given student, and False
    if problem encountered some kind of error in rescoring.
    '''
    # unpack the StudentModule:
    course_id = student_module.course_id
    student = student_module.student
    usage_key = student_module.module_state_key

    with modulestore().bulk_operations(course_id):
        course = get_course_by_id(course_id)
        # TODO: Here is a call site where we could pass in a loaded course.  I
        # think we certainly need it since grading is happening here, and field
        # overrides would be important in handling that correctly
        instance = _get_module_instance_for_task(course_id,
                                                 student,
                                                 module_descriptor,
                                                 xmodule_instance_args,
                                                 grade_bucket_type='rescore',
                                                 course=course)

        if instance is None:
            # Either permissions just changed, or someone is trying to be clever
            # and load something they shouldn't have access to.
            msg = "No module {loc} for student {student}--access denied?".format(
                loc=usage_key, student=student)
            TASK_LOG.warning(msg)
            return UPDATE_STATUS_FAILED

        # TODO: (TNL-6594)  Remove this switch once rescore_problem support
        # once CAPA uses ScorableXBlockMixin.
        for method in ['rescore', 'rescore_problem']:
            rescore_method = getattr(instance, method, None)
            if rescore_method is not None:
                break
        else:  # for-else: Neither method exists on the block.
            # This should not happen, since it should be already checked in the
            # caller, but check here to be sure.
            msg = "Specified problem does not support rescoring."
            raise UpdateProblemModuleStateError(msg)

        # TODO: Remove the first part of this if-else with TNL-6594
        # We check here to see if the problem has any submissions. If it does not, we don't want to rescore it
        if hasattr(instance, "done"):
            if not instance.done:
                return UPDATE_STATUS_SKIPPED
        elif not instance.has_submitted_answer():
            return UPDATE_STATUS_SKIPPED

        # Set the tracking info before this call, because it makes downstream
        # calls that create events.  We retrieve and store the id here because
        # the request cache will be erased during downstream calls.
        event_transaction_id = create_new_event_transaction_id()
        set_event_transaction_type(GRADES_RESCORE_EVENT_TYPE)

        result = rescore_method(only_if_higher=task_input['only_if_higher'])
        instance.save()

        if result is None or result.get(u'success') in {
                u'correct', u'incorrect'
        }:
            TASK_LOG.debug(
                u"successfully processed rescore call for course %(course)s, problem %(loc)s "
                u"and student %(student)s",
                dict(course=course_id, loc=usage_key, student=student))

            if result is not None:  # Only for CAPA. This will get moved to the grade handler.
                new_weighted_earned, new_weighted_possible = weighted_score(
                    result['new_raw_earned'] if result else None,
                    result['new_raw_possible'] if result else None,
                    module_descriptor.weight,
                )

                # TODO: remove this context manager after completion of AN-6134
                context = course_context_from_course_id(course_id)
                with tracker.get_tracker().context(GRADES_RESCORE_EVENT_TYPE,
                                                   context):
                    tracker.emit(
                        unicode(GRADES_RESCORE_EVENT_TYPE), {
                            'course_id':
                            unicode(course_id),
                            'user_id':
                            unicode(student.id),
                            'problem_id':
                            unicode(usage_key),
                            'new_weighted_earned':
                            new_weighted_earned,
                            'new_weighted_possible':
                            new_weighted_possible,
                            'only_if_higher':
                            task_input['only_if_higher'],
                            'instructor_id':
                            unicode(xmodule_instance_args['request_info']
                                    ['user_id']),
                            'event_transaction_id':
                            unicode(event_transaction_id),
                            'event_transaction_type':
                            unicode(GRADES_RESCORE_EVENT_TYPE),
                        })
            return UPDATE_STATUS_SUCCEEDED
        else:
            TASK_LOG.warning(
                u"error processing rescore call for course %(course)s, problem %(loc)s "
                u"and student %(student)s: %(msg)s",
                dict(msg=result.get('success', result),
                     course=course_id,
                     loc=usage_key,
                     student=student))
            return UPDATE_STATUS_FAILED