--- loncom/homework/grades.pm	2009/05/01 01:07:49	1.565
+++ loncom/homework/grades.pm	2010/01/06 18:43:57	1.581.2.1
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.565 2009/05/01 01:07:49 raeburn Exp $
+# $Id: grades.pm,v 1.581.2.1 2010/01/06 18:43:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -183,7 +183,8 @@ sub get_display_part {
     my ($partID,$symb)=@_;
     my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
     if (defined($display) and $display ne '') {
-	$display.= " (<span class=\"LC_internal_info\">id $partID</span>)";
+        $display.= ' (<span class="LC_internal_info">'
+                  .&mt('Part ID: [_1]',$partID).'</span>)';
     } else {
 	$display=$partID;
     }
@@ -217,9 +218,9 @@ sub showResourceInfo {
 		$partsseen{$partID}=1;
 	    }
 	    my $display_part=&get_display_part($partID,$symb);
-	    $result.='<td><b>'.&mt('Part').': </b>'.$display_part.
+            $result.='<td><b>'.&mt('Part: [_1]',$display_part).'</b>'.
                 ' <span class="LC_internal_info">'.$resID.'</span></td>'.
-		'<td><b>'.&mt('Type').': </b>'.$responsetype.'</td></tr>';
+                '<td><b>'.&mt('Type: [_1]',$responsetype).'</b></td></tr>';
 #	    '<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td></tr>';
 	}
     }
@@ -1734,8 +1735,7 @@ sub gradeBox {
 
 	#&mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line);
     $result .= 
-	    '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
-    
+	    '<td><b>'.&mt('Part:').'</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points:').'</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';
     $result.='</tr></table>'."\n";
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
 	'<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
@@ -2113,9 +2113,11 @@ KEYWORDS
 		}
 		my $responsetype = $responseType->{$partid}->{$respid};
 		if (!exists($record{"resource.$partid.$respid.submission"})) {
-		    $lastsubonly.="\n".'<div class="LC_grade_submission_part"><b>Part:</b> '.
-			$display_part.' <span class="LC_internal_info">( ID '.$respid.
-			' )</span>&nbsp; &nbsp;'.
+                    $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
+                        '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
+                        ' <span class="LC_internal_info">'.
+                        '('.&mt('Part ID: [_1]',$respid).')</b>'.
+                        '</span>&nbsp; &nbsp;'.
 			'<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
 		    next;
 		}
@@ -2135,10 +2137,9 @@ KEYWORDS
 								   {'one_time' => 1});
 
 			    $similar="<hr /><h3><span class=\"LC_warning\">".
-				&mt('Essay is [_1]% similar to an essay by [_2] ([_3]:[_4]) in course [_5] (course id [_6]:[_7])',
+				&mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
 				    $osim,
-				    &Apache::loncommon::plainname($oname,$odom),
-				    $oname,$odom,
+				    &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
 				    $old_course_desc{'description'},
 				    $old_course_desc{'num'},
 				    $old_course_desc{'domain'}).
