--- loncom/homework/grades.pm 2024/12/09 02:29:37 1.797
+++ loncom/homework/grades.pm 2024/12/14 17:47:39 1.806
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
-# $Id: grades.pm,v 1.797 2024/12/09 02:29:37 raeburn Exp $
+# $Id: grades.pm,v 1.806 2024/12/14 17:47:39 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -67,7 +67,7 @@ my $ssi_retries = 5;
my $ssi_error;
my $ssi_error_resource;
my $ssi_error_message;
-
+my $registered_cleanup;
sub ssi_with_retries {
my ($resource, $retries, %form) = @_;
@@ -776,7 +776,7 @@ sub getclasslist {
if (($udom ne '') && ($uname ne '')) {
my %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',[$filterbypbid],$udom,$uname);
if (ref($pbinfo{$filterbypbid}) eq 'ARRAY') {
- $passback{$student} = $pbinfo{$filterbypbid}
+ $passback{$student} = $pbinfo{$filterbypbid};
} else {
delete($classlist->{$student});
next;
@@ -1061,6 +1061,11 @@ sub verifyreceipt {
return $string;
}
+#-------------------------------------------------------------------
+
+#------------------------------------------- Grade Passback Routines
+#
+
sub initialpassback {
my ($request,$symb) = @_;
my $cdom = $env{"course.$env{'request.course.id'}.domain"};
@@ -1258,18 +1263,18 @@ sub do_passback {
my $cdom = $env{"course.$env{'request.course.id'}.domain"};
my $cnum = $env{"course.$env{'request.course.id'}.num"};
my $crstype = &Apache::loncommon::course_type();
- my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
+ my ($launchsymb,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
if ($env{'form.passback'} ne '') {
$chosen = &unescape($env{'form.passback'});
($linkuri,$linkprotector,$scope) = split("\0",$chosen);
- ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
+ ($launchsymb,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
}
- if ($launcher ne '') {
- $request->print(&launcher_info_box($launcher,$appname,$setter,$linkuri,$scope));
+ if ($launchsymb ne '') {
+ $request->print(&launcher_info_box($launchsymb,$appname,$setter,$linkuri,$scope));
}
my $error;
if ($perm{'mgr'}) {
- if ($launcher ne '') {
+ if ($launchsymb ne '') {
my @poss_students = &Apache::loncommon::get_env_multiple('form.stuinfo');
if (@poss_students) {
my %possibles;
@@ -1307,12 +1312,11 @@ sub do_passback {
if ($lti_in_use->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
$scoretype = $1;
}
- my $pbsymb = &Apache::loncommon::symb_from_tinyurl($linkuri,$cnum,$cdom);
my $pbmap;
- if ($pbsymb =~ /\.(page|sequence)$/) {
- $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[2]);
+ if ($launchsymb =~ /\.(page|sequence)$/) {
+ $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($launchsymb))[2]);
} else {
- $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[0]);
+ $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($launchsymb))[0]);
}
$pbmap = &Apache::lonnet::clutter($pbmap);
my $pbscope;
@@ -1323,21 +1327,17 @@ sub do_passback {
} elsif ($scope eq 'rec') {
$pbscope = 'map';
}
- my $sigmethod = 'HMAC-SHA1';
- my $type = 'linkprot';
- my $clientip = &Apache::lonnet::get_requestor_ip();
- my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
- my $ip = &Apache::lonnet::get_host_ip($lonhost);
+ my %pb = &common_passback_info();
my $numstudents = scalar(keys(%tosend));
my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($request,$numstudents);
my $outcome = &Apache::loncommon::start_data_table().
- &Apache::loncommon::start_data_table_header_row();
+ &Apache::loncommon::start_data_table_header_row();
my $loop = 0;
while ($loop < 2) {
$outcome .= '
'.&mt('No.').' | '.
- ''.&nameUserString('header').' '.&mt('Section/Group').' | '.
- ''.&mt('Score').' | ';
- $loop++;
+ ''.&nameUserString('header').' '.&mt('Section/Group').' | '.
+ ''.&mt('Score').' | ';
+ $loop++;
}
$outcome .= &Apache::loncommon::end_data_table_header_row()."\n";
my $ctr=0;
@@ -1364,14 +1364,14 @@ sub do_passback {
$possible = 0;
my $navmap = Apache::lonnavmaps::navmap->new($uname,$udom);
if (ref($navmap)) {
- my $res = $navmap->getBySymb($pbsymb);
+ my $res = $navmap->getBySymb($launchsymb);
if (ref($res)) {
my $partlist = $res->parts();
if (ref($partlist) eq 'ARRAY') {
- my %record = &Apache::lonnet::restore($pbsymb,$env{'request.course.id'},$udom,$uname);
+ my %record = &Apache::lonnet::restore($launchsymb,$env{'request.course.id'},$udom,$uname);
foreach my $part (@{$partlist}) {
next if ($record{"resource.$part.solved"} =~/^excused/);
- my $weight = &Apache::lonnet::EXT("resource.$part.weight",$pbsymb,$udom,$uname,$usec);
+ my $weight = &Apache::lonnet::EXT("resource.$part.weight",$launchsymb,$udom,$uname,$usec);
$possible += $weight;
if (($record{'version'}) && (exists($record{"resource.$part.awarded"}))) {
my $awarded = $record{"resource.$part.awarded"};
@@ -1385,12 +1385,12 @@ sub do_passback {
}
} elsif (($pbscope eq 'map') || ($pbscope eq 'nonrec')) {
($total,$possible) =
- &Apache::lonhomework::get_lti_score($uname,$udom,$pbmap,$pbscope);
+ &Apache::lonhomework::get_lti_score($uname,$udom,$usec,$pbmap,$pbscope);
}
if (($id ne '') && ($url ne '') && ($possible)) {
my ($sent,$score,$code,$result) =
- &LONCAPA::ltiutils::send_grade($cdom,$cnum,$crsdef,$type,$ltinum,$keynum,$id,
- $url,$scoretype,$sigmethod,$msgformat,$total,$possible);
+ &LONCAPA::ltiutils::send_grade($cdom,$cnum,$crsdef,$pb{'type'},$ltinum,$keynum,$id,
+ $url,$scoretype,$pb{'sigmethod'},$msgformat,$total,$possible);
my $no_passback;
if ($sent) {
if ($code == 200) {
@@ -1398,33 +1398,32 @@ sub do_passback {
my $namespace = $cdom.'_'.$cnum.'_lp_passback';
my $store = {
'score' => $score,
- 'ip' => $ip,
- 'host' => $lonhost,
+ 'ip' => $pb{'ip'},
+ 'host' => $pb{'lonhost'},
'protector' => $linkprotector,
'deeplink' => $linkuri,
'scope' => $scope,
'url' => $url,
'id' => $id,
- 'clientip' => $clientip,
+ 'clientip' => $pb{'clientip'},
'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
- };
+ };
my $value='';
foreach my $key (keys(%{$store})) {
$value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
}
$value=~s/\&$//;
&Apache::lonnet::courselog(&escape($linkuri).':'.$uname.':'.$udom.':EXPORT:'.$value);
- &Apache::lonnet::cstore({'score' => $score},$chosen,$namespace,$udom,$uname,'',$ip,1);
+ &Apache::lonnet::cstore({'score' => $score},$chosen,$namespace,$udom,$uname,'',$pb{'ip'},1);
$ctr++;
if ($ctr%2 ==1) {
$outcome .= &Apache::loncommon::start_data_table_row();
}
- my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
$outcome .= ''.$ctr.' | '.
- ''.&nameUserString(undef,$$fullname{$student},$uname,$udom).
- ' '.$section.($group ne '' ?'/'.$group:'').' | '.
- ''.$score.' | '."\n";
+ ''.&nameUserString(undef,$$fullname{$student},$uname,$udom).
+ ' '.$usec.($group ne '' ?'/'.$group:'').' | '.
+ ''.$score.' | '."\n";
if ($ctr%2 ==0) {
$outcome .= &Apache::loncommon::end_data_table_row()."\n";
}
@@ -1440,27 +1439,31 @@ sub do_passback {
}
if ($no_passback) {
&Apache::lonnet::log($udom,$uname,$uhome,$no_passback." score: $score; total: $total; possible: $possible");
+ my $key = &Time::HiRes::time().':'.$uname.':'.$udom.':'.
+ "$linkuri\0$linkprotector\0$scope";
my $ltigrade = {
- 'ltinum' => $ltinum,
- 'lti' => $lti_in_use,
- 'crsdef' => $crsdef,
- 'cid' => $cdom.'_'.$cnum,
- 'uname' => $uname,
- 'udom' => $udom,
- 'uhome' => $uhome,
- 'pbid' => $id,
- 'pburl' => $url,
- 'pbtype' => $type,
- 'pbscope' => $pbscope,
- 'pbmap' => $pbmap,
- 'pbsymb' => $pbsymb,
- 'format' => $scoretype,
- 'scope' => $scope,
- 'clientip' => $clientip,
- 'linkprot' => $linkprotector,
- 'total' => $total,
- 'possible' => $possible,
- 'score' => $score,
+ $key => {
+ 'ltinum' => $ltinum,
+ 'lti' => $lti_in_use,
+ 'crsdef' => $crsdef,
+ 'cid' => $cdom.'_'.$cnum,
+ 'uname' => $uname,
+ 'udom' => $udom,
+ 'uhome' => $uhome,
+ 'pbid' => $id,
+ 'pburl' => $url,
+ 'pbtype' => $pb{'type'},
+ 'pbscope' => $pbscope,
+ 'pbmap' => $pbmap,
+ 'pbsymb' => $launchsymb,
+ 'format' => $scoretype,
+ 'scope' => $scope,
+ 'clientip' => $pb{'clientip'},
+ 'linkprot' => $linkprotector.':'.$linkuri,
+ 'total' => $total,
+ 'possible' => $possible,
+ 'score' => $score,
+ },
};
&Apache::lonnet::put('linkprot_passback_pending',$ltigrade,$cdom,$cnum);
}
@@ -1574,31 +1577,27 @@ sub get_passback_launcher {
}
}
}
- if ($linkuri =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
- my $key = $1;
- my $tinyurl;
- my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
- if (defined($cached)) {
- $tinyurl = $result;
- } else {
- my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
- my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
- if ($currtiny{$key} ne '') {
- $tinyurl = $currtiny{$key};
- &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
- }
- }
- if ($tinyurl) {
- my ($crsnum,$launchsymb) = split(/\&/,$tinyurl);
- if ($crsnum eq $cnum) {
- my %passback = &Apache::lonnet::get('nohist_linkprot_passback',[$launchsymb],$cdom,$cnum);
- if (ref($passback{$launchsymb}) eq 'HASH') {
- if (exists($passback{$launchsymb}{$chosen})) {
- return ($launchsymb,$appname,$setter)
- }
+ my $launchsymb = &Apache::loncommon::symb_from_tinyurl($linkuri,$cnum,$cdom);
+ if ($launchsymb eq '') {
+ my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
+ foreach my $poss_symb (keys(%passback)) {
+ if (ref($passback{$poss_symb}) eq 'HASH') {
+ if (exists($passback{$poss_symb}{$chosen})) {
+ $launchsymb = $poss_symb;
+ last;
}
}
}
+ if ($launchsymb ne '') {
+ return ($launchsymb,$appname,$setter);
+ }
+ } else {
+ my %passback = &Apache::lonnet::get('nohist_linkprot_passback',[$launchsymb],$cdom,$cnum);
+ if (ref($passback{$launchsymb}) eq 'HASH') {
+ if (exists($passback{$launchsymb}{$chosen})) {
+ return ($launchsymb,$appname,$setter);
+ }
+ }
}
return ();
}
@@ -1666,6 +1665,273 @@ sub launcher_info_box {
&Apache::lonhtmlcommon::end_pick_box().''."\n";
}
+sub passbacks_for_symb {
+ my ($cdom,$cnum,$symb) = @_;
+ my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
+ my %needpb;
+ if (keys(%passback)) {
+ my $checkpb = 1;
+ if (exists($passback{$symb})) {
+ if (keys(%passback) == 1) {
+ undef($checkpb);
+ }
+ if (ref($passback{$symb}) eq 'HASH') {
+ foreach my $launcher (keys(%{$passback{$symb}})) {
+ $needpb{$launcher} = $symb;
+ }
+ }
+ }
+ if ($checkpb) {
+ my ($map,$id,$url) = &Apache::lonnet::decode_symb($symb);
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (ref($navmap)) {
+ my $mapres = $navmap->getResourceByUrl($map);
+ if (ref($mapres)) {
+ my $mapsymb = $mapres->symb();
+ if (exists($passback{$mapsymb})) {
+ if (keys(%passback) == 1) {
+ undef($checkpb);
+ }
+ if (ref($passback{$mapsymb}) eq 'HASH') {
+ foreach my $launcher (keys(%{$passback{$mapsymb}})) {
+ $needpb{$launcher} = $mapsymb;
+ }
+ }
+ }
+ my %posspb;
+ if ($checkpb) {
+ my @recurseup = $navmap->recurseup_maps($map,1);
+ if (@recurseup) {
+ map { $posspb{$_} = 1; } @recurseup;
+ }
+ }
+ foreach my $key (keys(%passback)) {
+ if (exists($posspb{$key})) {
+ if (ref($passback{$key}) eq 'HASH') {
+ foreach my $launcher (keys(%{$passback{$key}})) {
+ my ($linkuri,$linkprotector,$scope) = split("\0",$launcher);
+ next unless ($scope eq 'rec');
+ $needpb{$launcher} = $key;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return %needpb;
+}
+
+sub process_passbacks {
+ my ($context,$symbs,$cdom,$cnum,$udom,$uname,$usec,$weights,$awardeds,$excuseds,$needpb,
+ $skip_passback,$pbsave,$pbids) = @_;
+ if ((ref($needpb) eq 'HASH') && (ref($skip_passback) eq 'HASH') && (ref($pbsave) eq 'HASH')) {
+ my (%weight,%awarded,%excused);
+ if ((ref($symbs) eq 'ARRAY') && (ref($weights) eq 'HASH') && (ref($awardeds) eq 'HASH') &&
+ (ref($excuseds) eq 'HASH')) {
+ %weight = %{$weights};
+ %awarded = %{$awardeds};
+ %excused = %{$excuseds};
+ }
+ my $uhome = &Apache::lonnet::homeserver($uname,$udom);
+ my @launchers = keys(%{$needpb});
+ my %pbinfo;
+ if (ref($pbids) eq 'HASH') {
+ %pbinfo = %{$pbids};
+ } else {
+ %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@launchers,$udom,$uname);
+ }
+ my %pbc = &common_passback_info();
+ foreach my $launcher (@launchers) {
+ if (ref($pbinfo{$launcher}) eq 'ARRAY') {
+ my $pbid = $pbinfo{$launcher}[0];
+ my $pburl = $pbinfo{$launcher}[1];
+ my (%total_by_symb,%possible_by_symb);
+ if (($pbid ne '') && ($pburl ne '')) {
+ next if ($skip_passback->{$launcher});
+ my %pb = %pbc;
+ if ((exists($pbsave->{$launcher})) &&
+ (ref($pbsave->{$launcher}) eq 'HASH')) {
+ foreach my $item ('lti_in_use','crsdef','ltinum','keynum','scoretype','msgformat',
+ 'symb','map','pbscope','linkuri','linkprotector','scope') {
+ $pb{$item} = $pbsave->{$launcher}{$item};
+ }
+ } else {
+ my $ltitype;
+ ($pb{'linkuri'},$pb{'linkprotector'},$pb{'scope'}) = split("\0",$launcher);
+ ($pb{'ltinum'},$ltitype) = ($pb{'linkprotector'} =~ /^(\d+)(c|d)$/);
+ if ($ltitype eq 'c') {
+ my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
+ $pb{'lti_in_use'} = $crslti{$pb{'ltinum'}};
+ $pb{'crsdef'} = 1;
+ } else {
+ my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
+ $pb{'lti_in_use'} = $domlti{$pb{'ltinum'}};
+ }
+ if (ref($pb{'lti_in_use'}) eq 'HASH') {
+ $pb{'msgformat'} = $pb{'lti_in_use'}->{'passbackformat'};
+ $pb{'keynum'} = $pb{'lti_in_use'}->{'cipher'};
+ $pb{'scoretype'} = 'decimal';
+ if ($pb{'lti_in_use'}->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
+ $pb{'scoretype'} = $1;
+ }
+ $pb{'symb'} = $needpb->{$launcher};
+ if ($pb{'symb'} =~ /\.(page|sequence)$/) {
+ $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[2]);
+ } else {
+ $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[0]);
+ }
+ $pb{'map'} = &Apache::lonnet::clutter($pb{'map'});
+ if ($pb{'scope'} eq 'res') {
+ $pb{'pbscope'} = 'resource';
+ } elsif ($pb{'scope'} eq 'map') {
+ $pb{'pbscope'} = 'nonrec';
+ } elsif ($pb{'scope'} eq 'rec') {
+ $pb{'pbscope'} = 'map';
+ }
+ foreach my $item ('lti_in_use','crsdef','ltinum','keynum','scoretype','msgformat',
+ 'symb','map','pbscope','linkuri','linkprotector','scope') {
+ $pbsave->{$launcher}{$item} = $pb{$item};
+ }
+ } else {
+ $skip_passback->{$launcher} = 1;
+ }
+ }
+ if (ref($symbs) eq 'ARRAY') {
+ foreach my $symb (@{$symbs}) {
+ if ((ref($weight{$symb}) eq 'HASH') && (ref($awarded{$symb}) eq 'HASH') &&
+ (ref($excused{$symb}) eq 'HASH')) {
+ foreach my $part (keys(%{$weight{$symb}})) {
+ if ($excused{$symb}{$part}) {
+ next;
+ }
+ my $partweight = $weight{$symb}{$part} eq '' ? 1 :
+ $weight{$symb}{$part};
+ if ($awarded{$symb}{$part}) {
+ $total_by_symb{$symb} += $partweight * $awarded{$symb}{$part};
+ }
+ $possible_by_symb{$symb} += $partweight;
+ }
+ }
+ }
+ }
+ if ($context eq 'updatebypage') {
+ my $ltigrade = {
+ 'ltinum' => $pb{'ltinum'},
+ 'lti' => $pb{'lti_in_use'},
+ 'crsdef' => $pb{'crsdef'},
+ 'cid' => $cdom.'_'.$cnum,
+ 'uname' => $uname,
+ 'udom' => $udom,
+ 'uhome' => $uhome,
+ 'usec' => $usec,
+ 'pbid' => $pbid,
+ 'pburl' => $pburl,
+ 'pbtype' => $pb{'type'},
+ 'pbscope' => $pb{'pbscope'},
+ 'pbmap' => $pb{'map'},
+ 'pbsymb' => $pb{'symb'},
+ 'format' => $pb{'scoretype'},
+ 'scope' => $pb{'scope'},
+ 'clientip' => $pb{'clientip'},
+ 'linkprot' => $pb{'linkprotector'}.':'.$pb{'linkuri'},
+ 'total_s' => \%total_by_symb,
+ 'possible_s' => \%possible_by_symb,
+ };
+ push(@Apache::grades::ltipassback,$ltigrade);
+ next;
+ }
+ my ($total,$possible);
+ if ($pb{'pbscope'} eq 'resource') {
+ $total = $total_by_symb{$pb{'symb'}};
+ $possible = $possible_by_symb{$pb{'symb'}};
+ } elsif (($pb{'pbscope'} eq 'map') || ($pb{'pbscope'} eq 'nonrec')) {
+ ($total,$possible) =
+ &Apache::lonhomework::get_lti_score($uname,$udom,$usec,$pb{'map'},$pb{'pbscope'},
+ \%total_by_symb,\%possible_by_symb);
+ }
+ if (!$possible) {
+ $total = 0;
+ $possible = 1;
+ }
+ my ($sent,$score,$code,$result) =
+ &LONCAPA::ltiutils::send_grade($cdom,$cnum,$pb{'crsdef'},$pb{'type'},$pb{'ltinum'},
+ $pb{'keynum'},$pbid,$pburl,$pb{'scoretype'},$pb{'sigmethod'},
+ $pb{'msgformat'},$total,$possible);
+ my $no_passback;
+ if ($sent) {
+ if ($code == 200) {
+ my $namespace = $cdom.'_'.$cnum.'_lp_passback';
+ my $store = {
+ 'score' => $score,
+ 'ip' => $pb{'ip'},
+ 'host' => $pb{'lonhost'},
+ 'protector' => $pb{'linkprotector'},
+ 'deeplink' => $pb{'linkuri'},
+ 'scope' => $pb{'scope'},
+ 'url' => $pburl,
+ 'id' => $pbid,
+ 'clientip' => $pb{'clientip'},
+ 'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
+ };
+ my $value='';
+ foreach my $key (keys(%{$store})) {
+ $value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
+ }
+ $value=~s/\&$//;
+ &Apache::lonnet::courselog(&escape($pb{'linkuri'}).':'.$uname.':'.$udom.':EXPORT:'.$value);
+ &Apache::lonnet::cstore({'score' => $score},$launcher,$namespace,$udom,$uname,'',$pb{'ip'},1);
+ } else {
+ $no_passback = 1;
+ }
+ } else {
+ $no_passback = 1;
+ }
+ if ($no_passback) {
+ &Apache::lonnet::log($udom,$uname,$uhome,$no_passback." score: $score; total: $total; possible: $possible");
+ my $ltigrade = {
+ 'ltinum' => $pb{'ltinum'},
+ 'lti' => $pb{'lti_in_use'},
+ 'crsdef' => $pb{'crsdef'},
+ 'cid' => $cdom.'_'.$cnum,
+ 'uname' => $uname,
+ 'udom' => $udom,
+ 'uhome' => $uhome,
+ 'pbid' => $pbid,
+ 'pburl' => $pburl,
+ 'pbtype' => $pb{'type'},
+ 'pbscope' => $pb{'pbscope'},
+ 'pbmap' => $pb{'map'},
+ 'pbsymb' => $pb{'symb'},
+ 'format' => $pb{'scoretype'},
+ 'scope' => $pb{'scope'},
+ 'clientip' => $pb{'clientip'},
+ 'linkprot' => $pb{'linkprotector'}.':'.$pb{'linkuri'},
+ 'total' => $total,
+ 'possible' => $possible,
+ 'score' => $score,
+ };
+ &Apache::lonnet::put('linkprot_passback_pending',$ltigrade,$cdom,$cnum);
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub common_passback_info {
+ my %pbc = (
+ sigmethod => 'HMAC-SHA1',
+ type => 'linkprot',
+ clientip => &Apache::lonnet::get_requestor_ip(),
+ lonhost => $Apache::lonnet::perlvar{'lonHostID'},
+ ip => &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}),
+ );
+ return %pbc;
+}
+
#--- This is called by a number of programs.
#--- Called from the Grading Menu - View/Grade an individual student
#--- Also called directly when one clicks on the subm button
@@ -3914,11 +4180,13 @@ sub processHandGrade {
}
if ($button eq 'Save & Next') {
+ my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
+ my (%skip_passback,%pbsave,%pbcollab);
my $ctr = 0;
while ($ctr < $ngrade) {
my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
my ($errorflag,$pts,$wgt,$numhidden) =
- &saveHandGrade($request,$symb,$uname,$udom,$ctr,undef,undef,\%queueable);
+ &saveHandGrade($request,$symb,$uname,$udom,$ctr,undef,undef,\%queueable,\%needpb,\%skip_passback,\%pbsave);
if ($errorflag eq 'no_score') {
$ctr++;
next;
@@ -3971,28 +4239,77 @@ sub processHandGrade {
foreach my $collabstr (@collabstrs) {
my ($part,@collaborators) = split(/:/,$collabstr);
foreach my $collaborator (@collaborators) {
- my ($errorflag,$pts,$wgt) =
+ my ($errorflag,$pts,$wgt,$numchg,$numupdate) =
&saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
$env{'form.unamedom'.$ctr},$part,\%queueable);
if ($errorflag eq 'not_allowed') {
$request->print("".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."");
next;
- } elsif ($message ne '') {
- my ($baseurl,$showsymb) =
- &get_feedurl_and_symb($symb,$collaborator,
- $udom);
- if ($env{'form.withgrades'.$ctr}) {
- $messagetail = " for $restitle";
+ } else {
+ if ($numchg || $numupdate) {
+ $pbcollab{$collaborator}{$part} = [$pts,$wgt];
+ }
+ if ($message ne '') {
+ my ($baseurl,$showsymb) =
+ &get_feedurl_and_symb($symb,$collaborator,
+ $udom);
+ if ($env{'form.withgrades'.$ctr}) {
+ $messagetail = " for $restitle";
+ }
+ $msgstatus =
+ &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
}
- $msgstatus =
- &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
- }
+ }
}
}
}
$ctr++;
}
+ if ((keys(%pbcollab)) && (keys(%needpb))) {
+ foreach my $user (keys(%pbcollab)) {
+ my ($clbuname,$clbudom) = split(/:/,$user);
+ my $clbusec = &Apache::lonnet::getsection($clbudom,$clbuname,$cdom.'_'.$cnum);
+ if (ref($pbcollab{$user}) eq 'HASH') {
+ my @clparts = keys(%{$pbcollab{$user}});
+ if (@clparts) {
+ my $navmap = Apache::lonnavmaps::navmap->new($clbuname,$clbudom,$clbusec);
+ if (ref($navmap)) {
+ my $res = $navmap->getBySymb($symb);
+ if (ref($res)) {
+ my $partlist = $res->parts();
+ if (ref($partlist) eq 'ARRAY') {
+ my (%weights,%awardeds,%excuseds);
+ foreach my $part (@{$partlist}) {
+ if ($res->status($part) eq $res->EXCUSED) {
+ $excuseds{$symb}{$part} = 1;
+ } else {
+ $excuseds{$symb}{$part} = '';
+ }
+ if ((exists($pbcollab{$user}{$part})) && (ref($pbcollab{$user}{$part}) eq 'ARRAY')) {
+ my $pts = $pbcollab{$user}{$part}[0];
+ my $wt = $pbcollab{$user}{$part}[1];
+ if ($wt) {
+ $awardeds{$symb}{$part} = $pts/$wt;
+ $weights{$symb}{$part} = $wt;
+ } else {
+ $awardeds{$symb}{$part} = 0;
+ $weights{$symb}{$part} = 0;
+ }
+ } else {
+ $awardeds{$symb}{$part} = $res->awarded($part);
+ $weights{$symb}{$part} = $res->weight($part);
+ }
+ }
+ &process_passbacks('handgrade',[$symb],$cdom,$cnum,$clbudom,$clbuname,$clbusec,\%weights,
+ \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
my %keyhash = ();
@@ -4146,7 +4463,7 @@ sub processHandGrade {
#---- Save the score and award for each student, if changed
sub saveHandGrade {
- my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part,$queueable) = @_;
+ my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part,$queueable,$needpb,$skip_passback,$pbsave) = @_;
my @version_parts;
my $usec = &Apache::lonnet::getsection($domain,$stuname,
$env{'request.course.id'});
@@ -4154,7 +4471,7 @@ sub saveHandGrade {
my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
my @parts_graded;
my %newrecord = ();
- my ($pts,$wgt,$totchg) = ('','',0);
+ my ($pts,$wgt,$totchg,$sendupdate) = ('','',0,0);
my %aggregate = ();
my $aggregateflag = 0;
if ($env{'form.HIDE'.$newflg}) {
@@ -4162,18 +4479,27 @@ sub saveHandGrade {
my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);
$totchg += $numchgs;
}
+ my (%weights,%awardeds,%excuseds);
my @parts = split(/:/,$env{'form.partlist'.$newflg});
foreach my $new_part (@parts) {
- #collaborator ($submi may vary for different parts
+ #collaborator ($submitter may vary for different parts)
if ($submitter && $new_part ne $part) { next; }
my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
+ if ($env{'form.WGT'.$newflg.'_'.$new_part} eq '') {
+ $weights{$symb}{$new_part} = 1;
+ } else {
+ $weights{$symb}{$new_part} = $env{'form.WGT'.$newflg.'_'.$new_part};
+ }
if ($dropMenu eq 'excused') {
+ $excuseds{$symb}{$new_part} = 1;
+ $awardeds{$symb}{$new_part} = '';
if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {
$newrecord{'resource.'.$new_part.'.solved'} = 'excused';
if (exists($record{'resource.'.$new_part.'.awarded'})) {
$newrecord{'resource.'.$new_part.'.awarded'} = '';
}
$newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
+ $sendupdate ++;
}
} elsif ($dropMenu eq 'reset status'
&& exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
@@ -4197,6 +4523,9 @@ sub saveHandGrade {
&decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);
$aggregateflag = 1;
}
+ $sendupdate ++;
+ $excuseds{$symb}{$new_part} = '';
+ $awardeds{$symb}{$new_part} = '';
} elsif ($dropMenu eq '') {
$pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ?
$env{'form.GD_BOX'.$newflg.'_'.$new_part} :
@@ -4207,12 +4536,15 @@ sub saveHandGrade {
$wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 :
$env{'form.WGT'.$newflg.'_'.$new_part};
my $partial= $pts/$wgt;
+ $awardeds{$symb}{$new_part} = $partial;
+ $excuseds{$symb}{$new_part} = '';
if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
#do not update score for part if not changed.
&handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
next;
} else {
push(@parts_graded,$new_part);
+ $sendupdate ++;
}
if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
$newrecord{'resource.'.$new_part.'.awarded'} = $partial;
@@ -4264,7 +4596,14 @@ sub saveHandGrade {
&Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
$cdom,$cnum);
}
- return ('',$pts,$wgt,$totchg);
+ if (($sendupdate || $totchg) && (!$submitter)) {
+ if ((ref($needpb) eq 'HASH') &&
+ (keys(%{$needpb}))) {
+ &process_passbacks('handgrade',[$symb],$cdom,$cnum,$domain,$stuname,$usec,\%weights,
+ \%awardeds,\%excuseds,$needpb,$skip_passback,$pbsave);
+ }
+ }
+ return ('',$pts,$wgt,$totchg,$sendupdate);
}
sub makehidden {
@@ -5151,6 +5490,10 @@ sub editgrades {
);
my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
+
my (@partid);
my %weight = ();
my %columns = ();
@@ -5201,7 +5544,7 @@ sub editgrades {
&Apache::loncommon::end_data_table_header_row();
my @noupdate;
my ($updateCtr,$noupdateCtr) = (1,1);
- my ($got_types,%queueable);
+ my ($got_types,%queueable,%pbsave,%skip_passback);
for ($i=0; $i<$env{'form.total'}; $i++) {
my $user = $env{'form.ctr'.$i};
my ($uname,$udom)=split(/:/,$user);
@@ -5220,6 +5563,7 @@ sub editgrades {
my %aggregate = ();
my $aggregateflag = 0;
$user=~s/:/_/; # colon doen't work in javascript for names
+ my (%weights,%awardeds,%excuseds);
foreach (@partid) {
my $old_aw = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};
my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
@@ -5228,6 +5572,7 @@ sub editgrades {
my $awarded = $env{'form.GD_'.$user.'_'.$_.'_awarded'};
my $pcr = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
my $partial = $awarded eq '' ? '' : $pcr;
+ $awardeds{$symb}{$_} = $partial;
my $score;
if ($partial eq '') {
$score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
@@ -5268,6 +5613,11 @@ sub editgrades {
my $partid=$_;
+ if ($score eq 'excused') {
+ $excuseds{$symb}{$partid} = 1;
+ } else {
+ $excuseds{$symb}{$partid} = '';
+ }
foreach my $stores (@parts) {
my ($part,$type) = &split_part_type($stores);
if ($part !~ m/^\Q$partid\E/) { next;}
@@ -5285,9 +5635,6 @@ sub editgrades {
}
$line.="\n";
- my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
- my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
-
if ($updateflag) {
$count++;
&Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},
@@ -5339,6 +5686,11 @@ sub editgrades {
' '.$updateCtr.' | '.$line.
&Apache::loncommon::end_data_table_row();
$updateCtr++;
+ if (keys(%needpb)) {
+ $weights{$symb} = \%weight;
+ &process_passbacks('editgrades',[$symb],$cdom,$cnum,$udom,$uname,$usec,\%weights,
+ \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
+ }
} else {
push(@noupdate,
' '.$noupdateCtr.' | '.$line);
@@ -5704,11 +6056,33 @@ sub csvuploadassign {
my @gradedata = &Apache::loncommon::upfile_record_sep();
my %fields=&get_fields();
my $courseid=$env{'request.course.id'};
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
my ($classlist) = &getclasslist('all',0);
my @notallowed;
my @skipped;
my @warnings;
my $countdone=0;
+ my @parts;
+ my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
+ my $passback;
+ if (keys(%needpb)) {
+ $passback = 1;
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (ref($navmap)) {
+ my $res = $navmap->getBySymb($symb);
+ if (ref($res)) {
+ my $partlist = $res->parts();
+ if (ref($partlist) eq 'ARRAY') {
+ @parts = sort(@{$partlist});
+ }
+ }
+ } else {
+ return &navmap_errormsg();
+ }
+ }
+ my (%skip_passback,%pbsave,%weights,%awardeds,%excuseds);
+
foreach my $grade (@gradedata) {
my %entries=&Apache::loncommon::record_sep($grade);
my $domain;
@@ -5783,9 +6157,14 @@ sub csvuploadassign {
my $part=$1;
my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
$symb,$domain,$username);
+ $weights{$symb}{$part} = $wgt;
if ($wgt) {
$entries{$fields{$dest}}=~s/\s//g;
my $pcr=$entries{$fields{$dest}} / $wgt;
+ if ($passback) {
+ $awardeds{$symb}{$part} = $pcr;
+ $excuseds{$symb}{$part} = '';
+ }
my $award=($pcr == 0) ? 'incorrect_by_override'
: 'correct_by_override';
if ($pcr>1) {
@@ -5805,6 +6184,22 @@ sub csvuploadassign {
if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
if ($dest=~/stores_(.*)_solved/) { if ($points{$1}) {next;} }
my $store_key=$dest;
+ if ($passback) {
+ if ($store_key=~/stores_(.*)_(awarded|solved)/) {
+ my ($part,$key) = ($1,$2);
+ unless ((ref($weights{$symb}) eq 'HASH') && (exists($weights{$symb}{$part}))) {
+ $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',
+ $symb,$domain,$username);
+ }
+ if ($key eq 'awarded') {
+ $awardeds{$symb}{$part} = $entries{$fields{$dest}};
+ } elsif ($key eq 'solved') {
+ if ($entries{$fields{$dest}} =~ /^excused/) {
+ $excuseds{$symb}{$part} = 1;
+ }
+ }
+ }
+ }
$store_key=~s/^stores/resource/;
$store_key=~s/_/\./g;
$grades{$store_key}=$entries{$fields{$dest}};
@@ -5821,11 +6216,32 @@ sub csvuploadassign {
# Successfully stored
$request->print('.');
# Remove from grading queue
- &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,
- $env{'course.'.$env{'request.course.id'}.'.domain'},
- $env{'course.'.$env{'request.course.id'}.'.num'},
- $domain,$username);
+ &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,$cnum,
+ $domain,$username);
$countdone++;
+ if ($passback) {
+ my @parts_in_upload;
+ if (ref($weights{$symb}) eq 'HASH') {
+ @parts_in_upload = sort(keys(%{$weights{$symb}}));
+ }
+ my @diffs = &Apache::loncommon::compare_arrays(\@parts_in_upload,\@parts);
+ if (@diffs > 0) {
+ my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$username);
+ foreach my $part (@parts) {
+ next if (grep(/^\Q$part\E$/,@parts_in_upload));
+ $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',
+ $symb,$domain,$username);
+ if ($record{"resource.$part.solved"} =~/^excused/) {
+ $excuseds{$symb}{$part} = 1;
+ } else {
+ $excuseds{$symb}{$part} = '';
+ }
+ $awardeds{$symb}{$part} = $record{"resource.$part.awarded"};
+ }
+ }
+ &process_passbacks('csvupload',[$symb],$cdom,$cnum,$domain,$username,$usec,\%weights,
+ \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
+ }
} else {
$request->print("".
&mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
@@ -6421,6 +6837,7 @@ sub updateGradeByPage {
$iterator->next(); # skip the first BEGIN_MAP
my $curRes = $iterator->next(); # for "current resource"
my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);
+ my (@updates,%weights,%excuseds,%awardeds,@symbs_in_map);
while ($depth > 0) {
if($curRes == $iterator->BEGIN_MAP) { $depth++; }
if($curRes == $iterator->END_MAP) { $depth--; }
@@ -6429,6 +6846,7 @@ sub updateGradeByPage {
my $parts = $curRes->parts();
my $title = $curRes->compTitle();
my $symbx = $curRes->symb();
+ push(@symbs_in_map,$symbx);
$studentTable.=
&Apache::loncommon::start_data_table_row().
'
'.$prob.
@@ -6446,6 +6864,9 @@ sub updateGradeByPage {
my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);
my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);
+ if ($numchgs) {
+ push(@updates,$symbx);
+ }
$hideflag += $numchgs;
}
foreach my $partid (@{$parts}) {
@@ -6467,6 +6888,8 @@ sub updateGradeByPage {
}
my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ?
$env{'form.WGT'.$question.'_'.$partid} : 1;
+ $weights{$symbx}{$partid} = $wgt;
+ $excuseds{$symbx}{$partid} = '';
my $partial = $newpts/$wgt;
my $score;
if ($partial > 0) {
@@ -6478,6 +6901,7 @@ sub updateGradeByPage {
if ($dropMenu eq 'excused') {
$partial = '';
$score = 'excused';
+ $excuseds{$symbx}{$partid} = 1;
} elsif ($dropMenu eq 'reset status'
&& $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists
$newrecord{'resource.'.$partid.'.tries'} = 0;
@@ -6505,6 +6929,11 @@ sub updateGradeByPage {
(($score eq 'excused') ? 'excused' : $newpts).
' ';
$question++;
+ if (($newpts eq '') || ($partial eq '')) {
+ $awardeds{$symbx}{$partid} = 0;
+ } else {
+ $awardeds{$symbx}{$partid} = $partial;
+ }
next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused'));
$newrecord{'resource.'.$partid.'.awarded'} = $partial if $partial ne '';
@@ -6544,6 +6973,9 @@ sub updateGradeByPage {
&Apache::loncommon::end_data_table_row();
$prob++;
+ if ($changeflag) {
+ push(@updates,$symbx);
+ }
}
$curRes = $iterator->next();
}
@@ -6557,9 +6989,95 @@ sub updateGradeByPage {
$hideflag).' ');
$request->print($hidemsg.$grademsg.$studentTable);
+ if (@updates) {
+ my (@allsymbs,$mapsymb,@recurseup,%parentmapsymbs,%possmappb,%possrespb);
+ @allsymbs = @updates;
+ if (ref($map)) {
+ $mapsymb = $map->symb();
+ push(@allsymbs,$mapsymb);
+ @recurseup = $navmap->recurseup_maps($map->src,1);
+ }
+ if (@recurseup) {
+ push(@allsymbs,@recurseup);
+ map { $parentmapsymbs{$_} = 1; } @recurseup;
+ }
+ my %passback = &Apache::lonnet::get('nohist_linkprot_passback',\@allsymbs,$cdom,$cnum);
+ my (%uniqsymbs,$use_symbs_in_map,%launch_to_symb);
+ if (keys(%passback)) {
+ foreach my $possible (keys(%passback)) {
+ if (ref($passback{$possible}) eq 'HASH') {
+ if ($possible eq $mapsymb) {
+ foreach my $launcher (keys(%{$passback{$possible}})) {
+ $possmappb{$launcher} = 1;
+ $launch_to_symb{$launcher} = $possible;
+ }
+ $use_symbs_in_map = 1;
+ } elsif (exists($parentmapsymbs{$possible})) {
+ foreach my $launcher (keys(%{$passback{$possible}})) {
+ my ($linkuri,$linkprotector,$scope) = split(/\0/,$launcher);
+ if ($scope eq 'rec') {
+ $possmappb{$launcher} = 1;
+ $use_symbs_in_map = 1;
+ $launch_to_symb{$launcher} = $possible;
+ }
+ }
+ } elsif (grep(/^\Q$possible$\E$/,@updates)) {
+ foreach my $launcher (keys(%{$passback{$possible}})) {
+ $possrespb{$launcher} = 1;
+ $launch_to_symb{$launcher} = $possible;
+ }
+ $uniqsymbs{$possible} = 1;
+ }
+ }
+ }
+ }
+ if ($use_symbs_in_map) {
+ map { $uniqsymbs{$_} = 1; } @symbs_in_map;
+ }
+ my @posslaunchers;
+ if (keys(%possmappb)) {
+ push(@posslaunchers,keys(%possmappb));
+ }
+ if (keys(%possrespb)) {
+ push(@posslaunchers,keys(%possrespb));
+ }
+ if (@posslaunchers) {
+ my (%pbsave,%skip_passback,%needpb);
+ my %pbids = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@posslaunchers,$udom,$uname);
+ foreach my $key (keys(%pbids)) {
+ if (ref($pbids{$key}) eq 'ARRAY') {
+ if ($launch_to_symb{$key}) {
+ $needpb{$key} = $launch_to_symb{$key};
+ }
+ }
+ }
+ my @symbs = keys(%uniqsymbs);
+ &process_passbacks('updatebypage',\@symbs,$cdom,$cnum,$udom,$uname,$usec,\%weights,
+ \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave,\%pbids);
+ if (@Apache::grades::ltipassback) {
+ unless ($registered_cleanup) {
+ my $handlers = $request->get_handlers('PerlCleanupHandler');
+ $request->set_handlers('PerlCleanupHandler' =>
+ [\&Apache::grades::make_passback,@{$handlers}]);
+ $registered_cleanup=1;
+ }
+ }
+ }
+ }
return '';
}
+sub make_passback {
+ if (@Apache::grades::ltipassback) {
+ my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
+ my $ip = &Apache::lonnet::get_host_ip($lonhost);
+ foreach my $item (@Apache::grades::ltipassback) {
+ &Apache::lonhomework::run_passback($item,$lonhost,$ip);
+ }
+ undef(@Apache::grades::ltipassback);
+ }
+}
+
#-------- end of section for handling grading by page/sequence ---------
#
#-------------------------------------------------------------------
@@ -12076,6 +12594,10 @@ sub assign_clicker_grades {
if ($res_error) {
return &navmap_errormsg();
}
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
+ my (%skip_passback,%pbsave);
# FIXME: This should probably look for the first handgradeable part
my $part=$$partlist[0];
# Start screen output
@@ -12185,6 +12707,15 @@ sub assign_clicker_grades {
$result.=" Failed to save student $username:$domain. Message when trying to save was ($returncode)";
} else {
$storecount++;
+ if (keys(%needpb)) {
+ my (%weights,%awardeds,%excuseds);
+ my $usec = &Apache::lonnet::getsection($domain,$username,$env{'request.course.id'});
+ $weights{$symb}{$part} = &Apache::lonnet::EXT("resource.$part.weight",$symb,$domain,$username,$usec);
+ $awardeds{$symb}{$part} = $ave;
+ $excuseds{$symb}{$part} = '';
+ &process_passbacks('clickergrade',[$symb],$cdom,$cnum,$domain,$username,$usec,\%weights,
+ \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
+ }
}
}
}
@@ -12343,6 +12874,10 @@ sub handler {
&Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
}
+# -------------------------------------- Flag and buffer for registered cleanup
+ $registered_cleanup=0;
+ undef(@Apache::grades::ltipassback);
+
# see what the symb is
my $symb=$env{'form.symb'};
|