Exemple #1
0
def score_published_handler(sender, block, user, raw_earned, raw_possible,
                            only_if_higher, **kwargs):  # pylint: disable=unused-argument
    """
    Handles whenever a block's score is published.
    Returns whether the score was actually updated.
    """
    update_score = True
    if only_if_higher:
        previous_score = get_score(user.id, block.location)

        if previous_score is not None:
            prev_raw_earned, prev_raw_possible = (previous_score.grade,
                                                  previous_score.max_grade)

            if not is_score_higher_or_equal(prev_raw_earned, prev_raw_possible,
                                            raw_earned, raw_possible):
                update_score = False
                log.warning(
                    u"Grades: Rescore is not higher than previous: "
                    u"user: {}, block: {}, previous: {}/{}, new: {}/{} ".
                    format(
                        user,
                        block.location,
                        prev_raw_earned,
                        prev_raw_possible,
                        raw_earned,
                        raw_possible,
                    ))

    if update_score:
        # Set the problem score in CSM.
        score_modified_time = set_score(user.id, block.location, raw_earned,
                                        raw_possible)

        # Set the problem score on the xblock.
        if isinstance(block, ScorableXBlockMixin):
            block.set_score(
                Score(raw_earned=raw_earned, raw_possible=raw_possible))

        # Fire a signal (consumed by enqueue_subsection_update, below)
        PROBLEM_RAW_SCORE_CHANGED.send(
            sender=None,
            raw_earned=raw_earned,
            raw_possible=raw_possible,
            weight=getattr(block, 'weight', None),
            user_id=user.id,
            course_id=six.text_type(block.location.course_key),
            usage_id=six.text_type(block.location),
            only_if_higher=only_if_higher,
            modified=score_modified_time,
            score_db_table=ScoreDatabaseTableEnum.courseware_student_module,
            score_deleted=kwargs.get('score_deleted', False),
            grader_response=kwargs.get('grader_response', False))
    return update_score
Exemple #2
0
def _has_db_updated_with_new_score(self, scored_block_usage_key, **kwargs):
    """
    Returns whether the database has been updated with the
    expected new score values for the given problem and user.
    """
    if kwargs[
            'score_db_table'] == ScoreDatabaseTableEnum.courseware_student_module:
        score = get_score(kwargs['user_id'], scored_block_usage_key)
        found_modified_time = score.modified if score is not None else None

    elif kwargs['score_db_table'] == ScoreDatabaseTableEnum.submissions:
        score = sub_api.get_score({
            "student_id":
            kwargs['anonymous_user_id'],
            "course_id":
            six.text_type(scored_block_usage_key.course_key),
            "item_id":
            six.text_type(scored_block_usage_key),
            "item_type":
            scored_block_usage_key.block_type,
        })
        found_modified_time = score['created_at'] if score is not None else None
    else:
        assert kwargs['score_db_table'] == ScoreDatabaseTableEnum.overrides
        from . import api
        score = api.get_subsection_grade_override(
            user_id=kwargs['user_id'],
            course_key_or_id=kwargs['course_id'],
            usage_key_or_id=kwargs['usage_id'])
        found_modified_time = score.modified if score is not None else None

    if score is None:
        # score should be None only if it was deleted.
        # Otherwise, it hasn't yet been saved.
        db_is_updated = kwargs['score_deleted']
    else:
        db_is_updated = found_modified_time >= from_timestamp(
            kwargs['expected_modified_time'])

    if not db_is_updated:
        log.info(
            u"Grades: tasks._has_database_updated_with_new_score is False. Task ID: {}. Kwargs: {}. Found "
            u"modified time: {}".format(
                self.request.id,
                kwargs,
                found_modified_time,
            ))

    return db_is_updated
Exemple #3
0
    def test_scores_persisted(self):
        """
        Test that a block's emitted scores are cached in StudentModule

        In the future if there is a REST API to retrieve individual block's
        scores, that should be used instead of checking StudentModule directly.
        """
        block_id = library_api.create_library_block(self.library.key,
                                                    "problem",
                                                    "scored_problem").usage_key
        new_olx = """
        <problem display_name="New Multi Choice Question" max_attempts="5">
            <multiplechoiceresponse>
                <p>This is a normal capa problem. It has "maximum attempts" set to **5**.</p>
                <label>Blockstore is designed to store.</label>
                <choicegroup type="MultipleChoice">
                    <choice correct="false">XBlock metadata only</choice>
                    <choice correct="true">XBlock data/metadata and associated static asset files</choice>
                    <choice correct="false">Static asset files for XBlocks and courseware</choice>
                    <choice correct="false">XModule metadata only</choice>
                </choicegroup>
            </multiplechoiceresponse>
        </problem>
        """.strip()
        library_api.set_library_block_olx(block_id, new_olx)
        library_api.publish_changes(self.library.key)

        # Now view the problem as Alice:
        client = APIClient()
        client.login(username=self.student_a.username, password='******')
        student_view_result = client.get(
            URL_BLOCK_RENDER_VIEW.format(block_key=block_id,
                                         view_name='student_view'))
        problem_key = "input_{}_2_1".format(block_id)
        self.assertIn(problem_key, student_view_result.data["content"])
        # And submit a wrong answer:
        result = client.get(
            URL_BLOCK_GET_HANDLER_URL.format(block_key=block_id,
                                             handler_name='xmodule_handler'))
        problem_check_url = result.data["handler_url"] + 'problem_check'

        submit_result = client.post(problem_check_url,
                                    data={problem_key: "choice_3"})
        self.assertEqual(submit_result.status_code, 200)
        submit_data = json.loads(submit_result.content)
        self.assertDictContainsSubset(
            {
                "current_score": 0,
                "total_possible": 1,
                "attempts_used": 1,
            }, submit_data)

        # Now test that the score is also persisted in StudentModule:
        # If we add a REST API to get an individual block's score, that should be checked instead of StudentModule.
        sm = get_score(self.student_a, block_id)
        self.assertEqual(sm.grade, 0)
        self.assertEqual(sm.max_grade, 1)

        # And submit a correct answer:
        submit_result = client.post(problem_check_url,
                                    data={problem_key: "choice_1"})
        self.assertEqual(submit_result.status_code, 200)
        submit_data = json.loads(submit_result.content)
        self.assertDictContainsSubset(
            {
                "current_score": 1,
                "total_possible": 1,
                "attempts_used": 2,
            }, submit_data)
        # Now test that the score is also updated in StudentModule:
        # If we add a REST API to get an individual block's score, that should be checked instead of StudentModule.
        sm = get_score(self.student_a, block_id)
        self.assertEqual(sm.grade, 1)
        self.assertEqual(sm.max_grade, 1)