@@ -2152,9 +2153,11 @@ KEYWORDS
 			($env{'form.lastSub'} eq 'hdgrade' && 
 			 $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {
 			my $display_part=&get_display_part($partid,$symb);
-			$lastsubonly.='<div class="LC_grade_submission_part"><b>Part:</b> '.
-			    $display_part.' <span class="LC_internal_info">( ID '.$respid.
-			    ' )</span>&nbsp; &nbsp;';
+                        $lastsubonly.='<div class="LC_grade_submission_part">'.
+                            '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
+                            ' <span class="LC_internal_info">'.
+                            '('.&mt('Part ID: [_1]',$respid).')'.
+                            '</b></span>&nbsp; &nbsp;';
 			my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
 			if (@$files) {
 			    $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />';
@@ -2297,7 +2300,7 @@ KEYWORDS
 	    '<option>7</option><option>10</option></select>'."\n";
 	my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
 	$ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
-	$endform.=&mt('[quant,_1,student]',$ntstu);
+        $endform.=&mt('[_1]student(s)',$ntstu);
 	$endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.
 	    'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
 	    '<input type="button" value="'.&mt('Next').'" '.
@@ -2463,7 +2466,7 @@ sub processHandGrade {
                                                      undef,$feedurl,undef,
                                                      undef,undef,$showsymb,
                                                      $restitle);
-		$request->print('<br />'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '.
+		$request->print('<br />'.&mt('Sending message to [_1]',$uname.':'.$udom).': '.
 				$msgstatus);
 	    }
 	    if ($env{'form.collaborator'.$ctr}) {
@@ -3295,7 +3298,7 @@ sub viewgrades {
 
 	$result.=
 	    &Apache::loncommon::start_data_table_row()."\n".
-	    '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
+	    '<td><b>'.&mt('Part:').'</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points:').'</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
 	    &Apache::loncommon::end_data_table_row()."\n";
 	$ctsparts++;
     }
@@ -3681,7 +3684,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 ID');
+    my $error1=&mt('You need to specify the username or the student/employee ID');
     my $error2=&mt('You need to specify at least one grading field');
   return(<<ENDPICK);
   function verify(vf) {
@@ -3721,7 +3724,7 @@ ENDPICK
 }
 
 sub csvupload_javascript_forward_associate {
-    my $error1=&mt('You need to specify the username or ID');
+    my $error1=&mt('You need to specify the username or the student/employee ID');
     my $error2=&mt('You need to specify at least one grading field');
   return(<<ENDPICK);
   function verify(vf) {
@@ -4090,14 +4093,14 @@ sub csvuploadassign {
 	   $countdone++;
         }
     }
-    $request->print('<br /><span class="LC_info">'.&mt("Saved [_1] students",$countdone)."</span>\n");
+    $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
     if (@skipped) {
-	$request->print('<p><span class="LC_warning">'.&mt('Skipped Students').'</span></p>');
-	foreach my $student (@skipped) { $request->print("$student<br />\n"); }
+	$request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />');
+        $request->print(join(', ',@skipped));
     }
     if (@notallowed) {
-	$request->print('<p><span class="LC_error">'.&mt('Students Not Allowed to Modify').'</span></p>');
-	foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
+	$request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Modification of scores not allowed for the following username(s):'),1).'<br />');
+	$request->print(join(', ',@notallowed));
     }
     $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb));
@@ -4467,13 +4470,16 @@ sub displaySubByDates {
 
 		    my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
 				               : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
-		    $displaySub[0].='<b>'.&mt('Part:').'</b>&nbsp;'.$display_part.'&nbsp;';
-		    $displaySub[0].='<span class="LC_internal_info">('.&mt('ID').'&nbsp;'.
-			$responseId.')</span>&nbsp;<b>';
+                    $displaySub[0].='<span class="LC_nobreak"';
+                    $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
+                                   .' <span class="LC_internal_info">'
+                                   .'('.&mt('Part ID: [_1]',$responseId).')'
+                                   .'</span>'
+                                   .' <b>';
 		    if ($$record{"$where.$partid.tries"} eq '') {
-			$displaySub[0].=&mt('Trial&nbsp;not&nbsp;counted');
+			$displaySub[0].=&mt('Trial not counted');
 		    } else {
-			$displaySub[0].=&mt('Trial&nbsp;[_1]',
+			$displaySub[0].=&mt('Trial: [_1]',
 					    $$record{"$where.$partid.tries"});
 		    }
 		    my $responseType=($isTask ? 'Task'
@@ -4484,7 +4490,8 @@ sub displaySubByDates {
 			    &get_order($partid,$responseId,$symb,$uname,$udom,
                                        $no_increment);
 		    }
-		    $displaySub[0].='</b>&nbsp; '.
+		    $displaySub[0].='</b></span>'; # /nobreak
+		    $displaySub[0].='&nbsp; '.
 			&cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />';
 		}
 	    }
@@ -4690,7 +4697,7 @@ sub updateGradeByPage {
 #
 #-------------------------------------------------------------------
 
-#--------------------Scantron Grading-----------------------------------
+#-------------------- Bubblesheet (Scantron) Grading -------------------
 #
 #------ start of section for handling grading by page/sequence ---------
 
@@ -5069,10 +5076,10 @@ sub scantron_selectphase {
             <td> '.&mt('Sequence to grade:').' </td><td> '.$sequence_selector.' </td>
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
-            <td> '.&mt('Filename of scoring office file:').' </td><td> '.$file_selector.' </td>
+            <td> '.&mt('Filename of bubblesheet data file:').' </td><td> '.$file_selector.' </td>
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
-            <td> '.&mt('Format of data file:').' </td><td> '.$format_selector.' </td>
+            <td> '.&mt('Format of bubblesheet data file:').' </td><td> '.$format_selector.' </td>
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
             <td> '.&mt('Saved CODEs to validate against:').' </td><td> '.$CODE_selector.' </td>
@@ -5090,7 +5097,7 @@ sub scantron_selectphase {
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
             <td colspan="2">
-              <input type="submit" value="'.&mt('Grading: Validate Scantron Records').'" />
+              <input type="submit" value="'.&mt('Grading: Validate Bubblesheet Records').'" />
             </td>
        '.&Apache::loncommon::end_data_table_row().'
     '.&Apache::loncommon::end_data_table().'
@@ -5109,7 +5116,7 @@ sub scantron_selectphase {
     '.&Apache::loncommon::start_data_table('LC_scantron_action').'
        '.&Apache::loncommon::start_data_table_header_row().'
             <th>
-              &nbsp;'.&mt('Specify a Scantron data file to upload.').'
+              &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
             </th>
        '.&Apache::loncommon::end_data_table_header_row().'
        '.&Apache::loncommon::start_data_table_row().'
@@ -5136,7 +5143,7 @@ sub scantron_selectphase {
                 <input name="command" value="scantronupload_save" type="hidden" />
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
                 <br />
-                <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
+                <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
               </form>
 ');
 
@@ -5178,7 +5185,7 @@ sub scantron_selectphase {
              &Apache::loncommon::start_data_table('LC_scantron_action')."\n".
              &Apache::loncommon::start_data_table_header_row()."\n".
              '<th colspan="2">
-              &nbsp;'.&mt('Review scantron data and submissions for a previously graded folder/sequence')."\n".
+              &nbsp;'.&mt('Review bubblesheet data and submissions for a previously graded folder/sequence')."\n".
              '</th>'."\n".
               &Apache::loncommon::end_data_table_header_row()."\n".
               &Apache::loncommon::start_data_table_row()."\n".
@@ -5200,7 +5207,7 @@ sub scantron_selectphase {
               &Apache::loncommon::start_data_table_row()."\n".
               '<td colspan="2">'."\n".
               '<input type="hidden" name="command" value="checksubmissions" />'."\n".
-              '<input type="submit" value="'.&mt('Review Scantron Data and Submission Records').'" />'."\n".
+              '<input type="submit" value="'.&mt('Review Bubblesheet Data and Submission Records').'" />'."\n".
               '</td>'."\n".
               &Apache::loncommon::end_data_table_row()."\n".
               &Apache::loncommon::end_data_table()."\n".
@@ -5241,7 +5248,7 @@ sub scantron_selectphase {
       CODEstart   - (only matter if a CODE exists) column in the line where
                      the CODE starts
       CODElength  - length of the CODE
-      IDstart     - column where the student/employee ID number starts
+      IDstart     - column where the student/employee ID starts
       IDlength    - length of the student/employee ID info
       Qstart      - column where the information from the bubbled
                     'questions' start
@@ -5341,7 +5348,7 @@ sub username_to_idmap {
     $whichline         - line number of the passed in scanline
     $field             - type of change to process 
                          (either 
-                          'ID'     -> correct the student/employee ID number
+                          'ID'     -> correct the student/employee ID
                           'CODE'   -> correct the CODE
                           'answer' -> fixup the submitted answers)
     
@@ -6251,7 +6258,7 @@ sub scantron_validate_file {
                   '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').
                   '</label></span><br />'.
                   &mt('Grading will take longer if you use verification.').'<br />'.
-                  &mt("Alternatively, the 'Review scantron data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.
+                  &mt("Alternatively, the 'Review bubblesheet data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.
                   '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.
                   '<input type="hidden" name="command" value="scantron_process" />'."\n");
     } else {
@@ -6777,10 +6784,10 @@ sub scantron_get_correction {
 	    if ($closest > 0) {
 		foreach my $testcode (@{$closest}) {
 		    my $checked='';
-		    if (!$i) { $checked=' checked="checked" '; }
+		    if (!$i) { $checked=' checked="checked"'; }
 		    $r->print("
    <label>
-       <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked />
+       <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i'$checked />
        ".&mt("Use the similar CODE [_1] instead.",
 	    "<b><tt>".$testcode."</tt></b>")."
     </label>
@@ -6791,10 +6798,10 @@ sub scantron_get_correction {
 	    }
 	}
 	if ($$scan_record{'scantron.CODE'}=~/\S/ ) {
-	    my $checked; if (!$i) { $checked=' checked="checked" '; }
+	    my $checked; if (!$i) { $checked=' checked="checked"'; }
 	    $r->print("
     <label>
-        <input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked />
+        <input type='radio' name='scantron_CODE_resolution' value='use_unfound'$checked />
        ".&mt("Use the CODE [_1] that is was on the paper, ignoring the error.",
 	     "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."
     </label>");
@@ -7018,7 +7025,7 @@ sub prompt_for_corrections {
             ($responsetype_per_response{$question-1} eq 'imageresponse') ||
             ($responsetype_per_response{$question-1} eq 'reactionresponse') ||
             ($responsetype_per_response{$question-1} eq 'organicresponse')) {
-            $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their scantron sheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during scantron grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');
+            $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during bubblesheet grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');
         } else {
             $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");
         }
@@ -7475,8 +7482,8 @@ SCANTRONFORM
     
     my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
     my $count=&get_todo_count($scanlines,$scan_data);
-    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
- 				    'Scantron Progress',$count,
+    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet Status',
+ 				    'Bubblesheet Progress',$count,
 				    'inline',undef,'scantronupload');
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
 					  'Processing first student');
@@ -7710,15 +7717,17 @@ sub scantron_upload_scantron_data {
     my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.
                        ('&nbsp'x2).&mt('(shows course personnel)'); 
     my $default_form_data=&defaultFormData(&get_symb($r,1));
+    my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
+    my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded.");
     $r->print('
 <script type="text/javascript" language="javascript">
     function checkUpload(formname) {
 	if (formname.upfile.value == "") {
-	    alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
+	    alert("'.$nofile_alert.'");
 	    return false;
 	}
         if (formname.courseid.value == "") {
-            alert("'.&mt('Please use the \"Select Course\" link to open a separate window where you can search for a course to which a file can be uploaded.').'");
+            alert("'.$nocourseid_alert.'");
             return false;
         }
 	formname.submit();
@@ -7740,6 +7749,8 @@ sub scantron_upload_scantron_data {
 
 </script>
 
+<h3>'.&mt('Send scanned bubblesheet data to a course').'</h3>
+
 <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
 '.$default_form_data.
   &Apache::lonhtmlcommon::start_pick_box().
@@ -7758,7 +7769,7 @@ sub scantron_upload_scantron_data {
   &Apache::lonhtmlcommon::end_pick_box().'<br />
 
 <input name="command" value="scantronupload_save" type="hidden" />
-<input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
+<input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
 </form>
 ');
     return '';
@@ -7776,7 +7787,7 @@ sub scantron_upload_scantron_data_save {
     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
 	!&Apache::lonnet::allowed('usc',
 			    $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
-	$r->print(&mt("You are not allowed to upload Scantron data to the requested course.")."<br />");
+	$r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");
 	if ($symb) {
 	    $r->print(&show_grading_menu_form($symb));
 	} else {
@@ -7785,36 +7796,25 @@ sub scantron_upload_scantron_data_save {
 	return '';
     }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
-    $r->print(&mt("Doing upload to [_1]",$coursedata{'description'})." <br />");
-    my $fname=$env{'form.upfile.filename'};
-    #FIXME
-    #copied from lonnet::userfileupload()
-    #make that function able to target a specified course
-    # Replace Windows backslashes by forward slashes
-    $fname=~s/\\/\//g;
-    # Get rid of everything but the actual filename
-    $fname=~s/^.*\/([^\/]+)$/$1/;
-    # Replace spaces by underscores
-    $fname=~s/\s+/\_/g;
-    # Replace all other weird characters by nothing
-    $fname=~s/[^\w\.\-]//g;
-    # See if there is anything left
-    unless ($fname) { return 'error: no uploaded file'; }
-    my $uploadedfile=$fname;
-    $fname='scantron_orig_'.$fname;
+    my $uploadedfile;
+    $r->print('<h3>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</h3>');
     if (length($env{'form.upfile'}) < 2) {
-	$r->print(&mt("<span class=\"LC_error\">Error:</span> The file you attempted to upload, [_1]  contained no information. Please check that you entered the correct filename.",'<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));
+        $r->print(&mt('[_1]Error:[_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.','<span class="LC_error">','</span>','<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
     } else {
-	my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);
-	if ($result =~ m|^/uploaded/|) {
-	    $r->print(&mt("<span class=\"LC_success\">Success:</span> Successfully uploaded [_1] bytes of data into location [_2]",
-			  (length($env{'form.upfile'})-1),
-			  '<span class="LC_filename">'.$result."</span>"));
+        my $result = 
+            &Apache::lonnet::userfileupload('upfile','','scantron','','','',
+                                            $env{'form.courseid'},$env{'form.domainid'});
+	if ($result =~ m{^/uploaded/}) {
+	    $r->print(&mt('[_1]Success:[_2] Successfully uploaded [_3] bytes of data into location: [_4]',
+                          '<span class="LC_success">','</span>',(length($env{'form.upfile'})-1),
+			  '<span class="LC_filename">'.$result.'</span>'));
+            ($uploadedfile) = ($result =~ m{/([^/]+)$});
+            $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
+                                                       $env{'form.courseid'},$uploadedfile));
 	} else {
-	    $r->print(&mt("<span class=\"LC_error\">Error:</span> An error ([_1]) occurred when attempting to upload the file, [_2]",
-			  $result,
-			  '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));
-
+	    $r->print(&mt('[_1]Error:[_2] An error ([_3]) occurred when attempting to upload the file, [_4]',
+                          '<span class="LC_error">','</span>',$result,
+			  '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
 	}
     }
     if ($symb) {
@@ -7825,6 +7825,92 @@ sub scantron_upload_scantron_data_save {
     return '';
 }
 
+sub validate_uploaded_scantron_file {
+    my ($cdom,$cname,$fname) = @_;
+    my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
+    my @lines;
+    if ($scanlines ne '-1') {
+        @lines=split("\n",$scanlines,-1);
+    }
+    my $output;
+    if (@lines) {
+        my (%counts,$max_match_format);
+        my ($max_match_count,$max_match_pct) = (0,0);
+        my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cname);
+        my %idmap = &username_to_idmap($classlist);
+        foreach my $key (keys(%idmap)) {
+            my $lckey = lc($key);
+            $idmap{$lckey} = $idmap{$key};
+        }
+        my %unique_formats;
+        my @formatlines = &get_scantronformat_file();
+        foreach my $line (@formatlines) {
+            chomp($line);
+            my @config = split(/:/,$line);
+            my $idstart = $config[5];
+            my $idlength = $config[6];
+            if (($idstart ne '') && ($idlength > 0)) {
+                if (ref($unique_formats{$idstart.':'.$idlength}) eq 'ARRAY') {
+                    push(@{$unique_formats{$idstart.':'.$idlength}},$config[0].':'.$config[1]); 
+                } else {
+                    $unique_formats{$idstart.':'.$idlength} = [$config[0].':'.$config[1]];
+                }
+            }
+        }
+        foreach my $key (keys(%unique_formats)) {
+            my ($idstart,$idlength) = split(':',$key);
+            %{$counts{$key}} = (
+                               'found'   => 0,
+                               'total'   => 0,
+                              );
+            foreach my $line (@lines) {
+                next if ($line =~ /^#/);
+                next if ($line =~ /^[\s\cz]*$/);
+                my $id = substr($line,$idstart-1,$idlength);
+                $id = lc($id);
+                if (exists($idmap{$id})) {
+                    $counts{$key}{'found'} ++;
+                }
+                $counts{$key}{'total'} ++;
+            }
+            if ($counts{$key}{'total'}) {
+                my $percent_match = (100*$counts{$key}{'found'})/($counts{$key}{'total'});
+                if (($max_match_format eq '') || ($percent_match > $max_match_pct)) {
+                    $max_match_pct = $percent_match;
+                    $max_match_format = $key;
+                    $max_match_count = $counts{$key}{'total'};
+                }
+            }
+        }
+        if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {
+            my $format_descs;
+            my $numwithformat = @{$unique_formats{$max_match_format}};
+            for (my $i=0; $i<$numwithformat; $i++) {
+                my ($name,$desc) = split(':',$unique_formats{$max_match_format}[$i]);
+                if ($i<$numwithformat-2) {
+                    $format_descs .= '"<i>'.$desc.'</i>", ';
+                } elsif ($i==$numwithformat-2) {
+                    $format_descs .= '"<i>'.$desc.'</i>" '.&mt('and').' ';
+                } elsif ($i==$numwithformat-1) {
+                    $format_descs .= '"<i>'.$desc.'</i>"';
+                }
+            }
+            my $showpct = sprintf("%.0f",$max_match_pct).'%';
+            $output .= '<br />'.&mt('Comparison of student IDs in the uploaded file with the course roster found matches for [_1] of the [_2] entries in the file (for the format defined for [_3]).','<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs).
+                       '<br />'.&mt('A low percentage of matches results from one of the following:').'<ul>'.
+                       '<li>'.&mt('The file was uploaded to the wrong course').'</li>'.
+                       '<li>'.&mt('The data are not in the format expected for the domain: [_1]',
+                                  '<i>'.$cdom.'</i>').'</li>'.
+                       '<li>'.&mt('Students did not bubble their IDs, or mis-bubbled them').'</li>'.
+                       '<li>'.&mt('The course roster is not up to date').'</li>'.
+                       '</ul>';
+        }
+    } else {
+        $output = '<span class="LC_warning">'.&mt('Uploaded file contained no data').'</span>';
+    }
+    return $output;
+}
+
 sub valid_file {
     my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {
@@ -7904,8 +7990,8 @@ sub checkscantron_results {
     my %completedstudents;
 
     my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);
-    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status',
-                                    'Progress of Scantron Data/Submission Records Comparison',$count,
+    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet/Submissions Comparison Status',
+                                    'Progress of Bubblesheet Data/Submission Records Comparison',$count,
                                     'inline',undef,'checkscantron');
     my ($username,$domain,$started);
 
@@ -7982,14 +8068,14 @@ sub checkscantron_results {
                 if ($scandata{$pid} eq $record{$pid}) {
                     my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row';
                     $okstudents .= '<tr class="'.$css_class.'">'.
-'<td>'.&mt('Scantron').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
+'<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".
 '<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n";
                     $passed ++;
                 } else {
                     my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row';
-                    $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Scantron').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
+                    $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".
 '<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".
@@ -8000,10 +8086,10 @@ sub checkscantron_results {
             }
         }
     }
-    $r->print('<p>'.&mt('Comparison of scantron data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');
+    $r->print('<p>'.&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');
     $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');
     if ($passed) {
-        $r->print(&mt('Students with exact correspondence between scantron data and submissions are as follows:').'<br /><br />');
+        $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".
                  &Apache::loncommon::start_data_table_header_row()."\n".
                  '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
@@ -8012,14 +8098,14 @@ sub checkscantron_results {
                  &Apache::loncommon::end_data_table().'<br />');
     }
     if ($failed) {
-        $r->print(&mt('Students with differences between scantron data and submissions are as follows:').'<br /><br />');
+        $r->print(&mt('Students with differences between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".
                  &Apache::loncommon::start_data_table_header_row()."\n".
                  '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
                  &Apache::loncommon::end_data_table_header_row()."\n".
                  $badstudents."\n".
                  &Apache::loncommon::end_data_table()).'<br />'.
-                 &mt('Differences can occur if submissions were modified using manual grading after a scantron grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original scantron sheets.');  
+                 &mt('Differences can occur if submissions were modified using manual grading after a bubblesheet grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.');  
     }
     $r->print('</form><br />'.$grading_menu_button);
     return;
@@ -8234,7 +8320,7 @@ sub grading_menu {
                     		icon => 'addClickerInfoFile.png',
                     		linktitle => 'Specify a file containing the clicker information for this resource.'
                 	    },
-                	    {	linktext => 'Grade/Manage/Review Scantron Forms',
+                	    {	linktext => 'Grade/Manage/Review Bubblesheet Forms',
                     		url => $url4,
                     		permission => 'F',
                     		icon => 'stat.png',
@@ -8580,7 +8666,7 @@ sub process_clicker {
     my %checked;
     foreach my $gradingmechanism ('attendance','personnel','specific','given') {
        if ($env{'form.gradingmechanism'} eq $gradingmechanism) {
-          $checked{$gradingmechanism}="checked='checked'";
+          $checked{$gradingmechanism}=' checked="checked"';
        }
     }
 
@@ -8644,11 +8730,11 @@ function sanitycheck() {
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 <input type="file" name="upfile" size="50" />
 <br /><label>$type: $selectform</label>
-<br /><label><input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" />$attendance </label>
-<br /><label><input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" />$personnel</label>
-<br /><label><input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" />$specific </label>
+<br /><label><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onClick="sanitycheck()" />$attendance </label>
+<br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onClick="sanitycheck()" />$personnel</label>
+<br /><label><input type="radio" name="gradingmechanism" value="specific"$checked{'specific'} onClick="sanitycheck()" />$specific </label>
 <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />
-<br /><label><input type="radio" name="gradingmechanism" value="given" $checked{'given'} onClick="sanitycheck()" />$given </label>
+<br /><label><input type="radio" name="gradingmechanism" value="given"$checked{'given'} onClick="sanitycheck()" />$given </label>
 <br />&nbsp;&nbsp;&nbsp;
 <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
@@ -8967,13 +9053,13 @@ ENDHEADER
           my $sum=0;
           my $realnumber=$number;
           for (my $i=0;$i<$number;$i++) {
-             if ($answer[$i]) {
+             if  ($correct[$i] eq '-') {
+                $realnumber--;
+             } elsif ($answer[$i]) {
                 if ($gradingmechanism eq 'attendance') {
                    $sum+=$pcorrect;
-                } elsif ($answer[$i] eq '*') {
+                } elsif ($correct[$i] eq '*') {
                    $sum+=$pcorrect;
-                } elsif ($answer[$i] eq '-') {
-                   $realnumber--;
                 } else {
                    if ($answer[$i] eq $correct[$i]) {
                       $sum+=$pcorrect;
@@ -9030,6 +9116,11 @@ sub handler {
     my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];
     $request->print(&Apache::loncommon::start_page('Grading',undef,
                                           {'bread_crumbs' => $brcrum}));
+    if (&Apache::loncommon::needs_gci_custom()) {
+        $request->print('<h3>'.&mt('Grading screens are unavailable for GCI Concept Tests').'</h3>'.&Apache::loncommon::end_page());
+        &reset_caches();
+        return '';
+    }
     if ($symb eq '' && $command eq '') {
 	if ($env{'user.adv'}) {
 	    if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&