From 9760e689b20f307553a2f0d0fd3fbcb2fee5905c Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Mon, 4 Nov 2024 23:22:01 -0800 Subject: [PATCH] Control when sets with score 0 are included in LTI grade passback --- conf/authen_LTI.conf.dist | 7 +++++ lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm | 24 ++++++++++++----- .../Authen/LTIAdvantage/SubmitGrade.pm | 18 +++++++++++-- lib/WeBWorK/ConfigValues.pm | 19 +++++++++++++ lib/WeBWorK/Utils/Sets.pm | 27 ++++++++++++------- 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/conf/authen_LTI.conf.dist b/conf/authen_LTI.conf.dist index 8e901ece14..547dce3ead 100644 --- a/conf/authen_LTI.conf.dist +++ b/conf/authen_LTI.conf.dist @@ -135,6 +135,12 @@ $LTIGradeMode = ''; #$LTIGradeMode = 'course'; #$LTIGradeMode = 'homework'; +# This can be set to 'open', 'reduced', 'close', 'answer', or 'never'. It controls when a set +# with score 0 will be included in grades that are passed back to the LMS. For example, setting +# this to 'close' means that a set with score 0 is only included in grades sent to the LMS once +# it is past the set's close date. +$LTISendZeroScores = 'open'; + # When set this variable sends grades back to the LMS every time a user submits an answer. This # keeps students grades up to date but can be a drain on the server. $LTIGradeOnSubmit = 1; @@ -170,6 +176,7 @@ $LTIMassUpdateInterval = 86400; #in seconds #'LTI{v1p3}{LMS_url}', #'external_auth', #'LTIGradeMode', + #'LTISendZeroScores', #'LTIGradeOnSubmit', #'LTIMassUpdateInterval', #'LMSManageUserData', diff --git a/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm b/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm index a3ec411f47..ad9d07f747 100644 --- a/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm +++ b/lib/WeBWorK/Authen/LTIAdvanced/SubmitGrade.pm @@ -128,7 +128,8 @@ async sub submit_course_grade ($self, $userID) { $self->warning("lis_source_did is not available for user: $userID") if !$user->lis_source_did && ($ce->{debug_lti_grade_passback} || $self->{post_processing_mode}); - return await $self->submit_grade($user->lis_source_did, scalar(grade_all_sets($db, $userID))); + return await $self->submit_grade($user->lis_source_did, + scalar(grade_all_sets($db, $userID, $ce->{LTISendZeroScores}))); } # Computes and submits the set grade for $userID and $setID to the LMS. For gateways the best score is used. @@ -147,14 +148,23 @@ async sub submit_set_grade ($self, $userID, $setID) { $self->warning('lis_source_did is not available for this set.') if !$userSet->lis_source_did && ($ce->{debug_lti_grade_passback} || $self->{post_processing_mode}); - return await $self->submit_grade( - $userSet->lis_source_did, - scalar( - $userSet->assignment_type =~ /gateway/ + my $score = + scalar($userSet->assignment_type =~ /gateway/ ? grade_gateway($db, $userSet, $userSet->set_id, $userID) - : grade_set($db, $userSet, $userID, 0) - ) + : grade_set($db, $userSet, $userID, 0)); + + my %dates = ( + open => $userSet->open_date(), + reduced => $userSet->reduced_scoring_date(), + close => $userSet->close_date(), + answer => $userSet->answer_date() ); + + if ($score == 0) { + return if ($ce->{LTISendZeroScores} eq 'never' || before($dates{ $ce->{LTISendZeroScores} })); + } + + return await $self->submit_grade($userSet->lis_source_did, $score); } # Submits a score of $score to the lms with $sourcedid as the identifier. diff --git a/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm b/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm index 688c94d36e..0075da2961 100644 --- a/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm +++ b/lib/WeBWorK/Authen/LTIAdvantage/SubmitGrade.pm @@ -207,7 +207,8 @@ async sub submit_course_grade ($self, $userID) { $self->warning('LMS user id is not available for this user.') unless $user->lis_source_did; $self->warning('LMS lineitem is not available for the course.') unless $lineitem; - return await $self->submit_grade($user->lis_source_did, $lineitem, grade_all_sets($db, $userID)); + return await $self->submit_grade($user->lis_source_did, $lineitem, + grade_all_sets($db, $userID, $ce->{LTISendZeroScores})); } # Computes and submits the set grade for $userID and $setID to the LMS. For gateways the best score is used. @@ -225,10 +226,23 @@ async sub submit_set_grade ($self, $userID, $setID) { $self->warning('LMS user id is not available for this user.') unless $user->lis_source_did; $self->warning('LMS lineitem is not available for this set.') unless $userSet->lis_source_did; + my ($totalRight, $total) = (grade_set($db, $userSet, $userID, 0))[ 0, 1 ]; + + my %dates = ( + open => $userSet->open_date(), + reduced => $userSet->reduced_scoring_date(), + close => $userSet->close_date(), + answer => $userSet->answer_date() + ); + + if ($totalRight == 0) { + return if ($ce->{LTISendZeroScores} eq 'never' || before($dates{ $ce->{LTISendZeroScores} })); + } + return await $self->submit_grade($user->lis_source_did, $userSet->lis_source_did, $userSet->assignment_type =~ /gateway/ ? grade_gateway($db, $userSet, $userSet->set_id, $userID) - : (grade_set($db, $userSet, $userID, 0))[ 0, 1 ]); + : ($totalRight, $total)); } # Submits scoreGiven and scoreMaximum to the lms with $sourcedid as the identifier. diff --git a/lib/WeBWorK/ConfigValues.pm b/lib/WeBWorK/ConfigValues.pm index 24cb77116c..e67c2cf7d2 100644 --- a/lib/WeBWorK/ConfigValues.pm +++ b/lib/WeBWorK/ConfigValues.pm @@ -912,6 +912,25 @@ sub getConfigValues ($ce) { labels => { '' => 'None', 'course' => 'Course', 'homework' => 'Homework' }, type => 'popuplist' }, + LTISendZeroScores => { + var => 'LTISendZeroScores', + doc => x('When to send zero scores to the LMS'), + doc2 => x( + 'If the grade passback mode is "homework", this controls when a set with score 0 will have ' + . 'that score sent to the LMS, as opposed to leaving the score null in the LMS. If the grade ' + . 'passback mode is "course", this controls when a set with score 0 will be included in the ' + . 'overall grade calculation that is sent to the LMS.' + ), + values => [qw(open reduced close answer never)], + labels => { + open => 'After Open Date', + reduced => 'After Reduced Scoring Date', + close => 'After Close Date', + answer => 'After Answer Date', + never => 'Never' + }, + type => 'popuplist' + }, LTIGradeOnSubmit => { var => 'LTIGradeOnSubmit', doc => x('Update LMS Grade Each Submit'), diff --git a/lib/WeBWorK/Utils/Sets.pm b/lib/WeBWorK/Utils/Sets.pm index 5c2d69c868..fbf31882fa 100644 --- a/lib/WeBWorK/Utils/Sets.pm +++ b/lib/WeBWorK/Utils/Sets.pm @@ -20,7 +20,7 @@ use Carp; use PGrandom; use WeBWorK::Utils qw(wwRound); -use WeBWorK::Utils::DateTime qw(after); +use WeBWorK::Utils::DateTime qw(after before); use WeBWorK::Utils::JITAR qw(jitar_id_to_seq jitar_problem_adjusted_status); our @EXPORT_OK = qw( @@ -146,7 +146,7 @@ sub grade_gateway ($db, $set, $setName, $studentName) { } } -sub grade_all_sets ($db, $studentName) { +sub grade_all_sets ($db, $studentName, $LTISendZeroScores) { my @setIDs = $db->listUserSets($studentName); my @userSetIDs = map { [ $studentName, $_ ] } @setIDs; my @userSets = $db->getMergedSets(@userSetIDs); @@ -156,17 +156,24 @@ sub grade_all_sets ($db, $studentName) { for my $userSet (@userSets) { next unless (after($userSet->open_date())); + my $totalRight; + my $total; + my %dates = ( + open => $userSet->open_date(), + reduced => $userSet->reduced_scoring_date(), + close => $userSet->close_date(), + answer => $userSet->answer_date() + ); if ($userSet->assignment_type() =~ /gateway/) { - - my ($totalRight, $total) = grade_gateway($db, $userSet, $userSet->set_id, $studentName); - $courseTotalRight += $totalRight; - $courseTotal += $total; + ($totalRight, $total) = grade_gateway($db, $userSet, $userSet->set_id, $studentName); } else { - my ($totalRight, $total) = grade_set($db, $userSet, $studentName, 0); - - $courseTotalRight += $totalRight; - $courseTotal += $total; + ($totalRight, $total) = grade_set($db, $userSet, $studentName, 0); + } + if ($totalRight == 0) { + next if ($LTISendZeroScores eq 'never' || before($dates{$LTISendZeroScores})); } + $courseTotalRight += $totalRight; + $courseTotal += $total; } if (wantarray) {