Ejemplo n.º 1
0
    def _create_override(self, request_user, subsection_grade_model, **override_data):
        """
        Helper method to create a `PersistentSubsectionGradeOverride` object
        and send a `SUBSECTION_OVERRIDE_CHANGED` signal.
        """
        override = PersistentSubsectionGradeOverride.update_or_create_override(
            requesting_user=request_user,
            subsection_grade_model=subsection_grade_model,
            feature=grades_constants.GradeOverrideFeatureEnum.gradebook,
            **override_data
        )

        set_event_transaction_type(grades_events.SUBSECTION_GRADE_CALCULATED)
        create_new_event_transaction_id()

        recalculate_subsection_grade_v3.apply(
            kwargs=dict(
                user_id=subsection_grade_model.user_id,
                anonymous_user_id=None,
                course_id=text_type(subsection_grade_model.course_id),
                usage_id=text_type(subsection_grade_model.usage_key),
                only_if_higher=False,
                expected_modified_time=to_timestamp(override.modified),
                score_deleted=False,
                event_transaction_id=six.text_type(get_event_transaction_id()),
                event_transaction_type=six.text_type(get_event_transaction_type()),
                score_db_table=grades_constants.ScoreDatabaseTableEnum.overrides,
                force_update_subsections=True,
            )
        )
        # Emit events to let our tracking system to know we updated subsection grade
        grades_events.subsection_grade_calculated(subsection_grade_model)
        return override
