--- loncom/homework/grades.pm	2004/05/06 06:49:48	1.195
+++ loncom/homework/grades.pm	2004/07/27 15:16:10	1.204.2.1
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.195 2004/05/06 06:49:48 albertel Exp $
+# $Id: grades.pm,v 1.204.2.1 2004/07/27 15:16:10 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1084,7 +1084,7 @@ sub sub_page_kw_js {
     var ypos = (screen.height-height)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;
 
-    pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
+    pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
     pWin.focus();
     pDoc = pWin.document;
     pDoc.open('text/html','replace');
@@ -1215,7 +1215,7 @@ sub sub_page_kw_js {
     var ypos = (screen.height-330)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;
 
-    hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos);
+    hwdWin = window.open('', 'KeywordHighlightCentral', 'resizeable=yes,toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos);
     hwdWin.focus();
     var hDoc = hwdWin.document;
     hDoc.open('text/html','replace');
@@ -1669,7 +1669,9 @@ KEYWORDS
 			    $partid.'</b> <font color="#999999">( ID '.$respid.
 			    ' )</font>&nbsp; &nbsp;';
 			if ($record{"resource.$partid.$respid.uploadedurl"}) {
-			    $lastsubonly.='<a href="'.&Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).'" target="lonGRDs"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> <font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />';
+			    &Apache::lonnet::allowuploaded('/adm/grades',
+			      $record{"resource.$partid.$respid.uploadedurl"});
+			    $lastsubonly.='<a href="'.$record{"resource.$partid.$respid.uploadedurl"}.'" target="lonGRDs"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> <font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />';
 			}
 			$lastsubonly.='<b>Submitted Answer: </b>'.
 			    &cleanRecord($subval,$responsetype,$symb,$partid,
@@ -2049,11 +2051,11 @@ sub saveHandGrade {
 	    }
 	} elsif ($dropMenu eq 'reset status'
 		 && exists($record{'resource.'.$_.'.solved'})) { #don't bother if no old records -> no attempts
-	    $newrecord{'resource.'.$_.'.tries'} = 0;
-	    $newrecord{'resource.'.$_.'.solved'} = '';
-	    $newrecord{'resource.'.$_.'.award'} = '';
-	    $newrecord{'resource.'.$_.'.awarded'} = 0;
-	    $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
+	    foreach my $key (keys (%record)) {
+		if ($key=~/^resource\.\Q$_\E\./) { $newrecord{$key} = ''; }
+	    }
+	    $newrecord{'resource.'.$_.'.regrader'}=
+		"$ENV{'user.name'}:$ENV{'user.domain'}";
 	} elsif ($dropMenu eq '') {
 	    $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? 
 		    $ENV{'form.GD_BOX'.$newflg.'_'.$_} : 
@@ -2596,7 +2598,7 @@ sub editgrades {
     if ($noupdate) {
 #	my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
 	my $numcols=scalar(@partid)*4+2;
-	$result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occurred For the Students Below</td></tr>'.$noupdate;
+	$result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occurred For the Students Below</td></tr><tr bgcolor="#ffffde">'.$noupdate;
     }
     $result .= '</table></td></tr></table>'."\n".
 	&show_grading_menu_form ($symb,$url);
@@ -3108,7 +3110,7 @@ sub displayPage {
 	'<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
 	'<td><b>&nbsp;'.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';
 
-    my ($depth,$question) = (1,1);
+    my ($depth,$question,$prob) = (1,1,1);
     $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"
     while ($depth > 0) {
@@ -3119,7 +3121,7 @@ sub displayPage {
 	    my $parts = $curRes->parts();
             my $title = $curRes->compTitle();
 	    my $symbx = $curRes->symb();
-	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
+	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
 		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
 	    $studentTable.='<td valign="top">';
 	    if ($ENV{'form.vProb'} eq 'yes' ) {
@@ -3169,6 +3171,7 @@ sub displayPage {
 		    $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
 		    $question++;
 		}
+		$prob++;
 	    }
 	    $studentTable.='</td></tr>';
 
@@ -3211,7 +3214,8 @@ sub displaySubByDates {
 	    my @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
 #	    next if ($$record{"$version:resource.$partid.solved"} eq '');
 	    foreach my $matchKey (@matchKey) {
-		if (exists $$record{$version.':'.$matchKey}) {
+		if (exists($$record{$version.':'.$matchKey}) &&
+		    $$record{$version.':'.$matchKey} ne '') {
 		    my ($responseId)=($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/);
 		    $displaySub[0].='<b>Part&nbsp;'.$partid.'&nbsp;';
 		    $displaySub[0].='<font color="#999999">(ID&nbsp;'.
@@ -3295,7 +3299,7 @@ sub updateGradeByPage {
 
     $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"
-    my ($depth,$question,$changeflag)= (1,1,0);
+    my ($depth,$question,$prob,$changeflag)= (1,1,1,0);
     while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }
@@ -3304,7 +3308,7 @@ sub updateGradeByPage {
 	    my $parts = $curRes->parts();
             my $title = $curRes->compTitle();
 	    my $symbx = $curRes->symb();
-	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
+	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
 		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
 	    $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
 
@@ -3365,6 +3369,7 @@ sub updateGradeByPage {
 		'<td valign="top">'.$displayPts[1].'</td>'.
 		'</tr>';
 
+	    $prob++;
 	}
         $curRes = $iterator->next();
     }
@@ -3415,18 +3420,25 @@ sub getSequenceDropDown {
     return $result;
 }
 
-sub scantron_uploads {
-    if (!-e $Apache::lonnet::perlvar{'lonScansDir'}) { return ''};
-    my $result=	'<select name="scantron_selectfile">';
+sub scantron_filenames {
     my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
     my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
     my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
 				    &Apache::loncommon::propath($cdom,$cname));
-    $result.="<option></option>";
-    foreach my $filename (@files) {
+    my @possiblenames;
+    foreach my $filename (sort(@files)) {
 	($filename)=split(/&/,$filename);
 	if ($filename!~/^scantron_orig_/) { next ; }
 	$filename=~s/^scantron_orig_//;
+	push(@possiblenames,$filename);
+    }
+    return @possiblenames;
+}
+
+sub scantron_uploads {
+    my $result=	'<select name="scantron_selectfile">';
+    $result.="<option></option>";
+    foreach my $filename (sort(&scantron_filenames())) {
 	$result.="<option>$filename</option>\n";
     }
     $result.="</select>";
@@ -3452,7 +3464,7 @@ sub scantron_CODElist {
     my $cnum = $ENV{'course.'.$ENV{'request.course.id'}.'.num'};
     my @names=&Apache::lonnet::getkeys('CODEs',$cdom,$cnum);
     my $namechoice='<option></option>';
-    foreach my $name (@names) {
+    foreach my $name (sort(@names)) {
 	if ($name =~ /^error: 2 /) { next; }
 	$namechoice.='<option value="'.$name.'">'.$name.'</option>';
     }
@@ -3491,7 +3503,7 @@ sub scantron_selectphase {
     <tr>
       <td bgcolor="#777777">
        <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process">
-       <input type="hidden" name="command" value="scantron_validate" />
+       <input type="hidden" name="command" value="scantron_warning" />
         $default_form_data
         <table width="100%" border="0">
           <tr bgcolor="#e6ffff">
@@ -3517,8 +3529,8 @@ sub scantron_selectphase {
           <tr bgcolor="#ffffe6">
 	    <td> Options: </td>
             <td>
-                <input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only skipped records <br />
-                <input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove any exisiting corrections
+                <input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only previously skipped records <br />
+                <input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove all exisiting corrections
 	    </td>
           </tr>
           <tr bgcolor="#ffffe6">
@@ -3597,18 +3609,8 @@ SCANTRONFORM
               <td> Filename of scoring office file: </td><td> $file_selector </td>
             </tr>
             <tr bgcolor="#ffffe6">
-	      <td>
-                Records to download
-              </td>
-              <td>
-                  <input type="radio" name="scantron_options" value="download_skipped"/> Skipped Records <br />
-                  <input type="radio" name="scantron_options" value="download_corrected"/> Corrected Records <br />
-                  <input checked="on" type="radio" name="scantron_options" value="dowload_orig"/> Original Records
-              </td>
-            </tr>
-            <tr bgcolor="#ffffe6">
               <td colspan="2">
-                <input type="submit" value="Validate Scantron Records" />
+                <input type="submit" value="Show List of Files" />
               </td>
             </tr>
           </table>
@@ -3828,7 +3830,7 @@ sub scantron_process_corrections {
     my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();
     my $which=$ENV{'form.scantron_line'};
-    my $line=&scantron_get_line($scanlines,$which);
+    my $line=&scantron_get_line($scanlines,$scan_data,$which);
     my ($skip,$err,$errmsg);
     if ($ENV{'form.scantron_skip_record'}) {
 	$skip=1;
@@ -3874,31 +3876,95 @@ sub scantron_process_corrections {
     if ($err) {
 	$r->print("Unable to accept last correction, an error occurred :$errmsg:");
     } else {
-	&scantron_put_line($scanlines,$which,$line,$skip);
+	&scantron_put_line($scanlines,$scan_data,$which,$line,$skip);
 	&scantron_putfile($scanlines,$scan_data);
     }
 }
 
+sub reset_skipping_status {
+    my ($scanlines,$scan_data)=&scantron_getfile();
+    &scan_data($scan_data,'remember_skipping',undef,1);
+    &scantron_putfile(undef,$scan_data);
+}
 
-sub scantron_validate_file {
-    my ($r) = @_;
-    my ($symb,$url)=&get_symb_and_url($r);
-    if (!$symb) {return '';}
-    my $default_form_data=&defaultFormData($symb,$url);
-    if ($ENV{'form.scantron_options_ignore'} eq 'ignore_corrections') {
-	my $result=&scantron_remove('corrected');
-	if ($result ne 'ok' && $result ne 'not_found' ) {
-	    $r->print("An error occured ($result) when trying to Remove the existing corrections.");
+sub allow_skipping {
+    my ($scan_data,$i)=@_;
+    my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
+    delete($remembered{$i});
+    &scan_data($scan_data,'remember_skipping',join(':',%remembered));
+}
+
+sub should_be_skipped {
+    my ($scan_data,$i)=@_;
+    if ($ENV{'form.scantron_options_redo'} !~ /^redo_/) {
+	# not redoing old skips
+	return 0;
+    }
+    my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
+    if (exists($remembered{$i})) { return 0; }
+    return 1;
+}
+
+sub remember_current_skipped {
+    my ($scanlines,$scan_data)=&scantron_getfile();
+    my %to_remember;
+    for (my $i=0;$i<=$scanlines->{'count'};$i++) {
+	if ($scanlines->{'skipped'}[$i]) {
+	    $to_remember{$i}=1;
 	}
-	$ENV{'form.scantron_options_ignore'}='done';
     }
-    if ($ENV{'form.scantron_corrections'}) {
-	&scantron_process_corrections($r);
+    &Apache::lonnet::logthis('remembering '.join(':',%to_remember));
+    &scan_data($scan_data,'remember_skipping',join(':',%to_remember));
+    &scantron_putfile(undef,$scan_data);
+}
+
+sub check_for_error {
+    my ($r,$result)=@_;
+    if ($result ne 'ok' && $result ne 'not_found' ) {
+	$r->print("An error occured ($result) when trying to Remove the existing corrections.");
     }
-    $r->print("<p>Gathering neccessary info.</p>");$r->rflush();
-    my $max_bubble=&scantron_get_maxbubble($r);
-    #get the student pick code ready
-    $r->print(&Apache::loncommon::studentbrowser_javascript());
+}
+
+sub scantron_warning_screen {
+    my ($button_text)=@_;
+    my $title=&Apache::lonnet::gettitle($ENV{'form.selectpage'});
+    return (<<STUFF);
+<p>
+<font color="red">Please double check the information
+                 below before clicking on '$button_text'</font>
+</p>
+<table>
+<tr><td><b>Sequence To be Graded:</b></td><td>$title</td></tr>
+<tr><td><b>Data File that will be used:</b></td><td><tt>$ENV{'form.scantron_selectfile'}</tt></td></tr>
+</table>
+</font>
+<br />
+<p> If this information is correct, please click on '$button_text'.</p>
+<p> If something is incorrect, please click the 'Grading Menu' button to start over.</p>
+
+<br />
+STUFF
+}
+
+sub scantron_do_warning {
+    my ($r)=@_;
+    my ($symb,$url)=&get_symb_and_url($r);
+    if (!$symb) {return '';}
+    my $default_form_data=&defaultFormData($symb,$url);
+    $r->print(&scantron_form_start().$default_form_data);
+    my $warning=&scantron_warning_screen('Validate Records');
+    $r->print(<<STUFF);
+$warning
+<input type="submit" name="submit" value="Validate Records" />
+<input type="hidden" name="command" value="scantron_validate" />
+</form>
+STUFF
+    $r->print("<br />".&show_grading_menu_form($symb,$url)."</body></html>");
+    return '';
+}
+
+sub scantron_form_start {
+    my ($max_bubble)=@_;
     my $result= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
   <input type="hidden" name="selectpage" value="$ENV{'form.selectpage'}" />
@@ -3909,8 +3975,42 @@ sub scantron_validate_file {
   <input type="hidden" name="scantron_CODEunique" value="$ENV{'form.scantron_CODEunique'}" />
   <input type="hidden" name="scantron_options_redo" value="$ENV{'form.scantron_options_redo'}" />
   <input type="hidden" name="scantron_options_ignore" value="$ENV{'form.scantron_options_ignore'}" />
-  $default_form_data
 SCANTRONFORM
+    return $result;
+}
+
+sub scantron_validate_file {
+    my ($r) = @_;
+    my ($symb,$url)=&get_symb_and_url($r);
+    if (!$symb) {return '';}
+    my $default_form_data=&defaultFormData($symb,$url);
+    
+    # do the detection of only doing skipped records first befroe we delete
+    # them  when doing the corrections reset
+    if ($ENV{'form.scantron_options_redo'} ne 'redo_skipped_ready') {
+	&reset_skipping_status();
+    }
+    if ($ENV{'form.scantron_options_redo'} eq 'redo_skipped') {
+	&remember_current_skipped();
+	&scantron_remove_file('skipped');
+	$ENV{'form.scantron_options_redo'}='redo_skipped_ready';
+    }
+
+    if ($ENV{'form.scantron_options_ignore'} eq 'ignore_corrections') {
+	&check_for_error($r,&scantron_remove_file('corrected'));
+	&check_for_error($r,&scantron_remove_file('skipped'));
+	&check_for_error($r,&scantron_remove_scan_data());
+	$ENV{'form.scantron_options_ignore'}='done';
+    }
+
+    if ($ENV{'form.scantron_corrections'}) {
+	&scantron_process_corrections($r);
+    }
+    $r->print("<p>Gathering neccessary info.</p>");$r->rflush();
+    #get the student pick code ready
+    $r->print(&Apache::loncommon::studentbrowser_javascript());
+    my $max_bubble=&scantron_get_maxbubble($r);
+    my $result=&scantron_form_start($max_bubble).$default_form_data;
     $r->print($result);
     
     my @validate_phases=( 'ID',
@@ -3933,9 +4033,14 @@ SCANTRONFORM
 	}
     }
     if (!$stop) {
-	$r->print("Validation process complete.<br />");
-	$r->print('<input type="submit" name="submit" value="Start Grading" />');
-	$r->print('<input type="hidden" name="command" value="scantron_process" />');
+	my $warning=&scantron_warning_screen('Start Grading');
+	$r->print(<<STUFF);
+Validation process complete.<br />
+$warning
+<input type="submit" name="submit" value="Start Grading" />
+<input type="hidden" name="command" value="scantron_process" />
+STUFF
+
     } else {
 	$r->print('<input type="hidden" name="command" value="scantron_validate" />');
 	$r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />");
@@ -3951,35 +4056,44 @@ SCANTRONFORM
     return '';
 }
 
-sub scantron_remove {
+sub scantron_remove_file {
     my ($which)=@_;
     my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
     my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
     my $file='scantron_';
-    if ($which eq 'corrected') {
-	$file.='corrected_';
+    if ($which eq 'corrected' || $which eq 'skipped') {
+	$file.=$which.'_';
     } else {
 	return 'refused';
     }
     $file.=$ENV{'form.scantron_selectfile'};
-    my $result=&Apache::lonnet::removeuserfile($cname,$cdom,$file);
+    return &Apache::lonnet::removeuserfile($cname,$cdom,$file);
+}
+
+sub scantron_remove_scan_data {
+    my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+    my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
     my @keys=&Apache::lonnet::getkeys('nohist_scantrondata',$cdom,$cname);
     my @todelete;
     my $filename=$ENV{'form.scantron_selectfile'};
     foreach my $key (@keys) {
 	if ($key=~/^\Q$filename\E_/) {
+	    if ($ENV{'form.scantron_options_redo'} eq 'redo_skipped_ready' &&
+		$key=~/remember_skipping/) {
+		next;
+	    }
 	    push(@todelete,$key);
 	}
     }
+    my $result;
     if (@todelete) {
-	&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname);
+	$result=&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname);
     }
     return $result;
 }
 
 sub scantron_getfile {
-    #FIXME really would prefer a scantron directory but tokenwrapper
-    # doesn't allow access to subdirs of userfiles
+    #FIXME really would prefer a scantron directory
     my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
     my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
     my $lines;
@@ -4022,34 +4136,48 @@ sub lonnet_putfile {
 
 sub scantron_putfile {
     my ($scanlines,$scan_data) = @_;
-    #FIXME really would prefer a scantron directory but tokenwrapper
-    # doesn't allow access to subdirs of userfiles
+    #FIXME really would prefer a scantron directory
     my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
     my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
-    my $prefix='scantron_';
+    if ($scanlines) {
+	my $prefix='scantron_';
 # no need to update orig, shouldn't change
 #   &lonnet_putfile(join("\n",@{$scanlines->{'orig'}}),$prefix.'orig_'.
 #		    $ENV{'form.scantron_selectfile'});
-    &lonnet_putfile(join("\n",@{$scanlines->{'corrected'}}),
-		    $prefix.'corrected_'.
-		    $ENV{'form.scantron_selectfile'});
-    &lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}),
-		    $prefix.'skipped_'.
-		    $ENV{'form.scantron_selectfile'});
+	&lonnet_putfile(join("\n",@{$scanlines->{'corrected'}}),
+			$prefix.'corrected_'.
+			$ENV{'form.scantron_selectfile'});
+	&lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}),
+			$prefix.'skipped_'.
+			$ENV{'form.scantron_selectfile'});
+    }
     &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname);
 }
 
 sub scantron_get_line {
-    my ($scanlines,$i)=@_;
-    if ($scanlines->{'skipped'}[$i]) {return undef;}
+    my ($scanlines,$scan_data,$i)=@_;
+    if (&should_be_skipped($scan_data,$i)) { return undef; }
+    if ($scanlines->{'skipped'}[$i]) { return undef; }
     if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];}
     return $scanlines->{'orig'}[$i]; 
 }
 
+sub get_todo_count {
+    my ($scanlines,$scan_data)=@_;
+    my $count=0;
+    for (my $i=0;$i<=$scanlines->{'count'};$i++) {
+	my $line=&scantron_get_line($scanlines,$scan_data,$i);
+	if ($line=~/^[\s\cz]*$/) { next; }
+	$count++;
+    }
+    return $count;
+}
+
 sub scantron_put_line {
-    my ($scanlines,$i,$newline,$skip)=@_;
+    my ($scanlines,$scan_data,$i,$newline,$skip)=@_;
     if ($skip) {
 	$scanlines->{'skipped'}[$i]=$newline;
+	&allow_skipping($scan_data,$i);
 	return;
     }
     $scanlines->{'corrected'}[$i]=$newline;
@@ -4068,7 +4196,7 @@ sub scantron_validate_ID {
 
     my %found=('ids'=>{},'usernames'=>{});
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
-	my $line=&scantron_get_line($scanlines,$i);
+	my $line=&scantron_get_line($scanlines,$scan_data,$i);
 	if ($line=~/^[\s\cz]*$/) { next; }
 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
 						 $scan_data);
@@ -4303,7 +4431,7 @@ sub scantron_validate_CODE {
 
     my ($scanlines,$scan_data)=&scantron_getfile();
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
-	my $line=&scantron_get_line($scanlines,$i);
+	my $line=&scantron_get_line($scanlines,$scan_data,$i);
 	if ($line=~/^[\s\cz]*$/) { next; }
 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
 						 $scan_data);
@@ -4337,7 +4465,7 @@ sub scantron_validate_doublebubble {
     my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
-	my $line=&scantron_get_line($scanlines,$i);
+	my $line=&scantron_get_line($scanlines,$scan_data,$i);
 	if ($line=~/^[\s\cz]*$/) { next; }
 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
 						 $scan_data);
@@ -4387,7 +4515,7 @@ sub scantron_validate_missingbubbles {
     my $max_bubble=&scantron_get_maxbubble();
     if (!$max_bubble) { $max_bubble=2**31; }
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
-	my $line=&scantron_get_line($scanlines,$i);
+	my $line=&scantron_get_line($scanlines,$scan_data,$i);
 	if ($line=~/^[\s\cz]*$/) { next; }
 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
 						 $scan_data);
@@ -4432,19 +4560,25 @@ SCANTRONFORM
     my @delayqueue;
     my %completedstudents;
     
+    my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
- 				    'Scantron Progress',$scanlines->{'count'},
+ 				    'Scantron Progress',$count,
 				    'inline',undef,'scantronupload');
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
 					  'Processing first student');
     my $start=&Time::HiRes::time();
     my $i=-1;
-    my ($uname,$udom);
+    my ($uname,$udom,$started);
     while ($i<$scanlines->{'count'}) {
  	($uname,$udom)=('','');
  	$i++;
- 	my $line=&scantron_get_line($scanlines,$i);
+ 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
  	if ($line=~/^[\s\cz]*$/) { next; }
+	if ($started) {
+	    &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
+						     'last student');
+	}
+	$started=1;
  	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
  						 $scan_data);
  	unless ($uname=&scantron_find_student($scan_record,$scan_data,
@@ -4482,15 +4616,13 @@ SCANTRONFORM
     } continue {
 	&Apache::lonnet::delenv('form.counter');
 	&Apache::lonnet::delenv('scantron\.');
-	&Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
-						 'last student');
     }
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
 #    my $lasttime = &Time::HiRes::time()-$start;
 #    $r->print("<p>took $lasttime</p>");
 
     $navmap->untieHashes();
-    $r->print("</form><p>Done</p>");
+    $r->print("</form>");
     $r->print(&show_grading_menu_form($symb,$url));
     return '';
 }
@@ -4586,6 +4718,50 @@ sub scantron_upload_scantron_data_save {
     return '';
 }
 
+sub valid_file {
+    my ($requested_file)=@_;
+    foreach my $filename (sort(&scantron_filenames())) {
+	&Apache::lonnet::logthis("$requested_file  $filename");
+	if ($requested_file eq $filename) { return 1; }
+    }
+    return 0;
+}
+
+sub scantron_download_scantron_data {
+    my ($r)=@_;
+    my $default_form_data=&defaultFormData(&get_symb_and_url($r,1));
+    my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+    my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
+    my $file=$ENV{'form.scantron_selectfile'};
+    if (! &valid_file($file)) {
+	$r->print(<<ERROR);
+	<p>
+	    The requested file name was invalid.
+        </p>
+ERROR
+	$r->print(&show_grading_menu_form(&get_symb_and_url($r,1)));
+	return;
+    }
+    my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
+    my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
+    my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
+    &Apache::lonnet::allowuploaded('/adm/grades',$orig);
+    &Apache::lonnet::allowuploaded('/adm/grades',$corrected);
+    &Apache::lonnet::allowuploaded('/adm/grades',$skipped);
+    $r->print(<<DOWNLOAD);
+    <p>
+	<a href="$orig">Original</a> file as uploaded by the scantron office.
+    </p>
+    <p>
+	<a href="$corrected">Corrections</a>, a file of corrected records that were used in grading.
+    </p>
+    <p>
+	<a href="$skipped">Skipped</a>, a file of records that were skipped.
+    </p>
+DOWNLOAD
+    $r->print(&show_grading_menu_form(&get_symb_and_url($r,1)));
+    return '';
+}
 
 #-------- end of section for handling grading scantron forms -------
 #
@@ -4863,8 +5039,8 @@ sub handler {
 	    }
 	} elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
 	    $request->print(&scantron_selectphase($request));
- 	} elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
- 	    $request->print(&scantron_validate_file($request));
+ 	} elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
+ 	    $request->print(&scantron_do_warning($request));
 	} elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
 	    $request->print(&scantron_validate_file($request));
 	} elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
@@ -4877,7 +5053,7 @@ sub handler {
  		 (&Apache::lonnet::allowed('usc',$ENV{'request.role.domain'})||
 		  &Apache::lonnet::allowed('usc',$ENV{'request.course.id'}))) {
  	    $request->print(&scantron_upload_scantron_data_save($request));
- 	} elsif ($command eq 'scantrondownload' &&
+ 	} elsif ($command eq 'scantron_download' &&
 		 &Apache::lonnet::allowed('usc',$ENV{'request.course.id'})) {
  	    $request->print(&scantron_download_scantron_data($request));
 	} elsif ($command) {