78 lines
3.1 KiB
Python
78 lines
3.1 KiB
Python
"""
|
|
Signals handlers for the lti_provider Django app.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from django.conf import settings
|
|
from django.dispatch import receiver
|
|
from opaque_keys.edx.keys import LearningContextKey
|
|
|
|
import lms.djangoapps.lti_provider.outcomes as outcomes
|
|
from lms.djangoapps.grades.api import signals as grades_signals
|
|
from lms.djangoapps.lti_provider.tasks import send_composite_outcome, send_leaf_outcome
|
|
from lms.djangoapps.lti_provider.views import parse_course_and_usage_keys
|
|
from xmodule.modulestore.django import modulestore
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def increment_assignment_versions(course_key, usage_key, user_id):
|
|
"""
|
|
Update the version numbers for all assignments that are affected by a score
|
|
change event. Returns a list of all affected assignments.
|
|
"""
|
|
problem_descriptor = modulestore().get_item(usage_key)
|
|
# Get all assignments involving the current problem for which the campus LMS
|
|
# is expecting a grade. There may be many possible graded assignments, if
|
|
# a problem has been added several times to a course at different
|
|
# granularities (such as the unit or the vertical).
|
|
assignments = outcomes.get_assignments_for_problem(
|
|
problem_descriptor, user_id, course_key
|
|
)
|
|
for assignment in assignments:
|
|
assignment.version_number += 1
|
|
assignment.save()
|
|
return assignments
|
|
|
|
|
|
@receiver(grades_signals.PROBLEM_WEIGHTED_SCORE_CHANGED)
|
|
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)
|
|
|
|
# Make sure this came from a course because this code only works with courses
|
|
if not course_id:
|
|
return
|
|
context_key = LearningContextKey.from_string(course_id)
|
|
if not context_key.is_course:
|
|
return # This is a content library or something else...
|
|
|
|
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(
|
|
"Outcome Service: Required signal parameter is None. "
|
|
"points_possible: %s, points_earned: %s, user_id: %s, "
|
|
"course_id: %s, usage_id: %s",
|
|
points_possible, points_earned, user_id, course_id, usage_id
|
|
)
|