--- loncom/homework/grades.pm 2004/04/22 22:57:53 1.187 +++ loncom/homework/grades.pm 2004/05/10 23:18:27 1.199 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.187 2004/04/22 22:57:53 albertel Exp $ +# $Id: grades.pm,v 1.199 2004/05/10 23:18:27 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -1669,7 +1669,9 @@ KEYWORDS $partid.'</b> <font color="#999999">( ID '.$respid. ' )</font> '; 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.'_'.$_} : @@ -3108,7 +3110,7 @@ sub displayPage { '<td align="center"><b> Prob. </b></td>'. '<td><b> '.($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}).' 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 '.$partid.' '; $displaySub[0].='<font color="#999999">(ID '. @@ -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}).' parts)').'</td>'; $studentTable.='<td valign="top"> <b>'.$title.'</b> </td>'; @@ -3365,6 +3369,7 @@ sub updateGradeByPage { '<td valign="top">'.$displayPts[1].'</td>'. '</tr>'; + $prob++; } $curRes = $iterator->next(); } @@ -3422,6 +3427,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 +3441,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 +3458,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>'; @@ -3512,17 +3520,10 @@ sub scantron_selectphase { <td> Each CODE is only to be used once:</td><td> $CODE_unique </td> </tr> <tr bgcolor="#ffffe6"> - <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" /> - </td> - </tr> - <tr bgcolor="#ffffe6"> <td> Options: </td> <td> - <input type="checkbox" name="scantron_options" value="redo_skipped"/> Redo skipped records <br /> - <input type="checkbox" name="scantron_options" value="ignore_corrections"/> Ignore Original Corrections + <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"> @@ -3689,17 +3690,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'}; @@ -3731,19 +3734,25 @@ sub scan_data { } sub scantron_parse_scanline { - my ($line,$whichline,$scantron_config,$scan_data,$justCODE)=@_; + my ($line,$whichline,$scantron_config,$scan_data,$justHeader)=@_; 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'}= @@ -3755,6 +3764,8 @@ sub scantron_parse_scanline { $record{'scantron.LastName'}= substr($data,$$scantron_config{'LastName'}-1, $$scantron_config{'LastNamelength'}); + if ($justHeader) { return \%record; } + my @alphabet=('A'..'Z'); my $questnum=0; while ($questions) { @@ -3836,10 +3847,25 @@ 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'}; + } elsif ($resolution =~ /^use_closest_(\d+)/) { + $newCODE=$ENV{"form.scantron_CODE_closest_$1"}; + } + 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)= @@ -3864,10 +3890,18 @@ 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'); + 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; @@ -3875,7 +3909,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); @@ -3885,14 +3923,10 @@ SCANTRONFORM 'doublebubble', 'missingbubbles'); if (!$ENV{'form.validatepass'}) { - $ENV{'form.valiadatepass'} = 0; + $ENV{'form.validatepass'} = 0; } - my $currentphase=$ENV{'form.valiadatepass'}; + my $currentphase=$ENV{'form.validatepass'}; - if ($ENV{'form.scantron_selectfile'}=~m-^/-) { - #first pass copy file to classdir - - } my $stop=0; while (!$stop && $currentphase < scalar(@validate_phases)) { $r->print("<p> Validating ".$validate_phases[$currentphase]."</p>"); @@ -3922,6 +3956,32 @@ 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'}; + my $result=&Apache::lonnet::removeuserfile($cname,$cdom,$file); + 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_/) { + push(@todelete,$key); + } + } + 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 @@ -4027,11 +4087,11 @@ sub scantron_validate_ID { if ($found{'ids'}{$found}) { &scantron_get_correction($r,$i,$scan_record,\%scantron_config, $line,'duplicateID',$found); - return(1); + return(1,$currentphase); } elsif ($found{'usernames'}{$username}) { &scantron_get_correction($r,$i,$scan_record,\%scantron_config, $line,'duplicateID',$username); - return(1); + return(1,$currentphase); } #FIXME store away line we previously saw the ID on to use above $found{'ids'}{$found}++; @@ -4043,18 +4103,18 @@ sub scantron_validate_ID { &scantron_get_correction($r,$i,$scan_record, \%scantron_config, $line,'duplicateID',$username); - return(1); + return(1,$currentphase); } elsif (!defined($username)) { &scantron_get_correction($r,$i,$scan_record, \%scantron_config, $line,'incorrectID'); - return(1); + return(1,$currentphase); } $found{'usernames'}{$username}++; } else { &scantron_get_correction($r,$i,$scan_record,\%scantron_config, $line,'incorrectID'); - return(1); + return(1,$currentphase); } } } @@ -4105,7 +4165,7 @@ sub scantron_get_correction { if ($error eq 'incorrectCODE') { $r->print("</p><p>The encoded CODE is not in the list of possible CODEs</p>\n"); } elsif ($error eq 'duplicateCODE') { - $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><p>The encoded CODE has also been used by a previous paper ".join(', ',@{$arg}).", and CODEs are supposed to be unique</p>\n"); } $r->print("<p>The CODE on the form is <tt>". $$scan_record{'scantron.CODE'}."</tt><br />\n"); @@ -4116,20 +4176,43 @@ sub scantron_get_correction { $$scan_record{'scantron.FirstName'}."</p>"); $r->print("<p>How should I handle this? <br /> \n"); $r->print("\n<br /> "); - $r->print("<input type='radio' name='scan_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."); + my $i=0; + if ($error eq 'incorrectCODE') { + my ($max,$closest)=&scantron_get_closely_matching_CODEs($arg,$$scan_record{'scantron.CODE'}); + foreach my $testcode (@{$closest}) { + my $checked=''; + if (!$i) { $checked=' checked="on" '; } + $r->print("<input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> Use the similar CODE <b><tt>".$testcode."</tt></b> instead.<input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />"); + $r->print("\n<br />"); + $i++; + } + } + my $checked; if (!$i) { $checked=' checked="on" '; } + $r->print("<input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> 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='scan_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 type='text' size='8' name='scan_CODE_selectedvalue' />"); + $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='scan_CODE_resolution' value='use_typed' /> Use <input type='text' size='8' name='scan_CODE_newvalue' /> as the CODE."); + $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"); $r->print('<input type="hidden" name="scantron_questions" value="'. join(',',@{$arg}).'" />'); @@ -4176,28 +4259,52 @@ sub scantron_bubble_selector { $r->print('</tr></table>'); } +sub num_matches { + my ($orig,$code) = @_; + my @code=split(//,$code); + my @orig=split(//,$orig); + my $same=0; + for (my $i=0;$i<scalar(@code);$i++) { + if ($code[$i] eq $orig[$i]) { $same++; } + } + return $same; +} + +sub scantron_get_closely_matching_CODEs { + my ($allcodes,$CODE)=@_; + my @CODEs; + foreach my $testcode (sort(keys(%{$allcodes}))) { + push(@{$CODEs[&num_matches($CODE,$testcode)]},$testcode); + } + + return ($#CODEs,$CODEs[-1]); +} + +sub get_codes { + my $old_name=$ENV{'form.scantron_CODElist'}; + 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 {(&Apache::lonprintout::num_to_letters($_),1)} split(',',$result{$old_name}); + return %allcodes; +} + sub scantron_validate_CODE { my ($r,$currentphase) = @_; - #FIXME doesn't do anything yet my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 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); } my %usedCODEs; - my $old_name=$ENV{'form.scantron_CODElist'}; - 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=&get_codes(); my ($scanlines,$scan_data)=&scantron_getfile(); for (my $i=0;$i<=$scanlines->{'count'};$i++) { @@ -4207,19 +4314,20 @@ 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); + $line,'incorrectCODE',\%allcodes); + return(1,$currentphase); } - 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); - return(1); + $line,'duplicateCODE',$usedCODEs{$CODE}); + return(1,$currentphase); } - $usedCODEs{$CODE}++; + push (@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'}); } return (0,$currentphase+1); } @@ -4247,6 +4355,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 @@ -4256,7 +4389,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); @@ -4305,7 +4438,8 @@ SCANTRONFORM my %completedstudents; my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status', - 'Scantron Progress',$scanlines->{'count'}); + 'Scantron Progress',$scanlines->{'count'}, + 'inline',undef,'scantronupload'); &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, 'Processing first student'); my $start=&Time::HiRes::time(); @@ -4336,13 +4470,18 @@ SCANTRONFORM my $i=0; foreach my $resource (@resources) { $i++; - my $result=&Apache::lonnet::ssi($resource->src(), - ('submitted' =>'scantron', - 'grade_target' =>'grade', - 'grade_username'=>$uname, - 'grade_domain' =>$udom, - 'grade_courseid'=>$ENV{'request.course.id'}, - 'grade_symb' =>$resource->symb())); + my %form=('submitted' =>'scantron', + 'grade_target' =>'grade', + 'grade_username'=>$uname, + 'grade_domain' =>$udom, + 'grade_courseid'=>$ENV{'request.course.id'}, + 'grade_symb' =>$resource->symb()); + if (exists($scan_record->{'scantron.CODE'}) && + $scan_record->{'scantron.CODE'}) { + $form{'CODE'}=$scan_record->{'scantron.CODE'}; + } + my $result=&Apache::lonnet::ssi($resource->src(),%form); + } $completedstudents{$uname}={'line'=>$line}; } continue {