--- loncom/homework/grades.pm	2004/04/20 06:11:49	1.186
+++ loncom/homework/grades.pm	2004/04/29 07:21:16	1.192
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.186 2004/04/20 06:11:49 albertel Exp $
+# $Id: grades.pm,v 1.192 2004/04/29 07:21:16 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -3422,6 +3422,7 @@ sub scantron_uploads {
     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) {
 	($filename)=split(/&/,$filename);
 	if ($filename!~/^scantron_orig_/) { next ; }
@@ -3435,6 +3436,7 @@ sub scantron_uploads {
 sub scantron_scantab {
     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
     my $result='<select name="scantron_format">'."\n";
+    $result.='<option></option>'."\n";
     foreach my $line (<$fh>) {
 	my ($name,$descrip)=split(/:/,$line);
 	if ($name =~ /^\#/) { next; }
@@ -3451,6 +3453,7 @@ sub scantron_CODElist {
     my @names=&Apache::lonnet::getkeys('CODEs',$cdom,$cnum);
     my $namechoice='<option></option>';
     foreach my $name (@names) {
+	if ($name =~ /^error: 2 /) { next; }
 	$namechoice.='<option value="'.$name.'">'.$name.'</option>';
     }
     $namechoice='<select name="scantron_CODElist">'.$namechoice.'</select>';
@@ -3487,7 +3490,7 @@ sub scantron_selectphase {
     <table width="100%" border="0">
     <tr>
       <td bgcolor="#777777">
-       <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantro_process">
+       <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process">
        <input type="hidden" name="command" value="scantron_validate" />
         $default_form_data
         <table width="100%" border="0">
@@ -3512,10 +3515,10 @@ sub scantron_selectphase {
             <td> Each CODE is only to be used once:</td><td> $CODE_unique </td>
           </tr>
           <tr bgcolor="#ffffe6">
+	    <td> Options: </td>
             <td>
-<!-- FIXME this is lazy, a single parse of the set should let me know what this is -->
-              Last line to expect an answer on: </td><td>
-                <input type="text" name="scantron_maxbubble" />
+                <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
 	    </td>
           </tr>
           <tr bgcolor="#ffffe6">
@@ -3579,6 +3582,40 @@ UPLOAD
     </tr>
 SCANTRONFORM
     }
+    $r->print(<<SCANTRONFORM);
+    <tr>
+      <td bgcolor="#777777">
+        <form action='/adm/grades' name='scantron_download'>
+          <input type="hidden" name="command" value="scantron_download" />
+          <table width="100%" border="0">
+            <tr bgcolor="#e6ffff">
+              <td colspan="2">
+                &nbsp;<b>Download a scoring office file</b>
+              </td>
+            </tr>
+            <tr bgcolor="#ffffe6">
+              <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" />
+              </td>
+            </tr>
+          </table>
+        </form>
+      </td>
+    </tr>
+SCANTRONFORM
 
     $r->print(<<SCANTRONFORM);
   </table>
@@ -3648,17 +3685,19 @@ sub scantron_fixup_scanline {
 		       $args->{'username'}.':'.$args->{'domain'});
 	}
     } elsif ($field eq 'CODE') {
-	if (length($args->{'CODE'}) > $$scantron_config{'CODElength'}) {
-	    return ($line,1,'New CODE value too large');
+	if ($args->{'CODE_ignore_dup'}) {
+	    &scan_data($scan_data,"$whichline.CODE_ignore_dup",'1');
 	}
-	if (length($args->{'CODE'}) < $$scantron_config{'CODElength'}) {
-	    $args->{'CODE'}=sprintf('%-'.$$scantron_config{'CODElength'}.'s',
-				       $args->{'CODE'});
-	}
-	substr($line,$$scantron_config{'CODEstart'}-1,
-	       $$scantron_config{'CODElength'})=$args->{'CODE'};
-	if ($args->{'CODE'}=~/^\s*$/) {
-	    &scan_data($scan_data,"$whichline.CODE",$args->{'CODE'});
+	&scan_data($scan_data,"$whichline.useCODE",'1');
+	if ($args->{'CODE'} ne 'use_unfound') {
+	    if (length($args->{'CODE'}) > $$scantron_config{'CODElength'}) {
+		return ($line,1,'New CODE value too large');
+	    }
+	    if (length($args->{'CODE'}) < $$scantron_config{'CODElength'}) {
+		$args->{'CODE'}=sprintf('%-'.$$scantron_config{'CODElength'}.'s',$args->{'CODE'});
+	    }
+	    substr($line,$$scantron_config{'CODEstart'}-1,
+		   $$scantron_config{'CODElength'})=$args->{'CODE'};
 	}
     } elsif ($field eq 'answer') {
 	my $length=$scantron_config->{'Qlength'};
@@ -3690,18 +3729,26 @@ sub scan_data {
 }
 
 sub scantron_parse_scanline {
-    my ($line,$whichline,$scantron_config,$scan_data)=@_;
+    my ($line,$whichline,$scantron_config,$scan_data,$justCODE)=@_;
     my %record;
     my $questions=substr($line,$$scantron_config{'Qstart'}-1);
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);
     if ($$scantron_config{'CODElocation'} ne 0) {
 	if ($$scantron_config{'CODElocation'} < 0) {
-	    $record{'scantron.CODE'}=substr($data,$$scantron_config{'CODEstart'}-1,
+	    $record{'scantron.CODE'}=substr($data,
+					    $$scantron_config{'CODEstart'}-1,
 					    $$scantron_config{'CODElength'});
+	    if (&scan_data($scan_data,"$whichline.useCODE")) {
+		$record{'scantron.useCODE'}=1;
+	    }
+	    if (&scan_data($scan_data,"$whichline.CODE_ignore_dup")) {
+		$record{'scantron.CODE_ignore_dup'}=1;
+	    }
 	} else {
 	    #FIXME interpret first N questions
 	}
     }
+    if ($justCODE) { return \%record; }
     $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,
 				  $$scantron_config{'IDlength'});
     $record{'scantron.PaperID'}=
@@ -3794,10 +3841,23 @@ sub scantron_process_corrections {
 				    'username'=>$ENV{'form.scantron_username'},
 				    'domain'=>$ENV{'form.scantron_domain'}});
     } elsif ($ENV{'form.scantron_corrections'} =~ /^(duplicate|incorrect)CODE$/) {
-	my $newCODE=$ENV{'form.scantron_CODE'};
+	my $resolution=$ENV{'form.scantron_CODE_resolution'};
+	my $newCODE;
+	my %args;
+	if      ($resolution eq 'use_unfound') {
+	    $newCODE='use_unfound';
+	} elsif ($resolution eq 'use_found') {
+	    $newCODE=$ENV{'form.scantron_CODE_selectedvalue'};
+	} elsif ($resolution eq 'use_typed') {
+	    $newCODE=$ENV{'form.scantron_CODE_newvalue'};
+	}
+	if ($ENV{'form.scantron_corrections'} eq 'duplicateCODE') {
+	    $args{'CODE_ignore_dup'}=1;
+	}
+	$args{'CODE'}=$newCODE;
 	($line,$err,$errmsg)=
 	    &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
-				     'CODE',{'CODE'=>$newCODE});
+				     'CODE',\%args);
     } elsif ($ENV{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) {
 	foreach my $question (split(',',$ENV{'form.scantron_questions'})) {
 	    ($line,$err,$errmsg)=
@@ -3822,10 +3882,19 @@ sub scantron_validate_file {
     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');
+	&Apache::lonnet::logthis("result was $result");
+	if ($result ne 'ok' && $result ne 'not_found' ) {
+	    $r->print("An error occured ($result) when trying to Remove the existing corrections.");
+	}
+	$ENV{'form.scantron_options_ignore'}='done';
+    }
     if ($ENV{'form.scantron_corrections'}) {
 	&scantron_process_corrections($r);
     }
+    $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());
     my $result= <<SCANTRONFORM;
@@ -3833,7 +3902,11 @@ sub scantron_validate_file {
   <input type="hidden" name="selectpage" value="$ENV{'form.selectpage'}" />
   <input type="hidden" name="scantron_format" value="$ENV{'form.scantron_format'}" />
   <input type="hidden" name="scantron_selectfile" value="$ENV{'form.scantron_selectfile'}" />
-  <input type="hidden" name="scantron_maxbubble" value="$ENV{'form.scantron_maxbubble'}" />
+  <input type="hidden" name="scantron_maxbubble" value="$max_bubble'" />
+  <input type="hidden" name="scantron_CODElist" value="$ENV{'form.scantron_CODElist'}" />
+  <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
     $r->print($result);
@@ -3880,6 +3953,37 @@ SCANTRONFORM
     return '';
 }
 
+sub scantron_remove {
+    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_';
+    } else {
+	return 'refused';
+    }
+    $file.=$ENV{'form.scantron_selectfile'};
+    &Apache::lonnet::logthis("removeing $file");
+    my $result=&Apache::lonnet::removeuserfile($cname,$cdom,$file);
+    my @keys=&Apache::lonnet::getkeys('nohist_scantrondata',$cdom,$cname);
+    &Apache::lonnet::logthis('got keys '.join(':',@keys));
+    &Apache::lonnet::logthis("cdom $cdom cname $cname");
+    my @todelete;
+    my $filename=$ENV{'form.scantron_selectfile'};
+    &Apache::lonnet::logthis('filename '.$filename);
+    foreach my $key (@keys) {
+	if ($key=~/^\Q$filename\E_/) {
+	    push(@todelete,$key);
+	}
+    }
+    &Apache::lonnet::logthis('todelete '.join(':',@todelete));
+    if (@todelete) {
+	&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
@@ -4061,18 +4165,42 @@ sub scantron_get_correction {
 	$r->print('</li>');
     } elsif ($error =~ /CODE$/) {
 	if ($error eq 'incorrectCODE') {
-	    $r->print("The encoded CODE is not in the list of possible CODEs</p>\n");
+	    $r->print("</p><p>The encoded CODE is not in the list of possible CODEs</p>\n");
 	} elsif ($error eq 'duplicateCODE') {
-	    $r->print("The encoded CODE has also been used by a previous paper $arg, and CODEs were supposed to be unique</p>\n");
+	    $r->print("</p><p>The encoded CODE has also been used by a previous paper $arg, and CODEs were supposed to be unique</p>\n");
 	}
+	$r->print("<p>The CODE on the form is  <tt>".
+		  $$scan_record{'scantron.CODE'}."</tt><br />\n");
 	$r->print("<p>The ID on the form is  <tt>".
 		  $$scan_record{'scantron.ID'}."</tt><br />\n");
 	$r->print("The name on the paper is ".
 		  $$scan_record{'scantron.LastName'}.",".
 		  $$scan_record{'scantron.FirstName'}."</p>");
 	$r->print("<p>How should I handle this? <br /> \n");
-	$r->print("\n<ul><li> ");
-	$r->print('</li>');
+	$r->print("\n<br /> ");
+	$r->print("<input type='radio' name='scantron_CODE_resolution' value='use_unfound' checked='on' /> Use the CODE <b><tt>".$$scan_record{'scantron.CODE'}."</tt></b> that is was on the paper, ignoring the error.");
+	$r->print("\n<br />");
+	$r->print(<<ENDSCRIPT);
+<script type="text/javascript">
+function change_radio(field) {
+    var slct=document.scantronupload.scantron_CODE_resolution;
+    var i;
+    for (i=0;i<slct.length;i++) {
+        if (slct[i].value==field) { slct[i].checked=true; }
+    }
+}
+</script>
+ENDSCRIPT
+	my $href="/adm/pickcode?".
+	   "form=".&Apache::lonnet::escape("scantronupload").
+	   "&scantron_format=".&Apache::lonnet::escape($ENV{'form.scantron_format'}).
+	   "&scantron_CODElist=".&Apache::lonnet::escape($ENV{'form.scantron_CODElist'}).
+	   "&curCODE=".&Apache::lonnet::escape($$scan_record{'scantron.CODE'}).
+	   "&scantron_selectfile=".&Apache::lonnet::escape($ENV{'form.scantron_selectfile'});
+	$r->print("<input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it. Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />");
+	$r->print("\n<br />");
+	$r->print("<input type='radio' name='scantron_CODE_resolution' value='use_typed' /> Use <input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" /> as the CODE.");
+	$r->print("\n<br /><br />");
     } elsif ($error eq 'doublebubble') {
 #FIXME Need to print out who this is along with the paper info
 	$r->print("<p>There have been multiple bubbles scanned for a some question(s)</p>\n");
@@ -4128,11 +4256,10 @@ sub scantron_validate_CODE {
     if ($scantron_config{'CODElocation'} &&
 	$scantron_config{'CODEstart'} &&
 	$scantron_config{'CODElength'}) {
-	if (!$ENV{'form.scantron_CODElist'}) {
+	if (!defined($ENV{'form.scantron_CODElist'})) {
 	    &FIXME_blow_up()
 	}
     } else {
-	&Apache::lonnet::logthis(" CODE stuf $scantron_config{'CODElocation'}:$scantron_config{'CODEstart'}:$scantron_config{'CODElength'}");
 	return (0,$currentphase+1);
     }
     
@@ -4142,7 +4269,7 @@ sub scantron_validate_CODE {
     my $cdom =$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
     my $cnum =$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
     my %result=&Apache::lonnet::get('CODEs',[$old_name],$cdom,$cnum);
-    my %allcodes=map {($_,1)} split(',',$result{$old_name});
+    my %allcodes=map {(&Apache::lonprintout::num_to_letters($_),1)} split(',',$result{$old_name});
 
     my ($scanlines,$scan_data)=&scantron_getfile();
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
@@ -4152,13 +4279,14 @@ sub scantron_validate_CODE {
 						 $scan_data);
 	my $CODE=$$scan_record{'scantron.CODE'};
 	my $error=0;
-	if (!exists($allcodes{$CODE})) {
+	if (!exists($allcodes{$CODE}) && !$$scan_record{'scantron.useCODE'}) {
 	    &scantron_get_correction($r,$i,$scan_record,
 				     \%scantron_config,
 				     $line,'incorrectCODE',$CODE);
 	    return(1);
 	}
-	if (exists($usedCODEs{$CODE}) && $ENV{'form.scantron_CODEunique'}) {
+	if (exists($usedCODEs{$CODE}) && $ENV{'form.scantron_CODEunique'}
+	    && !$$scan_record{'scantron.CODE_ignore_dup'}) {
 	    &scantron_get_correction($r,$i,$scan_record,
 				     \%scantron_config,
 				     $line,'duplicateCODE',$CODE);
@@ -4192,6 +4320,31 @@ sub scantron_validate_doublebubble {
     return (0,$currentphase+1);
 }
 
+sub scantron_get_maxbubble {
+    my ($r)=@_;
+    if (defined($ENV{'form.scantron_maxbubble'}) &&
+	$ENV{'form.scantron_maxbubble'}) {
+	return $ENV{'form.scantron_maxbubble'};
+    }
+    my $navmap=Apache::lonnavmaps::navmap->new();
+    my (undef,undef,$sequence)=
+	&Apache::lonnet::decode_symb($ENV{'form.selectpage'});
+    my $map=$navmap->getResourceByUrl($sequence);
+    my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
+    &Apache::lonnet::delenv('form.counter');
+    foreach my $resource (@resources) {
+	my $result=&Apache::lonnet::ssi($resource->src());
+    }
+    &Apache::lonnet::delenv('scantron\.');
+    my $envfile=$ENV{'user.environment'};
+    $envfile=~/\/([^\/]+)\.id$/;
+    $envfile=$1;
+    &Apache::lonnet::transfer_profile_to_env($r->dir_config('lonIDsDir'),
+					     $envfile);
+    $ENV{'form.scantron_maxbubble'}=$ENV{'form.counter'}-1;
+    return $ENV{'form.scantron_maxbubble'};
+}
+
 sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;
     #get student info
@@ -4201,7 +4354,7 @@ sub scantron_validate_missingbubbles {
     #get scantron line setup
     my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();
-    my $max_bubble=$ENV{'form.scantron_maxbubble'};
+    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);