Ejemplo n.º 2
0
def grade_updated(**kwargs):
    """
    Emits the appropriate grade-related event after checking for which
    event-transaction is active.

    Emits a problem.submitted event only if there is no current event
    transaction type, i.e. we have not reached this point in the code via
    an outer event type (such as problem.rescored or score_overridden).
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            six.text_type(PROBLEM_SUBMITTED_EVENT_TYPE),
            {
                'user_id': six.text_type(kwargs['user_id']),
                'course_id': six.text_type(kwargs['course_id']),
                'problem_id': six.text_type(kwargs['usage_id']),
                'event_transaction_id': six.text_type(root_id),
                'event_transaction_type': six.text_type(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            }
        )

    elif root_type in [GRADES_RESCORE_EVENT_TYPE, GRADES_OVERRIDE_EVENT_TYPE]:
        current_user = get_current_user()
        instructor_id = getattr(current_user, 'id', None)
        tracker.emit(
            six.text_type(root_type),
            {
                'course_id': six.text_type(kwargs['course_id']),
                'user_id': six.text_type(kwargs['user_id']),
                'problem_id': six.text_type(kwargs['usage_id']),
                'new_weighted_earned': kwargs.get('weighted_earned'),
                'new_weighted_possible': kwargs.get('weighted_possible'),
                'only_if_higher': kwargs.get('only_if_higher'),
                'instructor_id': six.text_type(instructor_id),
                'event_transaction_id': six.text_type(get_event_transaction_id()),
                'event_transaction_type': six.text_type(root_type),
            }
        )

    elif root_type in [SUBSECTION_OVERRIDE_EVENT_TYPE]:
        tracker.emit(
            six.text_type(root_type),
            {
                'course_id': six.text_type(kwargs['course_id']),
                'user_id': six.text_type(kwargs['user_id']),
                'problem_id': six.text_type(kwargs['usage_id']),
                'only_if_higher': kwargs.get('only_if_higher'),
                'override_deleted': kwargs.get('score_deleted', False),
                'event_transaction_id': six.text_type(get_event_transaction_id()),
                'event_transaction_type': six.text_type(root_type),
            }
        )
Ejemplo n.º 3
0
def subsection_grade_calculated(subsection_grade):
    """
    Emits an edx.grades.subsection.grade_calculated event
    with data from the passed subsection_grade.
    """
    event_name = SUBSECTION_GRADE_CALCULATED
    context = contexts.course_context_from_course_id(subsection_grade.course_id)
    # TODO (AN-6134): remove this context manager
    with tracker.get_tracker().context(event_name, context):
        tracker.emit(
            event_name,
            {
                'user_id': unicode(subsection_grade.user_id),
                'course_id': unicode(subsection_grade.course_id),
                'block_id': unicode(subsection_grade.usage_key),
                'course_version': unicode(subsection_grade.course_version),
                'weighted_total_earned': subsection_grade.earned_all,
                'weighted_total_possible': subsection_grade.possible_all,
                'weighted_graded_earned': subsection_grade.earned_graded,
                'weighted_graded_possible': subsection_grade.possible_graded,
                'first_attempted': unicode(subsection_grade.first_attempted),
                'subtree_edited_timestamp': unicode(subsection_grade.subtree_edited_timestamp),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(get_event_transaction_type()),
                'visible_blocks_hash': unicode(subsection_grade.visible_blocks_id),
            }
        )
Ejemplo n.º 4
0
def subsection_grade_calculated(subsection_grade):
    """
    Emits an edx.grades.subsection.grade_calculated event
    with data from the passed subsection_grade.
    """
    event_name = SUBSECTION_GRADE_CALCULATED
    context = contexts.course_context_from_course_id(subsection_grade.course_id)
    # TODO (AN-6134): remove this context manager
    with tracker.get_tracker().context(event_name, context):
        tracker.emit(
            event_name,
            {
                'user_id': six.text_type(subsection_grade.user_id),
                'course_id': six.text_type(subsection_grade.course_id),
                'block_id': six.text_type(subsection_grade.usage_key),
                'course_version': six.text_type(subsection_grade.course_version),
                'weighted_total_earned': subsection_grade.earned_all,
                'weighted_total_possible': subsection_grade.possible_all,
                'weighted_graded_earned': subsection_grade.earned_graded,
                'weighted_graded_possible': subsection_grade.possible_graded,
                'first_attempted': six.text_type(subsection_grade.first_attempted),
                'subtree_edited_timestamp': six.text_type(subsection_grade.subtree_edited_timestamp),
                'event_transaction_id': six.text_type(get_event_transaction_id()),
                'event_transaction_type': six.text_type(get_event_transaction_type()),
                'visible_blocks_hash': six.text_type(subsection_grade.visible_blocks_id),
            }
        )
Ejemplo n.º 5
0
def _emit_problem_submitted_event(kwargs):
    """
    Emits a problem submitted event only if
    there is no current event transaction type,
    i.e. we have not reached this point in the
    code via a rescore or student state deletion.
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
            {
                'user_id': unicode(kwargs['user_id']),
                'course_id': unicode(kwargs['course_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'event_transaction_id': unicode(root_id),
                'event_transaction_type': unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            }
        )
Ejemplo n.º 6
0
def grade_updated(**kwargs):
    """
    Emits the appropriate grade-related event after checking for which
    event-transaction is active.

    Emits a problem.submitted event only if there is no current event
    transaction type, i.e. we have not reached this point in the code via
    an outer event type (such as problem.rescored or score_overridden).
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
            {
                'user_id': unicode(kwargs['user_id']),
                'course_id': unicode(kwargs['course_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'event_transaction_id': unicode(root_id),
                'event_transaction_type': unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            }
        )

    elif root_type in [GRADES_RESCORE_EVENT_TYPE, GRADES_OVERRIDE_EVENT_TYPE]:
        current_user = get_current_user()
        instructor_id = getattr(current_user, 'id', None)
        tracker.emit(
            unicode(root_type),
            {
                'course_id': unicode(kwargs['course_id']),
                'user_id': unicode(kwargs['user_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'new_weighted_earned': kwargs.get('weighted_earned'),
                'new_weighted_possible': kwargs.get('weighted_possible'),
                'only_if_higher': kwargs.get('only_if_higher'),
                'instructor_id': unicode(instructor_id),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(root_type),
            }
        )

    elif root_type in [SUBSECTION_OVERRIDE_EVENT_TYPE]:
        tracker.emit(
            unicode(root_type),
            {
                'course_id': unicode(kwargs['course_id']),
                'user_id': unicode(kwargs['user_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'only_if_higher': kwargs.get('only_if_higher'),
                'override_deleted': kwargs.get('score_deleted', False),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(root_type),
            }
        )
Ejemplo n.º 7
0
def course_grade_calculated(course_grade):
    """
    Emits an edx.grades.course.grade_calculated event
    with data from the passed course_grade.
    """
    event_name = COURSE_GRADE_CALCULATED
    context = contexts.course_context_from_course_id(course_grade.course_id)
    # TODO (AN-6134): remove this context manager
    with tracker.get_tracker().context(event_name, context):
        tracker.emit(
            event_name, {
                'user_id':
                unicode(course_grade.user_id),
                'course_id':
                unicode(course_grade.course_id),
                'course_version':
                unicode(course_grade.course_version),
                'percent_grade':
                course_grade.percent_grade,
                'letter_grade':
                unicode(course_grade.letter_grade),
                'course_edited_timestamp':
                unicode(course_grade.course_edited_timestamp),
                'event_transaction_id':
                unicode(get_event_transaction_id()),
                'event_transaction_type':
                unicode(get_event_transaction_type()),
                'grading_policy_hash':
                unicode(course_grade.grading_policy_hash),
            })
    def _create_override(self, request_user, subsection_grade_model,
                         **override_data):
        """
        Helper method to create a `PersistentSubsectionGradeOverride` object
        and send a `SUBSECTION_OVERRIDE_CHANGED` signal.
        """
        override = PersistentSubsectionGradeOverride.update_or_create_override(
            requesting_user=request_user,
            subsection_grade_model=subsection_grade_model,
            feature=PersistentSubsectionGradeOverrideHistory.GRADEBOOK,
            **override_data)

        set_event_transaction_type(SUBSECTION_GRADE_CALCULATED)
        create_new_event_transaction_id()

        recalculate_subsection_grade_v3.apply(kwargs=dict(
            user_id=subsection_grade_model.user_id,
            anonymous_user_id=None,
            course_id=text_type(subsection_grade_model.course_id),
            usage_id=text_type(subsection_grade_model.usage_key),
            only_if_higher=False,
            expected_modified_time=to_timestamp(override.modified),
            score_deleted=False,
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
            score_db_table=ScoreDatabaseTableEnum.overrides,
            force_update_subsections=True,
        ))
        # Emit events to let our tracking system to know we updated subsection grade
        subsection_grade_calculated(subsection_grade_model)
        return override
Ejemplo n.º 9
0
def _emit_event(kwargs):
    """
    Emits a problem submitted event only if there is no current event
    transaction type, i.e. we have not reached this point in the code via a
    rescore or student state deletion.

    If the event transaction type has already been set and the transacation is
    a rescore, emits a problem rescored event.
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
            {
                'user_id': unicode(kwargs['user_id']),
                'course_id': unicode(kwargs['course_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'event_transaction_id': unicode(root_id),
                'event_transaction_type': unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            }
        )

    if root_type in [GRADES_RESCORE_EVENT_TYPE, GRADES_OVERRIDE_EVENT_TYPE]:
        current_user = get_current_user()
        instructor_id = getattr(current_user, 'id', None)
        tracker.emit(
            unicode(GRADES_RESCORE_EVENT_TYPE),
            {
                'course_id': unicode(kwargs['course_id']),
                'user_id': unicode(kwargs['user_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'new_weighted_earned': kwargs.get('weighted_earned'),
                'new_weighted_possible': kwargs.get('weighted_possible'),
                'only_if_higher': kwargs.get('only_if_higher'),
                'instructor_id': unicode(instructor_id),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(root_type),
            }
        )

    if root_type in [SUBSECTION_OVERRIDE_EVENT_TYPE]:
        tracker.emit(
            unicode(SUBSECTION_OVERRIDE_EVENT_TYPE),
            {
                'course_id': unicode(kwargs['course_id']),
                'user_id': unicode(kwargs['user_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'only_if_higher': kwargs.get('only_if_higher'),
                'override_deleted': kwargs.get('score_deleted', False),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(root_type),
            }
        )
Ejemplo n.º 10
0
 def _emit_grade_calculated_event(grade):
     """
     Emits an edx.grades.course.grade_calculated event
     with data from the passed grade.
     """
     # TODO: remove this context manager after completion of AN-6134
     event_name = u'edx.grades.course.grade_calculated'
     context = contexts.course_context_from_course_id(grade.course_id)
     with tracker.get_tracker().context(event_name, context):
         tracker.emit(
             event_name, {
                 'user_id':
                 unicode(grade.user_id),
                 'course_id':
                 unicode(grade.course_id),
                 'course_version':
                 unicode(grade.course_version),
                 'percent_grade':
                 grade.percent_grade,
                 'letter_grade':
                 unicode(grade.letter_grade),
                 'course_edited_timestamp':
                 unicode(grade.course_edited_timestamp),
                 'event_transaction_id':
                 unicode(get_event_transaction_id()),
                 'event_transaction_type':
                 unicode(get_event_transaction_type()),
                 'grading_policy_hash':
                 unicode(grade.grading_policy_hash),
             })
Ejemplo n.º 11
0
 def _emit_grade_calculated_event(grade):
     """
     Emits an edx.grades.subsection.grade_calculated event
     with data from the passed grade.
     """
     # TODO: remove this context manager after completion of AN-6134
     event_name = u'edx.grades.subsection.grade_calculated'
     context = contexts.course_context_from_course_id(grade.course_id)
     with tracker.get_tracker().context(event_name, context):
         tracker.emit(
             event_name,
             {
                 'user_id': unicode(grade.user_id),
                 'course_id': unicode(grade.course_id),
                 'block_id': unicode(grade.usage_key),
                 'course_version': unicode(grade.course_version),
                 'weighted_total_earned': grade.earned_all,
                 'weighted_total_possible': grade.possible_all,
                 'weighted_graded_earned': grade.earned_graded,
                 'weighted_graded_possible': grade.possible_graded,
                 'first_attempted': unicode(grade.first_attempted),
                 'subtree_edited_timestamp': unicode(grade.subtree_edited_timestamp),
                 'event_transaction_id': unicode(get_event_transaction_id()),
                 'event_transaction_type': unicode(get_event_transaction_type()),
                 'visible_blocks_hash': unicode(grade.visible_blocks_id),
             }
         )
Ejemplo n.º 12
0
def _retry_recalculate_subsection_grade(
        user_id,
        course_id,
        usage_id,
        only_if_higher,
        expected_modified_time,
        score_deleted,
        exc=None,
):
    """
    Calls retry for the recalculate_subsection_grade task with the
    given inputs.
    """
    recalculate_subsection_grade_v2.retry(
        kwargs=dict(
            user_id=user_id,
            course_id=course_id,
            usage_id=usage_id,
            only_if_higher=only_if_higher,
            expected_modified_time=expected_modified_time,
            score_deleted=score_deleted,
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
        ),
        exc=exc,
    )
Ejemplo n.º 13
0
 def _assert_tracker_emitted_event(self, tracker_mock, grade):
     """
     Helper function to ensure that the mocked event tracker
     was called with the expected info based on the passed grade.
     """
     tracker_mock.emit.assert_called_with(
         u'edx.grades.subsection.grade_calculated', {
             'user_id':
             six.text_type(grade.user_id),
             'course_id':
             six.text_type(grade.course_id),
             'block_id':
             six.text_type(grade.usage_key),
             'course_version':
             six.text_type(grade.course_version),
             'weighted_total_earned':
             grade.earned_all,
             'weighted_total_possible':
             grade.possible_all,
             'weighted_graded_earned':
             grade.earned_graded,
             'weighted_graded_possible':
             grade.possible_graded,
             'first_attempted':
             six.text_type(grade.first_attempted),
             'subtree_edited_timestamp':
             six.text_type(grade.subtree_edited_timestamp),
             'event_transaction_id':
             six.text_type(get_event_transaction_id()),
             'event_transaction_type':
             six.text_type(get_event_transaction_type()),
             'visible_blocks_hash':
             six.text_type(grade.visible_blocks_id),
         })
Ejemplo n.º 14
0
 def _assert_tracker_emitted_event(self, tracker_mock, grade):
     """
     Helper function to ensure that the mocked event tracker
     was called with the expected info based on the passed grade.
     """
     tracker_mock.emit.assert_called_with(
         u'edx.grades.course.grade_calculated', {
             'user_id':
             six.text_type(grade.user_id),
             'course_id':
             six.text_type(grade.course_id),
             'course_version':
             six.text_type(grade.course_version),
             'percent_grade':
             grade.percent_grade,
             'letter_grade':
             six.text_type(grade.letter_grade),
             'course_edited_timestamp':
             six.text_type(grade.course_edited_timestamp),
             'event_transaction_id':
             six.text_type(get_event_transaction_id()),
             'event_transaction_type':
             six.text_type(get_event_transaction_type()),
             'grading_policy_hash':
             six.text_type(grade.grading_policy_hash),
         })
Ejemplo n.º 15
0
def _retry_recalculate_subsection_grade(
    user_id,
    course_id,
    usage_id,
    only_if_higher,
    expected_modified_time,
    score_deleted,
    exc=None,
):
    """
    Calls retry for the recalculate_subsection_grade task with the
    given inputs.
    """
    recalculate_subsection_grade_v2.retry(
        kwargs=dict(
            user_id=user_id,
            course_id=course_id,
            usage_id=usage_id,
            only_if_higher=only_if_higher,
            expected_modified_time=expected_modified_time,
            score_deleted=score_deleted,
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
        ),
        exc=exc,
    )
Ejemplo n.º 16
0
def enqueue_subsection_update(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Handles the PROBLEM_WEIGHTED_SCORE_CHANGED or SUBSECTION_OVERRIDE_CHANGED signals by
    enqueueing a subsection update operation to occur asynchronously.
    """
    events.grade_updated(**kwargs)
    context_key = LearningContextKey.from_string(kwargs['course_id'])
    if not context_key.is_course:
        return  # If it's not a course, it has no subsections, so skip the subsection grading update
    recalculate_subsection_grade_v3.apply_async(
        kwargs=dict(
            user_id=kwargs['user_id'],
            anonymous_user_id=kwargs.get('anonymous_user_id'),
            course_id=kwargs['course_id'],
            usage_id=kwargs['usage_id'],
            only_if_higher=kwargs.get('only_if_higher'),
            expected_modified_time=to_timestamp(kwargs['modified']),
            score_deleted=kwargs.get('score_deleted', False),
            event_transaction_id=six.text_type(get_event_transaction_id()),
            event_transaction_type=six.text_type(get_event_transaction_type()),
            score_db_table=kwargs['score_db_table'],
            force_update_subsections=kwargs.get('force_update_subsections',
                                                False),
        ),
        countdown=RECALCULATE_GRADE_DELAY_SECONDS,
    )
Ejemplo n.º 17
0
def _emit_problem_submitted_event(kwargs):
    """
    Emits a problem submitted event only if
    there is no current event transaction type,
    i.e. we have not reached this point in the
    code via a rescore or student state deletion.
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
            {
                'user_id': unicode(kwargs['user_id']),
                'course_id': unicode(kwargs['course_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'event_transaction_id': unicode(root_id),
                'event_transaction_type': unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            }
        )
Ejemplo n.º 18
0
def enqueue_subsection_update(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Handles the PROBLEM_WEIGHTED_SCORE_CHANGED signal by
    enqueueing a subsection update operation to occur asynchronously.
    """
    _emit_problem_submitted_event(kwargs)
    result = recalculate_subsection_grade_v3.apply_async(
        kwargs=dict(
            user_id=kwargs['user_id'],
            anonymous_user_id=kwargs.get('anonymous_user_id'),
            course_id=kwargs['course_id'],
            usage_id=kwargs['usage_id'],
            only_if_higher=kwargs.get('only_if_higher'),
            expected_modified_time=to_timestamp(kwargs['modified']),
            score_deleted=kwargs.get('score_deleted', False),
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
            score_db_table=kwargs['score_db_table'],
        ),
        countdown=RECALCULATE_GRADE_DELAY,
    )
    log.info(
        u'Grades: Request async calculation of subsection grades with args: {}. Task [{}]'.format(
            ', '.join('{}:{}'.format(arg, kwargs[arg]) for arg in sorted(kwargs)),
            getattr(result, 'id', 'N/A'),
        )
    )
Ejemplo n.º 19
0
def enqueue_subsection_update(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Handles the PROBLEM_WEIGHTED_SCORE_CHANGED signal by
    enqueueing a subsection update operation to occur asynchronously.
    """
    _emit_problem_submitted_event(kwargs)
    result = recalculate_subsection_grade_v3.apply_async(
        kwargs=dict(
            user_id=kwargs['user_id'],
            anonymous_user_id=kwargs.get('anonymous_user_id'),
            course_id=kwargs['course_id'],
            usage_id=kwargs['usage_id'],
            only_if_higher=kwargs.get('only_if_higher'),
            expected_modified_time=to_timestamp(kwargs['modified']),
            score_deleted=kwargs.get('score_deleted', False),
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
            score_db_table=kwargs['score_db_table'],
        ),
        countdown=RECALCULATE_GRADE_DELAY,
    )
    log.info(
        u'Grades: Request async calculation of subsection grades with args: {}. Task [{}]'.format(
            ', '.join('{}:{}'.format(arg, kwargs[arg]) for arg in sorted(kwargs)),
            getattr(result, 'id', 'N/A'),
        )
    )
Ejemplo n.º 20
0
 def _emit_grade_calculated_event(grade):
     """
     Emits an edx.grades.subsection.grade_calculated event
     with data from the passed grade.
     """
     # TODO: remove this context manager after completion of AN-6134
     event_name = u'edx.grades.subsection.grade_calculated'
     context = contexts.course_context_from_course_id(grade.course_id)
     with tracker.get_tracker().context(event_name, context):
         tracker.emit(
             event_name,
             {
                 'user_id': unicode(grade.user_id),
                 'course_id': unicode(grade.course_id),
                 'block_id': unicode(grade.usage_key),
                 'course_version': unicode(grade.course_version),
                 'weighted_total_earned': grade.earned_all,
                 'weighted_total_possible': grade.possible_all,
                 'weighted_graded_earned': grade.earned_graded,
                 'weighted_graded_possible': grade.possible_graded,
                 'first_attempted': unicode(grade.first_attempted),
                 'subtree_edited_timestamp': unicode(grade.subtree_edited_timestamp),
                 'event_transaction_id': unicode(get_event_transaction_id()),
                 'event_transaction_type': unicode(get_event_transaction_type()),
                 'visible_blocks_hash': unicode(grade.visible_blocks_id),
             }
         )
Ejemplo n.º 21
0
def handle_grading_policy_changed(sender, **kwargs):
    # pylint: disable=unused-argument
    """
    Receives signal and kicks off celery task to recalculate grades
    """
    course_key = kwargs.get('course_key')
    result = compute_all_grades_for_course.apply_async(
        course_key=course_key,
        event_transaction_id=get_event_transaction_id(),
        event_transaction_type=get_event_transaction_type(),
    )
    log.info("Grades: Created {task_name}[{task_id}] with arguments {kwargs}".format(
        task_name=compute_all_grades_for_course.name,
        task_id=result.task_id,
        kwargs=kwargs,
    ))
Ejemplo n.º 22
0
def handle_grading_policy_changed(sender, **kwargs):
    # pylint: disable=unused-argument
    """
    Receives signal and kicks off celery task to recalculate grades
    """
    kwargs = {
        'course_key': unicode(kwargs.get('course_key')),
        'event_transaction_id': unicode(get_event_transaction_id()),
        'event_transaction_type': unicode(get_event_transaction_type()),
    }
    result = compute_all_grades_for_course.apply_async(kwargs=kwargs, countdown=GRADING_POLICY_COUNTDOWN_SECONDS)
    log.info("Grades: Created {task_name}[{task_id}] with arguments {kwargs}".format(
        task_name=compute_all_grades_for_course.name,
        task_id=result.task_id,
        kwargs=kwargs,
    ))
Ejemplo n.º 23
0
def handle_grading_policy_changed(sender, **kwargs):
    # pylint: disable=unused-argument
    """
    Receives signal and kicks off celery task to recalculate grades
    """
    kwargs = {
        'course_key': unicode(kwargs.get('course_key')),
        'event_transaction_id': unicode(get_event_transaction_id()),
        'event_transaction_type': unicode(get_event_transaction_type()),
    }
    result = compute_all_grades_for_course.apply_async(kwargs=kwargs)
    log.info("Grades: Created {task_name}[{task_id}] with arguments {kwargs}".format(
        task_name=compute_all_grades_for_course.name,
        task_id=result.task_id,
        kwargs=kwargs,
    ))
Ejemplo n.º 24
0
def _emit_event(kwargs):
    """
    Emits a problem submitted event only if there is no current event
    transaction type, i.e. we have not reached this point in the code via a
    rescore or student state deletion.

    If the event transaction type has already been set and the transacation is
    a rescore, emits a problem rescored event.
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
            {
                'user_id': unicode(kwargs['user_id']),
                'course_id': unicode(kwargs['course_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'event_transaction_id': unicode(root_id),
                'event_transaction_type': unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            }
        )

    if root_type in [GRADES_RESCORE_EVENT_TYPE, GRADES_OVERRIDE_EVENT_TYPE]:
        current_user = get_current_user()
        instructor_id = getattr(current_user, 'id', None)
        tracker.emit(
            unicode(GRADES_RESCORE_EVENT_TYPE),
            {
                'course_id': unicode(kwargs['course_id']),
                'user_id': unicode(kwargs['user_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'new_weighted_earned': kwargs.get('weighted_earned'),
                'new_weighted_possible': kwargs.get('weighted_possible'),
                'only_if_higher': kwargs.get('only_if_higher'),
                'instructor_id': unicode(instructor_id),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(root_type),
            }
        )
Ejemplo n.º 25
0
def _emit_event(kwargs):
    """
    Emits a problem submitted event only if there is no current event
    transaction type, i.e. we have not reached this point in the code via a
    rescore or student state deletion.

    If the event transaction type has already been set and the transacation is
    a rescore, emits a problem rescored event.
    """
    root_type = get_event_transaction_type()

    if not root_type:
        root_id = get_event_transaction_id()
        if not root_id:
            root_id = create_new_event_transaction_id()
        set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
        tracker.emit(
            unicode(PROBLEM_SUBMITTED_EVENT_TYPE), {
                'user_id': unicode(kwargs['user_id']),
                'course_id': unicode(kwargs['course_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'event_transaction_id': unicode(root_id),
                'event_transaction_type':
                unicode(PROBLEM_SUBMITTED_EVENT_TYPE),
                'weighted_earned': kwargs.get('weighted_earned'),
                'weighted_possible': kwargs.get('weighted_possible'),
            })

    if root_type == 'edx.grades.problem.rescored':
        current_user = get_current_user()
        if current_user is not None and hasattr(current_user, 'id'):
            instructor_id = unicode(current_user.id)
        else:
            instructor_id = None
        tracker.emit(
            unicode(GRADES_RESCORE_EVENT_TYPE), {
                'course_id': unicode(kwargs['course_id']),
                'user_id': unicode(kwargs['user_id']),
                'problem_id': unicode(kwargs['usage_id']),
                'new_weighted_earned': kwargs.get('weighted_earned'),
                'new_weighted_possible': kwargs.get('weighted_possible'),
                'only_if_higher': kwargs.get('only_if_higher'),
                'instructor_id': instructor_id,
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(GRADES_RESCORE_EVENT_TYPE),
            })
Ejemplo n.º 26
0
 def _emit_grade_calculated_event(grade):
     """
     Emits an edx.grades.course.grade_calculated event
     with data from the passed grade.
     """
     tracker.emit(
         u'edx.grades.course.grade_calculated',
         {
             'user_id': unicode(grade.user_id),
             'course_id': unicode(grade.course_id),
             'course_version': unicode(grade.course_version),
             'percent_grade': grade.percent_grade,
             'letter_grade': unicode(grade.letter_grade),
             'course_edited_timestamp': unicode(grade.course_edited_timestamp),
             'event_transaction_id': unicode(get_event_transaction_id()),
             'event_transaction_type': unicode(get_event_transaction_type()),
             'grading_policy_hash': unicode(grade.grading_policy_hash),
         }
     )
Ejemplo n.º 27
0
 def _assert_tracker_emitted_event(self, tracker_mock, grade):
     """
     Helper function to ensure that the mocked event tracker
     was called with the expected info based on the passed grade.
     """
     tracker_mock.emit.assert_called_with(
         u'edx.grades.course.grade_calculated',
         {
             'user_id': unicode(grade.user_id),
             'course_id': unicode(grade.course_id),
             'course_version': unicode(grade.course_version),
             'percent_grade': grade.percent_grade,
             'letter_grade': unicode(grade.letter_grade),
             'course_edited_timestamp': unicode(grade.course_edited_timestamp),
             'event_transaction_id': unicode(get_event_transaction_id()),
             'event_transaction_type': unicode(get_event_transaction_type()),
             'grading_policy_hash': unicode(grade.grading_policy_hash),
         }
     )
Ejemplo n.º 28
0
def enqueue_subsection_update(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Handles the PROBLEM_WEIGHTED_SCORE_CHANGED or SUBSECTION_OVERRIDE_CHANGED signals by
    enqueueing a subsection update operation to occur asynchronously.
    """
    events.grade_updated(**kwargs)
    recalculate_subsection_grade_v3.apply_async(
        kwargs=dict(
            user_id=kwargs['user_id'],
            anonymous_user_id=kwargs.get('anonymous_user_id'),
            course_id=kwargs['course_id'],
            usage_id=kwargs['usage_id'],
            only_if_higher=kwargs.get('only_if_higher'),
            expected_modified_time=to_timestamp(kwargs['modified']),
            score_deleted=kwargs.get('score_deleted', False),
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
            score_db_table=kwargs['score_db_table'],
        ),
        countdown=RECALCULATE_GRADE_DELAY_SECONDS,
    )
Ejemplo n.º 29
0
def enqueue_subsection_update(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Handles the PROBLEM_WEIGHTED_SCORE_CHANGED or SUBSECTION_OVERRIDE_CHANGED signals by
    enqueueing a subsection update operation to occur asynchronously.
    """
    _emit_event(kwargs)
    result = recalculate_subsection_grade_v3.apply_async(
        kwargs=dict(
            user_id=kwargs['user_id'],
            anonymous_user_id=kwargs.get('anonymous_user_id'),
            course_id=kwargs['course_id'],
            usage_id=kwargs['usage_id'],
            only_if_higher=kwargs.get('only_if_higher'),
            expected_modified_time=to_timestamp(kwargs['modified']),
            score_deleted=kwargs.get('score_deleted', False),
            event_transaction_id=unicode(get_event_transaction_id()),
            event_transaction_type=unicode(get_event_transaction_type()),
            score_db_table=kwargs['score_db_table'],
        ),
        countdown=RECALCULATE_GRADE_DELAY,
    )
Ejemplo n.º 30
0
 def _emit_grade_calculated_event(grade):
     """
     Emits an edx.grades.subsection.grade_calculated event
     with data from the passed grade.
     """
     tracker.emit(
         u'edx.grades.subsection.grade_calculated',
         {
             'user_id': unicode(grade.user_id),
             'course_id': unicode(grade.course_id),
             'block_id': unicode(grade.usage_key),
             'course_version': unicode(grade.course_version),
             'weighted_total_earned': grade.earned_all,
             'weighted_total_possible': grade.possible_all,
             'weighted_graded_earned': grade.earned_graded,
             'weighted_graded_possible': grade.possible_graded,
             'first_attempted': unicode(grade.first_attempted),
             'subtree_edited_timestamp': unicode(grade.subtree_edited_timestamp),
             'event_transaction_id': unicode(get_event_transaction_id()),
             'event_transaction_type': unicode(get_event_transaction_type()),
             'visible_blocks_hash': unicode(grade.visible_blocks_id),
         }
     )
Ejemplo n.º 31
0
 def _emit_grade_calculated_event(grade):
     """
     Emits an edx.grades.course.grade_calculated event
     with data from the passed grade.
     """
     # TODO: remove this context manager after completion of AN-6134
     event_name = u'edx.grades.course.grade_calculated'
     context = contexts.course_context_from_course_id(grade.course_id)
     with tracker.get_tracker().context(event_name, context):
         tracker.emit(
             event_name,
             {
                 'user_id': unicode(grade.user_id),
                 'course_id': unicode(grade.course_id),
                 'course_version': unicode(grade.course_version),
                 'percent_grade': grade.percent_grade,
                 'letter_grade': unicode(grade.letter_grade),
                 'course_edited_timestamp': unicode(grade.course_edited_timestamp),
                 'event_transaction_id': unicode(get_event_transaction_id()),
                 'event_transaction_type': unicode(get_event_transaction_type()),
                 'grading_policy_hash': unicode(grade.grading_policy_hash),
             }
         )
Ejemplo n.º 32
0
 def _assert_tracker_emitted_event(self, tracker_mock, grade):
     """
     Helper function to ensure that the mocked event tracker
     was called with the expected info based on the passed grade.
     """
     tracker_mock.emit.assert_called_with(
         u'edx.grades.subsection.grade_calculated',
         {
             'user_id': unicode(grade.user_id),
             'course_id': unicode(grade.course_id),
             'block_id': unicode(grade.usage_key),
             'course_version': unicode(grade.course_version),
             'weighted_total_earned': grade.earned_all,
             'weighted_total_possible': grade.possible_all,
             'weighted_graded_earned': grade.earned_graded,
             'weighted_graded_possible': grade.possible_graded,
             'first_attempted': unicode(grade.first_attempted),
             'subtree_edited_timestamp': unicode(grade.subtree_edited_timestamp),
             'event_transaction_id': unicode(get_event_transaction_id()),
             'event_transaction_type': unicode(get_event_transaction_type()),
             'visible_blocks_hash': unicode(grade.visible_blocks_id),
         }
     )
Ejemplo n.º 33
0
def course_grade_calculated(course_grade):
    """
    Emits an edx.grades.course.grade_calculated event
    with data from the passed course_grade.
    """
    event_name = COURSE_GRADE_CALCULATED
    context = contexts.course_context_from_course_id(course_grade.course_id)
    # TODO (AN-6134): remove this context manager
    with tracker.get_tracker().context(event_name, context):
        tracker.emit(
            event_name,
            {
                'user_id': unicode(course_grade.user_id),
                'course_id': unicode(course_grade.course_id),
                'course_version': unicode(course_grade.course_version),
                'percent_grade': course_grade.percent_grade,
                'letter_grade': unicode(course_grade.letter_grade),
                'course_edited_timestamp': unicode(course_grade.course_edited_timestamp),
                'event_transaction_id': unicode(get_event_transaction_id()),
                'event_transaction_type': unicode(get_event_transaction_type()),
                'grading_policy_hash': unicode(course_grade.grading_policy_hash),
            }
        )
Ejemplo n.º 34
0
    def _create_override(self, request_user, subsection_grade_model, **override_data):
        """
        Helper method to create a `PersistentSubsectionGradeOverride` object
        and send a `SUBSECTION_OVERRIDE_CHANGED` signal.
        """
        override, _ = PersistentSubsectionGradeOverride.objects.update_or_create(
            grade=subsection_grade_model,
            defaults=self._clean_override_data(override_data),
        )

        _ = PersistentSubsectionGradeOverrideHistory.objects.create(
            override_id=override.id,
            user=request_user,
            feature=PersistentSubsectionGradeOverrideHistory.GRADEBOOK,
            action=PersistentSubsectionGradeOverrideHistory.CREATE_OR_UPDATE,
        )

        set_event_transaction_type(SUBSECTION_GRADE_CALCULATED)
        create_new_event_transaction_id()

        recalculate_subsection_grade_v3.apply(
            kwargs=dict(
                user_id=subsection_grade_model.user_id,
                anonymous_user_id=None,
                course_id=text_type(subsection_grade_model.course_id),
                usage_id=text_type(subsection_grade_model.usage_key),
                only_if_higher=False,
                expected_modified_time=to_timestamp(override.modified),
                score_deleted=False,
                event_transaction_id=unicode(get_event_transaction_id()),
                event_transaction_type=unicode(get_event_transaction_type()),
                score_db_table=ScoreDatabaseTableEnum.overrides,
                force_update_subsections=True,
            )
        )
        # Emit events to let our tracking system to know we updated subsection grade
        subsection_grade_calculated(subsection_grade_model)