--- loncom/homework/grades.pm	2024/12/09 21:39:48	1.799
+++ 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.799 2024/12/09 21:39:48 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 .= '<th>'.&mt('No.').'</th>'.
-                                           '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>'.
-                                           '<th>'.&mt('Score').'</th>';
-                                 $loop++;
+                                            '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>'.
+                                            '<th>'.&mt('Score').'</th>';
+                                $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 .= '<td align="right">'.$ctr.'&nbsp;</td>'.
-                                                       '<td>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).
-                                                       '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'.
-                                                       '<td>'.$score.'</td>'."\n";
+                                                        '<td>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).
+                                                        '&nbsp;'.$usec.($group ne '' ?'/'.$group:'').'</td>'.
+                                                        '<td>'.$score.'</td>'."\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,32 +4239,76 @@ 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("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");
 			    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);
-			    if ($env{'form.withgrades'.$ctr}) {
-				$messagetail = " for <a href=\"".
-                                    $baseurl."?symb=$showsymb\">$restitle</a>";
+			        my ($baseurl,$showsymb) = 
+				    &get_feedurl_and_symb($symb,$collaborator,
+						          $udom);
+			        if ($env{'form.withgrades'.$ctr}) {
+				    $messagetail = " for <a href=\"".
+                                        $baseurl."?symb=$showsymb\">$restitle</a>";
+			        }
+			        $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))) {
-            # 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);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
         }
     }
 
@@ -4428,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 '') {
@@ -4460,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
@@ -4484,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 '') {
@@ -4505,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;
@@ -4557,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 {
@@ -5649,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 {
@@ -6178,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;
@@ -6200,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 {
@@ -6951,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)) {
@@ -6964,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})) {
@@ -6979,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;
                     }
@@ -7005,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;
                 }
             }
         }
@@ -7023,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 ---------
 #
 #-------------------------------------------------------------------
@@ -12539,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
@@ -12648,7 +12707,15 @@ sub assign_clicker_grades {
              $result.="<br /><span class=\"LC_error\">Failed to save student $username:$domain. Message when trying to save was ($returncode)</span>";
           } 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);
+             }
           }
        }
     }
@@ -12807,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'};