def score_changed_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume signals that indicate score changes. See the definition of PROBLEM_WEIGHTED_SCORE_CHANGED for a description of the signal. """ points_possible = kwargs.get('weighted_possible', None) points_earned = kwargs.get('weighted_earned', None) user_id = kwargs.get('user_id', None) course_id = kwargs.get('course_id', None) usage_id = kwargs.get('usage_id', None) if None not in (points_earned, points_possible, user_id, course_id): course_key, usage_key = parse_course_and_usage_keys( course_id, usage_id) assignments = increment_assignment_versions(course_key, usage_key, user_id) for assignment in assignments: if assignment.usage_key == usage_key: send_leaf_outcome.delay(assignment.id, points_earned, points_possible) else: send_composite_outcome.apply_async( (user_id, course_id, assignment.id, assignment.version_number), countdown=settings.LTI_AGGREGATE_SCORE_PASSBACK_DELAY) else: log.error( u"Outcome Service: Required signal parameter is None. " u"points_possible: %s, points_earned: %s, user_id: %s, " u"course_id: %s, usage_id: %s", points_possible, points_earned, user_id, course_id, usage_id)
def score_changed_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume signals that indicate score changes. See the definition of SCORE_CHANGED for a description of the signal. """ points_possible = kwargs.get('points_possible', None) points_earned = kwargs.get('points_earned', None) user = kwargs.get('user', None) course_id = kwargs.get('course_id', None) usage_id = kwargs.get('usage_id', None) if None not in (points_earned, points_possible, user.id, course_id, user.id): course_key, usage_key = parse_course_and_usage_keys(course_id, usage_id) assignments = increment_assignment_versions(course_key, usage_key, user.id) for assignment in assignments: if assignment.usage_key == usage_key: send_leaf_outcome.delay( assignment.id, points_earned, points_possible ) else: send_composite_outcome.apply_async( (user.id, course_id, assignment.id, assignment.version_number), countdown=settings.LTI_AGGREGATE_SCORE_PASSBACK_DELAY ) else: log.error( "Outcome Service: Required signal parameter is None. " "points_possible: %s, points_earned: %s, user: %s, " "course_id: %s, usage_id: %s", points_possible, points_earned, unicode(user), course_id, usage_id )
def send_outcome(points_possible, points_earned, user_id, course_id, usage_id): """ Calculate the score for a given user in a problem and send it to the appropriate LTI consumer's outcome service. """ course_key, usage_key = parse_course_and_usage_keys(course_id, usage_id) assignments = GradedAssignment.objects.filter( user=user_id, course_key=course_key, usage_key=usage_key ) # Calculate the user's score, on a scale of 0.0 - 1.0. score = float(points_earned) / float(points_possible) # There may be zero or more assignment records. We would expect for there # to be zero if the user/course/usage combination does not relate to a # previous graded LTI launch. This can happen if an LTI consumer embeds some # gradable content in a context that doesn't require a score (maybe by # including an exercise as a sample that students may complete but don't # count towards their grade). # There could be more than one GradedAssignment record if the same content # is embedded more than once in a single course. This would be a strange # course design on the consumer's part, but we handle it by sending update # messages for all launches of the content. for assignment in assignments: xml = lti_provider.outcomes.generate_replace_result_xml( assignment.lis_result_sourcedid, score ) try: response = lti_provider.outcomes.sign_and_send_replace_result(assignment, xml) except RequestException: # failed to send result. 'response' is None, so more detail will be # logged at the end of the method. response = None log.exception("Outcome Service: Error when sending result.") # If something went wrong, make sure that we have a complete log record. # That way we can manually fix things up on the campus system later if # necessary. if not (response and lti_provider.outcomes.check_replace_result_response(response)): log.error( "Outcome Service: Failed to update score on LTI consumer. " "User: %s, course: %s, usage: %s, score: %s, possible: %s " "status: %s, body: %s", user_id, course_key, usage_key, points_earned, points_possible, response, response.text if response else 'Unknown' )