--- loncom/homework/grades.pm 2024/12/09 22:01:01 1.800 +++ 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.800 2024/12/09 22:01:01 raeburn Exp $ +# $Id: grades.pm,v 1.806 2024/12/14 17:47:39 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -1263,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; @@ -1312,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; @@ -1332,13 +1331,13 @@ sub do_passback { 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; @@ -1365,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"}; @@ -1386,7 +1385,7 @@ 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) = @@ -1408,7 +1407,7 @@ sub do_passback { 'id' => $id, '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}).'&'; @@ -1420,12 +1419,11 @@ sub do_passback { 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"; } @@ -1441,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' => $pb{'type'}, - 'pbscope' => $pbscope, - 'pbmap' => $pbmap, - 'pbsymb' => $pbsymb, - 'format' => $scoretype, - 'scope' => $scope, - 'clientip' => $pb{'clientip'}, - 'linkprot' => $linkprotector.':'.$linkuri, - '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); } @@ -1575,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 (); } @@ -1679,7 +1677,7 @@ sub passbacks_for_symb { } if (ref($passback{$symb}) eq 'HASH') { foreach my $launcher (keys(%{$passback{$symb}})) { - $needpb{$launcher} = 1; + $needpb{$launcher} = $symb; } } } @@ -1696,7 +1694,7 @@ sub passbacks_for_symb { } if (ref($passback{$mapsymb}) eq 'HASH') { foreach my $launcher (keys(%{$passback{$mapsymb}})) { - $needpb{$launcher} = 1; + $needpb{$launcher} = $mapsymb; } } } @@ -1713,7 +1711,7 @@ sub passbacks_for_symb { foreach my $launcher (keys(%{$passback{$key}})) { my ($linkuri,$linkprotector,$scope) = split("\0",$launcher); next unless ($scope eq 'rec'); - $needpb{$launcher} = 1; + $needpb{$launcher} = $key; } } } @@ -1726,7 +1724,7 @@ sub passbacks_for_symb { } sub process_passbacks { - my ($context,$symbs,$cdom,$cnum,$udom,$uname,$weights,$awardeds,$excuseds,$needpb, + 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); @@ -1778,7 +1776,7 @@ sub process_passbacks { if ($pb{'lti_in_use'}->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) { $pb{'scoretype'} = $1; } - $pb{'symb'} = &Apache::loncommon::symb_from_tinyurl($pb{'linkuri'},$cnum,$cdom); + $pb{'symb'} = $needpb->{$launcher}; if ($pb{'symb'} =~ /\.(page|sequence)$/) { $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[2]); } else { @@ -1827,6 +1825,7 @@ sub process_passbacks { 'uname' => $uname, 'udom' => $udom, 'uhome' => $uhome, + 'usec' => $usec, 'pbid' => $pbid, 'pburl' => $pburl, 'pbtype' => $pb{'type'}, @@ -1840,7 +1839,7 @@ sub process_passbacks { 'total_s' => \%total_by_symb, 'possible_s' => \%possible_by_symb, }; - push(@Apache::lonhomework::ltipassback,$ltigrade); + push(@Apache::grades::ltipassback,$ltigrade); next; } my ($total,$possible); @@ -1849,7 +1848,7 @@ sub process_passbacks { $possible = $possible_by_symb{$pb{'symb'}}; } elsif (($pb{'pbscope'} eq 'map') || ($pb{'pbscope'} eq 'nonrec')) { ($total,$possible) = - &Apache::lonhomework::get_lti_score($uname,$udom,$pb{'map'},$pb{'pbscope'}, + &Apache::lonhomework::get_lti_score($uname,$udom,$usec,$pb{'map'},$pb{'pbscope'}, \%total_by_symb,\%possible_by_symb); } if (!$possible) { @@ -4240,18 +4239,20 @@ 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,\%needpb,\%skip_passback,%pbsave); + $env{'form.unamedom'.$ctr},$part,\%queueable); if ($errorflag eq 'not_allowed') { $request->print("".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom").""); next; } else { - $pbcollab{$collaborator}{$part} = [$pts,$wgt]; + if ($numchg || $numupdate) { + $pbcollab{$collaborator}{$part} = [$pts,$wgt]; + } if ($message ne '') { my ($baseurl,$showsymb) = &get_feedurl_and_symb($symb,$collaborator, - $udom); + $udom); if ($env{'form.withgrades'.$ctr}) { $messagetail = " for $restitle"; @@ -4266,7 +4267,48 @@ sub processHandGrade { $ctr++; } if ((keys(%pbcollab)) && (keys(%needpb))) { - # FIXME passback scores for collaborators + 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); + } + } + } + } + } + } } } @@ -4429,22 +4471,18 @@ 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; - my $sendupdate; if ($env{'form.HIDE'.$newflg}) { my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2); my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1); $totchg += $numchgs; - if ($numchgs) { - $sendupdate = 1; - } } 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 '') { @@ -4461,7 +4499,7 @@ sub saveHandGrade { $newrecord{'resource.'.$new_part.'.awarded'} = ''; } $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}"; - $sendupdate = 1; + $sendupdate ++; } } elsif ($dropMenu eq 'reset status' && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts @@ -4485,7 +4523,7 @@ sub saveHandGrade { &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus); $aggregateflag = 1; } - $sendupdate = 1; + $sendupdate ++; $excuseds{$symb}{$new_part} = ''; $awardeds{$symb}{$new_part} = ''; } elsif ($dropMenu eq '') { @@ -4506,7 +4544,7 @@ sub saveHandGrade { next; } else { push(@parts_graded,$new_part); - $sendupdate = 1; + $sendupdate ++; } if ($record{'resource.'.$new_part.'.awarded'} ne $partial) { $newrecord{'resource.'.$new_part.'.awarded'} = $partial; @@ -4558,14 +4596,14 @@ sub saveHandGrade { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, $cdom,$cnum); } - if (($sendupdate) && (!$submitter)) { + if (($sendupdate || $totchg) && (!$submitter)) { if ((ref($needpb) eq 'HASH') && (keys(%{$needpb}))) { - &process_passbacks('handgrade',[$symb],$cdom,$cnum,$domain,$stuname,\%weights, + &process_passbacks('handgrade',[$symb],$cdom,$cnum,$domain,$stuname,$usec,\%weights, \%awardeds,\%excuseds,$needpb,$skip_passback,$pbsave); } } - return ('',$pts,$wgt,$totchg); + return ('',$pts,$wgt,$totchg,$sendupdate); } sub makehidden { @@ -5650,7 +5688,7 @@ sub editgrades { $updateCtr++; if (keys(%needpb)) { $weights{$symb} = \%weight; - &process_passbacks('editgrades',[$symb],$cdom,$cnum,$udom,$uname,\%weights, + &process_passbacks('editgrades',[$symb],$cdom,$cnum,$udom,$uname,$usec,\%weights, \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave); } } else { @@ -6179,7 +6217,7 @@ sub csvuploadassign { $request->print('.'); # Remove from grading queue &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,$cnum, - $domain,$username); + $domain,$username); $countdone++; if ($passback) { my @parts_in_upload; @@ -6201,7 +6239,7 @@ sub csvuploadassign { $awardeds{$symb}{$part} = $record{"resource.$part.awarded"}; } } - &process_passbacks('csvupload',[$symb],$cdom,$cnum,$domain,$username,\%weights, + &process_passbacks('csvupload',[$symb],$cdom,$cnum,$domain,$username,$usec,\%weights, \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave); } } else { @@ -6952,7 +6990,6 @@ sub updateGradeByPage { $request->print($hidemsg.$grademsg.$studentTable); if (@updates) { - undef(@Apache::lonhomework::ltipassback); my (@allsymbs,$mapsymb,@recurseup,%parentmapsymbs,%possmappb,%possrespb); @allsymbs = @updates; if (ref($map)) { @@ -6965,13 +7002,14 @@ sub updateGradeByPage { map { $parentmapsymbs{$_} = 1; } @recurseup; } my %passback = &Apache::lonnet::get('nohist_linkprot_passback',\@allsymbs,$cdom,$cnum); - my (%uniqsymbs,$use_symbs_in_map); + 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})) { @@ -6980,11 +7018,13 @@ sub updateGradeByPage { 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; } @@ -7006,17 +7046,20 @@ sub updateGradeByPage { my %pbids = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@posslaunchers,$udom,$uname); foreach my $key (keys(%pbids)) { if (ref($pbids{$key}) eq 'ARRAY') { - $needpb{$key} = 1; + if ($launch_to_symb{$key}) { + $needpb{$key} = $launch_to_symb{$key}; + } } } my @symbs = keys(%uniqsymbs); - &process_passbacks('updatebypage',\@symbs,$cdom,$cnum,$udom,$uname,\%weights, + &process_passbacks('updatebypage',\@symbs,$cdom,$cnum,$udom,$uname,$usec,\%weights, \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave,\%pbids); - if (@Apache::lonhomework::ltipassback) { + if (@Apache::grades::ltipassback) { unless ($registered_cleanup) { my $handlers = $request->get_handlers('PerlCleanupHandler'); $request->set_handlers('PerlCleanupHandler' => - [\&Apache::lonhomework::do_ltipassback,@{$handlers}]); + [\&Apache::grades::make_passback,@{$handlers}]); + $registered_cleanup=1; } } } @@ -7024,6 +7067,17 @@ sub updateGradeByPage { 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 --------- # #------------------------------------------------------------------- @@ -12540,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 @@ -12649,7 +12707,15 @@ sub assign_clicker_grades { $result.="
Failed to save student $username:$domain. Message when trying to save was ($returncode)"; } else { $storecount++; - #FIXME Do passback for $user if required + 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); + } } } } @@ -12808,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'};