--- loncom/homework/grades.pm	2017/07/02 16:50:30	1.741
+++ loncom/homework/grades.pm	2017/12/31 14:00:41	1.749
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.741 2017/07/02 16:50:30 raeburn Exp $
+# $Id: grades.pm,v 1.749 2017/12/31 14:00:41 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -116,7 +116,11 @@ sub getpartlist {
     my $res      = $navmap->getBySymb($symb);
     my $partlist = $res->parts();
     my $url      = $res->src();
-    my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));
+    my $toolsymb;
+    if ($url =~ /ext\.tool$/) {
+        $toolsymb = $symb;
+    }
+    my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys',$toolsymb));
 
     my @stores;
     foreach my $part (@{ $partlist }) {
@@ -852,6 +856,7 @@ sub verifyreceipt {
 sub listStudents {
     my ($request,$symb,$submitonly) = @_;
 
+    my $is_tool   = ($symb =~ /ext\.tool$/);
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
@@ -907,38 +912,66 @@ LISTJAVASCRIPT
 	"\n";
 	
     $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
-    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
-                  .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
-                  .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
-                  .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
-                  .&Apache::lonhtmlcommon::row_closure();
-    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
-                  .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
-                  .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
-                  .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
-                  .&Apache::lonhtmlcommon::row_closure();
+    unless ($is_tool) {
+        $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
+                      .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
+                      .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
+                      .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
+                      .&Apache::lonhtmlcommon::row_closure();
+        $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
+                      .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
+                      .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
+                      .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
+                      .&Apache::lonhtmlcommon::row_closure();
+    }
 
     my $submission_options;
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;
     $env{'form.Status'} = $saveStatus;
+    my %optiontext;
+    if ($is_tool) {
+        %optiontext = &Apache::lonlocal::texthash (
+                          lastonly => 'last transaction',
+                          last     => 'last transaction with details',
+                          datesub  => 'all transactions',
+                          all      => 'all transactions with details',
+                      );
+    } else {
+        %optiontext = &Apache::lonlocal::texthash (
+                          lastonly => 'last submission',
+                          last     => 'last submission with details',
+                          datesub  => 'all submissions',
+                          all      => 'all submissions with details',
+                      );
+    }
     $submission_options.=
         '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="lastonly" /> '.
-        &mt('last submission').' </label></span>'."\n".
+        $optiontext{'lastonly'}.' </label></span>'."\n".
         '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="last" /> '.
-        &mt('last submission with details').' </label></span>'."\n".
+        $optiontext{'last'}.' </label></span>'."\n".
         '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.
-        &mt('all submissions').'</label></span>'."\n".
+        $optiontext{'datesub'}.'</label></span>'."\n".
         '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="all" /> '.
-        &mt('all submissions with details').'</label></span>';
-    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Submissions'))
+        $optiontext{'all'}.'</label></span>';
+    my $viewtitle;
+    if ($is_tool) {
+        $viewtitle = &mt('View Transactions');
+    } else {
+        $viewtitle = &mt('View Submissions');
+    }
+    $gradeTable .= &Apache::lonhtmlcommon::row_title($viewtitle)
                   .$submission_options
                   .&Apache::lonhtmlcommon::row_closure();
 
+    my $closure;
+    if (($is_tool) && (exists($env{'form.Status'}))) {
+        $closure = 1;
+    }
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
                   .'<select name="increment">'
                   .'<option value="1">'.&mt('Whole Points').'</option>'
@@ -946,7 +979,7 @@ LISTJAVASCRIPT
                   .'<option value=".25">'.&mt('Quarter Points').'</option>'
                   .'<option value=".1">'.&mt('Tenths of a Point').'</option>'
                   .'</select>'
-                  .&Apache::lonhtmlcommon::row_closure();
+                  .&Apache::lonhtmlcommon::row_closure($closure);
 
     $gradeTable .= 
         &build_section_inputs().
@@ -957,19 +990,30 @@ LISTJAVASCRIPT
     if (exists($env{'form.Status'})) {
 	$gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
     } else {
+        if ($is_tool) {
+            $closure = 1;
+        }
         $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))
                       .&Apache::lonhtmlcommon::StatusOptions(
                            $saveStatus,undef,1,'javascript:reLoadList(this.form);')
-                      .&Apache::lonhtmlcommon::row_closure();
+                      .&Apache::lonhtmlcommon::row_closure($closure);
     }
 
-    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
-                  .'<input type="checkbox" name="checkPlag" checked="checked" />'
-                  .&Apache::lonhtmlcommon::row_closure(1)
-                  .&Apache::lonhtmlcommon::end_pick_box();
-
+    unless ($is_tool) {
+        $closure = 1;
+        $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
+                      .'<input type="checkbox" name="checkPlag" checked="checked" />'
+                      .&Apache::lonhtmlcommon::row_closure($closure);
+    }
+    $gradeTable .= &Apache::lonhtmlcommon::end_pick_box();
+    my $regrademsg;
+    if ($is_tool) {
+        $regrademsg =&mt("To view/grade/regrade, click on the check box(es) next to the student's name(s). Then click on the Next button.");
+    } else {
+        $regrademsg = &mt("To view/grade/regrade a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.");
+    }
     $gradeTable .= '<p>'
-                  .&mt("To view/grade/regrade a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n"
+                  .$regrademsg."\n"
                   .'<input type="hidden" name="command" value="processGroup" />'
                   .'</p>';
 
@@ -1977,6 +2021,7 @@ sub submission {
 
     my $probtitle=&Apache::lonnet::gettitle($symb); 
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
+    my $is_tool = ($symb =~ /ext\.tool$/);
 
     if (!&canview($usec)) {
         $request->print(
@@ -1989,8 +2034,10 @@ sub submission {
     }
 
     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
-    if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
-    if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
+    unless ($is_tool) { 
+        if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
+        if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
+    }
     my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
     my $checkIcon = '<img alt="'.&mt('Check Mark').
 	'" src="'.$request->dir_config('lonIconsURL').
@@ -2078,7 +2125,7 @@ sub submission {
 	$request->print($prnmsg);
 
 #	if ($env{'form.handgrade'} eq 'yes') {
-        if (1) {
+        unless ($is_tool) {
 
             my %lt = &Apache::lonlocal::texthash(
                           keyh => 'Keyword Highlighting for Essays',
@@ -2163,12 +2210,16 @@ sub submission {
     # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));
 
+    my $boxtitle = &mt('Submissions');
+    if ($is_tool) {
+        $boxtitle = &mt('Transactions')
+    }
     my $result='<div class="LC_Box">'
-              .'<h3 class="LC_hcell">'.&mt('Submissions').'</h3>';
+              .'<h3 class="LC_hcell">'.$boxtitle.'</h3>';
     $result.='<input type="hidden" name="name'.$counter.
              '" value="'.$env{'form.fullname'}.'" />'."\n";
 #    if ($env{'form.handgrade'} eq 'no') {
-    if (1) {
+    unless ($is_tool) {
         $result.='<p class="LC_info">'
                 .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
                 ."</p>\n";
@@ -2178,7 +2229,7 @@ sub submission {
     my $fullname;
     my $col_fullnames = [];
 #    if ($env{'form.handgrade'} eq 'yes') {
-    if (1) {
+    unless ($is_tool) {
 	(my $sub_result,$fullname,$col_fullnames)=
 	    &check_collaborators($symb,$uname,$udom,\%record,$handgrade,
 				 $counter);
@@ -2193,12 +2244,16 @@ sub submission {
     #             (3) Last submission plus the parts info
     #             (4) The whole record for this student
     
-    my ($string,$timestamp)= &get_last_submission(\%record);
+    my ($string,$timestamp)= &get_last_submission(\%record,$is_tool);
 	
     my $lastsubonly;
 
     if ($$timestamp eq '') {
         $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>'; 
+    } elsif ($is_tool) {
+        $lastsubonly =
+            '<div class="LC_grade_submissions_body">'
+           .'<b>'.&mt('Date Grade Passed Back:').'</b> '.$$timestamp."</div>\n";
     } else {
         $lastsubonly =
             '<div class="LC_grade_submissions_body">'
@@ -2381,7 +2436,12 @@ sub submission {
     my %seen = ();
     my @partlist;
     my @gradePartRespid;
-    my @part_response_id = &flatten_responseType($responseType);
+    my @part_response_id;
+    if ($is_tool) {
+        @part_response_id = ([0,'']);
+    } else {
+        @part_response_id = &flatten_responseType($responseType);
+    }
     $request->print(
         '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'
@@ -2507,7 +2567,7 @@ sub check_collaborators {
 
 #--- Retrieve the last submission for all the parts
 sub get_last_submission {
-    my ($returnhash)=@_;
+    my ($returnhash,$is_tool)=@_;
     my (@string,$timestamp,%lasthidden);
     if ($$returnhash{'version'}) {
 	my %lasthash=();
@@ -2573,8 +2633,14 @@ sub get_last_submission {
 	}
     }
     if (!@string) {
+        my $msg;
+        if ($is_tool) {
+            $msg = &mt('No grade passed back.');
+        } else {
+            $msg = &mt('Nothing submitted - no attempts.');
+        }
 	$string[0] =
-	    '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';
+	    '<span class="LC_warning">'.$msg.'</span>';
     }
     return (\@string,\$timestamp);
 }
@@ -3561,6 +3627,11 @@ VIEWJAVASCRIPT
 #--- show scores for a section or whole class w/ option to change/update a score
 sub viewgrades {
     my ($request,$symb) = @_;
+    my ($is_tool,$toolsymb);
+    if ($symb =~ /ext\.tool$/) {
+        $is_tool = 1;
+        $toolsymb = $symb;
+    }
     &viewgrades_js($request);
 
     #need to make sure we have the correct data for later EXT calls, 
@@ -3641,7 +3712,13 @@ sub viewgrades {
     if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$common_header.'</h3>';
     } else {
-        $result.= '<h3>'.$common_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';
+        my $text;
+        if ($is_tool) {
+            $text = &mt('(transaction status: "[_1]")',$submission_status);
+        } else {
+            $text = &mt('(submission status: "[_1]")',$submission_status);
+        }
+        $result.= '<h3>'.$common_header.'&nbsp;'.$text.'</h3>';
     }
     $result .= &Apache::loncommon::start_data_table();
     #radio buttons/text box for assigning points for a section or class.
@@ -3654,13 +3731,18 @@ sub viewgrades {
     my %weight = ();
     my $ctsparts = 0;
     my %seen = ();
-    my @part_response_id = &flatten_responseType($responseType);
+    my @part_response_id;
+    if ($is_tool) {
+        @part_response_id = ([0,'']);
+    } else {
+        @part_response_id = &flatten_responseType($responseType);
+    }
     foreach my $part_response_id (@part_response_id) {
     	my ($partid,$respid) = @{ $part_response_id };
 	my $part_resp = join('_',@{ $part_response_id });
 	next if $seen{$partid};
 	$seen{$partid}++;
-	my $handgrade=$$handgrade{$part_resp};
+#	my $handgrade=$$handgrade{$part_resp};
 	my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
 	$weight{$partid} = $wgt eq '' ? '1' : $wgt;
 
@@ -3709,7 +3791,13 @@ sub viewgrades {
     if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$specific_header.'</h3>';
     } else {
-        $result.= '<h3>'.$specific_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';
+        my $text;
+        if ($is_tool) {
+            $text = &mt('(transaction status: "[_1]")',$submission_status);
+        } else {
+            $text = &mt('(submission status: "[_1]")',$submission_status);
+        }
+        $result.= '<h3>'.$specific_header.'&nbsp;'.$text.'</h3>';
     }
     $result.= &Apache::loncommon::start_data_table().
 	      &Apache::loncommon::start_data_table_header_row().
@@ -3723,10 +3811,10 @@ sub viewgrades {
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     my @partids = ();
     foreach my $part (@parts) {
-	my $display=&Apache::lonnet::metadata($url,$part.'.display');
+	my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
         my $narrowtext = &mt('Tries');
 	$display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower
-	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
+	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name',$toolsymb); }
 	my ($partid) = &split_part_type($part);
         push(@partids,$partid);
 #
@@ -3767,7 +3855,7 @@ sub viewgrades {
 		 return $a cmp $b;
 	     } (keys(%$fullname))) {
 	$result.=&viewstudentgrade($symb,$env{'request.course.id'},
-				   $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets);
+				   $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets,$is_tool);
     }
     $result.=&Apache::loncommon::end_data_table();
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
@@ -3855,7 +3943,7 @@ sub viewgrades {
 
 #--- call by previous routine to display each student who satisfies submission filter. 
 sub viewstudentgrade {
-    my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;
+    my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets,$is_tool) = @_;
     my ($uname,$udom) = split(/:/,$student);
     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
     my $submitonly = $env{'form.submitonly'};
@@ -3961,6 +4049,10 @@ sub viewstudentgrade {
 #    record does not get update if unchanged
 sub editgrades {
     my ($request,$symb) = @_;
+    my $toolsymb;
+    if ($symb =~ /ext\.tool$/) {
+        $toolsymb = $symb;
+    }
 
     my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     my $title='<h2>'.&mt('Current Grade Status').'</h2>';
@@ -3998,6 +4090,7 @@ sub editgrades {
 	$ctr++;
     }
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
+    my $totcolspan = 0;
     foreach my $partid (@partid) {
 	$header .= '<th align="center">'.&mt('Old Score').'</th>'.
 	    '<th align="center">'.&mt('New Score').'</th>';
@@ -4006,7 +4099,7 @@ sub editgrades {
 	    my ($part,$type) = &split_part_type($stores);
 	    if ($part !~ m/^\Q$partid\E/) { next;}
 	    if ($type eq 'awarded' || $type eq 'solved') { next; }
-	    my $display=&Apache::lonnet::metadata($url,$stores.'.display');
+	    my $display=&Apache::lonnet::metadata($url,$stores.'.display',$toolsymb);
 	    $display =~ s/\[Part: \Q$part\E\]//;
             my $narrowtext = &mt('Tries');
 	    $display =~ s/Number of Attempts/$narrowtext/;
@@ -4014,6 +4107,7 @@ sub editgrades {
 		'<th align="center">'.&mt('New').' '.$display.'</th>';
 	    $columns{$partid}+=2;
 	}
+        $totcolspan += $columns{$partid};
     }
     foreach my $partid (@partid) {
 	my $display_part=&get_display_part($partid,$symb);
@@ -4029,18 +4123,18 @@ sub editgrades {
     my @noupdate;
     my ($updateCtr,$noupdateCtr) = (1,1);
     for ($i=0; $i<$env{'form.total'}; $i++) {
-	my $line;
 	my $user = $env{'form.ctr'.$i};
 	my ($uname,$udom)=split(/:/,$user);
 	my %newrecord;
 	my $updateflag = 0;
-	$line .= '<td>'.&nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
 	my $usec=$classlist->{"$uname:$udom"}[5];
-	if (!&canmodify($usec)) {
-	    my $numcols=scalar(@partid)*4+2;
+	my $canmodify = &canmodify($usec);
+	my $line = '<td'.($canmodify?'':' colspan="2"').'>'.
+		   &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
+	if (!$canmodify) {
 	    push(@noupdate,
-		 $line."<td colspan=\"$numcols\"><span class=\"LC_warning\">".
-		 &mt('Not allowed to modify student')."</span></td></tr>");
+		 $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".
+		 &mt('Not allowed to modify student')."</span></td>");
 	    next;
 	}
         my %aggregate = ();
@@ -4157,8 +4251,7 @@ sub editgrades {
         }
     }
     if (@noupdate) {
-#	my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
-	my $numcols=scalar(@partid)*4+2;
+        my $numcols=$totcolspan+2;
 	$result .= &Apache::loncommon::start_data_table_row('LC_empty_row').
 	    '<td align="center" colspan="'.$numcols.'">'.
 	    &mt('No Changes Occurred For the Students Below').
@@ -4199,7 +4292,7 @@ sub split_part_type {
 #
 #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {
-    my $error1=&mt('You need to specify the username or the student/employee ID');
+    my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);
   &js_escape(\$error2);
@@ -4208,13 +4301,15 @@ sub csvupload_javascript_reverse_associa
     var foundsomething=0;
     var founduname=0;
     var foundID=0;
+    var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');
       if (i==0 && tw!=0) { foundID=1; }
       if (i==1 && tw!=0) { founduname=1; }
-      if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }
+      if (i==2 && tw!=0) { foundclicker=1; }
+      if (i!=0 && i!=1 && i!=2 && i!=3 && tw!=0) { foundsomething=1; }
     }
-    if (founduname==0 && foundID==0) {
+    if (founduname==0 && foundID==0 && foundclicker==0) {
 	alert('$error1');
 	return;
     }
@@ -4241,7 +4336,7 @@ ENDPICK
 }
 
 sub csvupload_javascript_forward_associate {
-    my $error1=&mt('You need to specify the username or the student/employee ID');
+    my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);
   &js_escape(\$error2);
@@ -4250,13 +4345,15 @@ sub csvupload_javascript_forward_associa
     var foundsomething=0;
     var founduname=0;
     var foundID=0;
+    var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');
       if (tw==1) { foundID=1; }
       if (tw==2) { founduname=1; }
-      if (tw>3) { foundsomething=1; }
+      if (tw==3) { foundclicker=1; }
+      if (tw>4) { foundsomething=1; }
     }
-    if (founduname==0 && foundID==0) {
+    if (founduname==0 && foundID==0 && Ć’oundclicker==0) {
 	alert('$error1');
 	return;
     }
@@ -4314,6 +4411,10 @@ ENDPICK
 
 sub csvupload_fields {
     my ($symb,$errorref) = @_;
+    my $toolsymb;
+    if ($symb =~ /ext\.tool$/) {
+        $toolsymb = $symb;
+    }
     my (@parts) = &getpartlist($symb,$errorref);
     if (ref($errorref)) {
         if ($$errorref) {
@@ -4322,15 +4423,15 @@ sub csvupload_fields {
     }
 
     my @fields=(['ID','Student/Employee ID'],
-                ['clicker','Clicker ID'],
 		['username','Student Username'],
+		['clicker','Clicker ID'],
 		['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $part (sort(@parts)) {
 	my @datum;
-	my $display=&Apache::lonnet::metadata($url,$part.'.display');
+	my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
 	my $name=$part;
-	if  (!$display) { $display = $name; }
+	if (!$display) { $display = $name; }
 	@datum=($name,$display);
 	if ($name=~/^stores_(.*)_awarded/) {
 	    push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
@@ -4405,8 +4506,10 @@ sub csvuploadmap {
     if (!$env{'form.datatoken'}) {
 	$datatoken=&Apache::loncommon::upfile_store($request);
     } else {
-	$datatoken=$env{'form.datatoken'};
-	&Apache::loncommon::load_tmp_file($request);
+	$datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
+        if ($datatoken ne '') {
+	    &Apache::loncommon::load_tmp_file($request,$datatoken);
+        }
     }
     my @records=&Apache::loncommon::upfile_record_sep();
     &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
@@ -4495,7 +4598,10 @@ sub csvuploadassign {
     my ($request,$symb)= @_;
     if (!$symb) {return '';}
     my $error_msg = '';
-    &Apache::loncommon::load_tmp_file($request);
+    my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
+    if ($datatoken ne '') { 
+        &Apache::loncommon::load_tmp_file($request,$datatoken);
+    }
     my @gradedata = &Apache::loncommon::upfile_record_sep();
     my %fields=&get_fields();
     my $courseid=$env{'request.course.id'};
@@ -4815,7 +4921,7 @@ sub getSymbMap {
     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
 					       1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {
-	if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
+	if ($navmap->hasResource($sequence, sub { shift->is_gradable(); }, 0) ) {
 	    my $title = $minder.'.'.
 		&HTML::Entities::encode($sequence->compTitle(),'"\'&');
 	    push(@titles, $title); # minder in case two titles are identical
@@ -4912,10 +5018,11 @@ sub displayPage {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }
 
-        if (ref($curRes) && $curRes->is_problem()) {
+        if (ref($curRes) && $curRes->is_gradable()) {
 	    my $parts = $curRes->parts();
             my $title = $curRes->compTitle();
 	    my $symbx = $curRes->symb();
+            my $is_tool = ($symbx =~ /ext\.tool$/);
 	    $studentTable.=
 		&Apache::loncommon::start_data_table_row().
 		'<td align="center" valign="top" >'.$prob.
@@ -4926,26 +5033,34 @@ sub displayPage {
 		 '</td>';
 	    $studentTable.='<td valign="top">';
 	    my %form = ('CODE' => $env{'form.CODE'},);
-	    if ($env{'form.vProb'} eq 'yes' ) {
-		$studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
-					     undef,'both',\%form);
-	    } else {
-		my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
-		$companswer =~ s|<form(.*?)>||g;
-		$companswer =~ s|</form>||g;
-#		while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
-#		    $companswer =~ s/$1/ /ms;
-#		    $request->print('match='.$1."<br />\n");
-#		}
-#		$companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
-		$studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;
+            if ($is_tool) {
+                $studentTable.='&nbsp;<b>'.$title.'</b><br />';
+            } else {
+	        if ($env{'form.vProb'} eq 'yes' ) {
+		    $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
+					         undef,'both',\%form);
+	        } else {
+		    my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
+		    $companswer =~ s|<form(.*?)>||g;
+		    $companswer =~ s|</form>||g;
+#		    while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
+#		        $companswer =~ s/$1/ /ms;
+#		        $request->print('match='.$1."<br />\n");
+#		    }
+#		    $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
+		    $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;
+		}
 	    }
 
 	    my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
 
 	    if ($env{'form.lastSub'} eq 'datesub') {
 		if ($record{'version'} eq '') {
-		    $studentTable.='<br />&nbsp;<span class="LC_warning">'.&mt('No recorded submission for this problem.').'</span><br />';
+                    my $msg = &mt('No recorded submission for this problem.');
+                    if ($is_tool) {
+                        $msg = &mt('No recorded transactions for this external tool');
+                    }
+		    $studentTable.='<br />&nbsp;<span class="LC_warning">'.$msg.'</span><br />';
 		} else {
 		    my %responseType = ();
 		    foreach my $partid (@{$parts}) {
@@ -4958,7 +5073,6 @@ sub displayPage {
 			$responseType{$partid} = \%responseIds;
 		    }
 		    $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
-
 		}
 	    } elsif ($env{'form.lastSub'} eq 'all') {
 		my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
@@ -4999,13 +5113,14 @@ sub displaySubByDates {
     my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
     my $isCODE=0;
     my $isTask = ($symb =~/\.task$/);
+    my $is_tool = ($symb =~/\.tool$/);
     if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     my $studentTable=&Apache::loncommon::start_data_table().
 	&Apache::loncommon::start_data_table_header_row().
 	'<th>'.&mt('Date/Time').'</th>'.
 	($isCODE?'<th>'.&mt('CODE').'</th>':'').
         ($isTask?'<th>'.&mt('Version').'</th>':'').
-	'<th>'.&mt('Submission').'</th>'.
+	'<th>'.($is_tool?&mt('Grade'):&mt('Submission')).'</th>'.
 	'<th>'.&mt('Status').'</th>'.
 	&Apache::loncommon::end_data_table_header_row();
     my ($version);
@@ -5013,7 +5128,11 @@ sub displaySubByDates {
     my %orders;
     $mark{'correct_by_student'} = $checkIcon;
     if (!exists($$record{'1:timestamp'})) {
-	return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
+        if ($is_tool) {
+            return '<br />&nbsp;<span class="LC_warning">'.&mt('No grade passed back.').'</span><br />';
+        } else {
+            return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
+        }
     }
 
     my $interaction;
@@ -5046,56 +5165,64 @@ sub displaySubByDates {
             if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                 $hidden = 1;
             }
-	    my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)
-			            : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
-	    
+            my @matchKey;
+            if ($isTask) {
+                @matchKey = sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys);
+            } elsif ($is_tool) {
+                @matchKey = sort(grep /^resource\.\Q$partid\E\.awarded$/,@versionKeys);
+            } else {
+                @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
+            }
 #	    next if ($$record{"$version:resource.$partid.solved"} eq '');
 	    my $display_part=&get_display_part($partid,$symb);
 	    foreach my $matchKey (@matchKey) {
 		if (exists($$record{$version.':'.$matchKey}) &&
 		    $$record{$version.':'.$matchKey} ne '') {
-                    
-		    my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
-				               : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
-                    $displaySub[0].='<span class="LC_nobreak">';
-                    $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
-                                   .' <span class="LC_internal_info">'
-                                   .'('.&mt('Response ID: [_1]',$responseId).')'
-                                   .'</span>'
-                                   .' <b>';
-                    if ($hidden) {
-                        $displaySub[0].= &mt('Anonymous Survey').'</b>';
+                    if ($is_tool) {
+                        $displaySub[0].=$$record{"$version:resource.$partid.awarded"};
                     } else {
-                        my ($trial,$rndseed,$newvariation);
-                        if ($type eq 'randomizetry') {
-                            $trial = $$record{"$where.$partid.tries"};
-                            $rndseed = $$record{"$where.$partid.rndseed"};
-                        }
-		        if ($$record{"$where.$partid.tries"} eq '') {
-			    $displaySub[0].=&mt('Trial not counted');
-		        } else {
-			    $displaySub[0].=&mt('Trial: [_1]',
-					    $$record{"$where.$partid.tries"});
-                            if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
-                                if (($rndseed ne $lastrndseed{$partid}) &&
-                                    (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
-                                    $newvariation = '&nbsp;('.&mt('New variation this try').')';
-                                }
+		        my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
+				                   : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
+                        $displaySub[0].='<span class="LC_nobreak">';
+                        $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
+                                       .' <span class="LC_internal_info">'
+                                       .'('.&mt('Response ID: [_1]',$responseId).')'
+                                       .'</span>'
+                                       .' <b>';
+                        if ($hidden) {
+                            $displaySub[0].= &mt('Anonymous Survey').'</b>';
+                        } else {
+                            my ($trial,$rndseed,$newvariation);
+                            if ($type eq 'randomizetry') {
+                                $trial = $$record{"$where.$partid.tries"};
+                                $rndseed = $$record{"$where.$partid.rndseed"};
                             }
-                            $lastrndseed{$partid} = $rndseed;
-                            $lasttype{$partid} = $type;
-		        }
-		        my $responseType=($isTask ? 'Task'
+		            if ($$record{"$where.$partid.tries"} eq '') {
+			        $displaySub[0].=&mt('Trial not counted');
+		            } else {
+			        $displaySub[0].=&mt('Trial: [_1]',
+					        $$record{"$where.$partid.tries"});
+                                if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
+                                    if (($rndseed ne $lastrndseed{$partid}) &&
+                                        (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
+                                        $newvariation = '&nbsp;('.&mt('New variation this try').')';
+                                    }
+                                }
+                                $lastrndseed{$partid} = $rndseed;
+                                $lasttype{$partid} = $type;
+		            }
+		            my $responseType=($isTask ? 'Task'
                                               : $responseType->{$partid}->{$responseId});
-		        if (!exists($orders{$partid})) { $orders{$partid}={}; }
-		        if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
-			    $orders{$partid}->{$responseId}=
-			        &get_order($partid,$responseId,$symb,$uname,$udom,
-                                           $no_increment,$type,$trial,$rndseed);
-		        }
-		        $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
-		        $displaySub[0].='&nbsp; '.
-			    &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
+		            if (!exists($orders{$partid})) { $orders{$partid}={}; }
+		            if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
+			        $orders{$partid}->{$responseId}=
+			            &get_order($partid,$responseId,$symb,$uname,$udom,
+                                               $no_increment,$type,$trial,$rndseed);
+		            }
+		            $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
+		            $displaySub[0].='&nbsp; '.
+			        &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
+                        }
                     }
 		}
 	    }
@@ -5110,14 +5237,22 @@ sub displaySubByDates {
 		    lc($$record{"$where.$partid.award"}).' '.
 		    $mark{$$record{"$where.$partid.solved"}}.
 		    '<br />';
+	    } elsif (($is_tool) && (exists($$record{"$version:resource.$partid.solved"}))) {
+		if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {
+		    $displaySub[1].=&mt('Grade passed back by external tool');
+		}
 	    }
 	    if (exists $$record{"$where.$partid.regrader"}) {
-		$displaySub[2].=$$record{"$where.$partid.regrader"}.
-		    ' (<b>'.&mt('Part').':</b> '.$display_part.')';
+		$displaySub[2].=$$record{"$where.$partid.regrader"};
+		unless ($is_tool) {
+		    $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
+		}
 	    } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {
 		$displaySub[2].=
-		    $$record{"$version:resource.$partid.regrader"}.
-		    ' (<b>'.&mt('Part').':</b> '.$display_part.')';
+		    $$record{"$version:resource.$partid.regrader"};
+                unless ($is_tool) {
+		    $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
+                }
 	    }
 	}
 	# needed because old essay regrader has not parts info
@@ -9649,12 +9784,13 @@ sub submit_options_table {
     my ($request,$symb) = @_;
     if (!$symb) {return '';}
     &commonJSfunctions($request);
+    my $is_tool = ($symb =~ /ext\.tool$/);
     my $result;
 
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
         '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
 
-    $result.=&selectfield(1).
+    $result.=&selectfield(1,$is_tool).
             '<input type="hidden" name="command" value="viewgrades" />
             <div>
               <input type="submit" value="'.&mt('Next').' &rarr;" />
@@ -9668,6 +9804,7 @@ sub submit_options_download {
     my ($request,$symb) = @_;
     if (!$symb) {return '';}
 
+    my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);
 
     my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
@@ -9675,7 +9812,7 @@ sub submit_options_download {
     $result.='
 <h2>
   '.&mt('Select Students for Which to Download Submissions').'
-</h2>'.&selectfield(1).'
+</h2>'.&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="downloadfileslink" /> 
               <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>
@@ -9691,12 +9828,13 @@ sub submit_options {
     my ($request,$symb) = @_;
     if (!$symb) {return '';}
 
+    my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);
     my $result;
 
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
 	'<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
-    $result.=&selectfield(1).'
+    $result.=&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="submission" /> 
 	      <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>
@@ -9708,10 +9846,17 @@ sub submit_options {
 }
 
 sub selectfield {
-   my ($full)=@_;
-   my %options = 
-          (&substatus_options,
-           'select_form_order' => ['yes','queued','graded','incorrect','all']);
+   my ($full,$is_tool)=@_;
+   my %options;
+   if ($is_tool) {
+       %options =
+           (&transtatus_options,
+            'select_form_order' => ['yes','incorrect','all']);
+   } else {
+       %options = 
+           (&substatus_options,
+            'select_form_order' => ['yes','queued','graded','incorrect','all']);
+   }
    my $result='<div class="LC_columnSection">
   
     <fieldset>
@@ -9735,10 +9880,14 @@ sub selectfield {
       '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'
     </fieldset>';
     if ($full) {
-       $result.='
+        my $heading = &mt('Submission Status');
+        if ($is_tool) {
+            $heading = &mt('Transaction Status');
+        }
+        $result.='
     <fieldset>
       <legend>
-        '.&mt('Submission Status').'
+        '.$heading.'
       </legend>'.
        &Apache::loncommon::select_form('all','submitonly',\%options).
    '</fieldset>';
@@ -9757,6 +9906,14 @@ sub substatus_options {
                                       );
 }
 
+sub transtatus_options {
+    return &Apache::lonlocal::texthash(
+                                       'yes'       => 'with score transactions',
+                                       'incorrect' => 'with less than full credit',
+                                       'all'       => 'with any status',
+                                      );
+}
+
 sub reset_perm {
     undef(%perm);
 }
@@ -10387,7 +10544,7 @@ sub startpage {
 sub select_problem {
     my ($r)=@_;
     $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');
-    $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1));
+    $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,undef,undef,1));
     $r->print('<input type="hidden" name="command" value="gradingmenu" />');
     $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');
 }