--- loncom/homework/grades.pm 2003/12/02 04:50:26 1.165 +++ loncom/homework/grades.pm 2004/04/29 04:47:47 1.191 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.165 2003/12/02 04:50:26 albertel Exp $ +# $Id: grades.pm,v 1.191 2004/04/29 04:47:47 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -48,6 +48,7 @@ use Apache::lonhomework; use Apache::loncoursedata; use Apache::lonmsg qw(:user_normal_msg); use Apache::Constants qw(:common); +use Apache::lonlocal; use String::Similarity; my %oldessays=(); @@ -88,10 +89,15 @@ sub getpartlist { # --- Get the symbolic name of a problem and the url sub get_symb_and_url { - my ($request) = @_; + my ($request,$silent) = @_; (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))); - if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; } + if ($symb eq '') { + if (!$silent) { + $request->print("Unable to handle ambiguous references:$url:."); + return (); + } + } return ($symb,$url); } @@ -168,7 +174,7 @@ sub showResourceInfo { my $col=3; if ($checkboxes) { $col=4; } my $result ='<table border="0">'. - '<tr><td colspan="'.$col.'"><font size="+1"><b>Current Resource: </b>'. + '<tr><td colspan="'.$col.'"><font size="+1"><b>'.&mt('Current Resource').': </b>'. $probTitle.'</font></td></tr>'."\n"; my ($partlist,$handgrade,$responseType) = &response_type($url); my %resptype = (); @@ -290,7 +296,8 @@ sub cleanRecord { $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : ''; $ENV{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob. } - return '<br /><br /><blockquote><pre>'.&keywords_highlight($answer).'</pre></blockquote>'; + $answer =~ s-\n-<br />-g; + return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>'; } return $answer; } @@ -504,7 +511,7 @@ sub verifyreceipt { my $request = shift; my $courseid = $ENV{'request.course.id'}; - my $receipt = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'. + my $receipt = &Apache::lonnet::recprefix($courseid).'-'. $ENV{'form.receipt'}; $receipt =~ s/[^\-\d]//g; my $url = $ENV{'form.url'}; @@ -519,18 +526,27 @@ sub verifyreceipt { my ($string,$contents,$matches) = ('','',0); my (undef,undef,$fullname) = &getclasslist('all','0'); - + + my $receiptparts=0; + if ($ENV{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; } + my $parts=['0']; + if ($receiptparts) { ($parts)=&response_type($url,$symb); } foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { my ($uname,$udom)=split(/\:/); - if ($receipt eq - &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) { - $contents.='<tr bgcolor="#ffffe6"><td> '."\n". - '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom. - '\')"; TARGET=_self>'.$$fullname{$_}.'</a> </td>'."\n". - '<td> '.$uname.' </td>'. - '<td> '.$udom.' </td></tr>'."\n"; - - $matches++; + foreach my $part (@$parts) { + if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) { + $contents.='<tr bgcolor="#ffffe6"><td> '."\n". + '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom. + '\')"; TARGET=_self>'.$$fullname{$_}.'</a> </td>'."\n". + '<td> '.$uname.' </td>'. + '<td> '.$udom.' </td>'; + if ($receiptparts) { + $contents.='<td> '.$part.' </td>'; + } + $contents.='</tr>'."\n"; + + $matches++; + } } } if ($matches == 0) { @@ -543,8 +559,11 @@ sub verifyreceipt { '<table border="0"><tr bgcolor="#e6ffff">'."\n". '<td><b> Fullname </b></td>'."\n". '<td><b> Username </b></td>'."\n". - '<td><b> Domain </b></td></tr>'."\n". - $contents. + '<td><b> Domain </b></td>'; + if ($receiptparts) { + $string.='<td> Problem Part </td>'; + } + $string.='</tr>'."\n".$contents. '</table></td></tr></table>'."\n"; } return $string.&show_grading_menu_form($symb,$url); @@ -736,9 +755,12 @@ LISTJAVASCRIPT if ($num_students eq 0) { $gradeTable='<br /> <font color="red">There are no students currently enrolled.</font>'; } else { + my $submissions='submissions'; + if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; } + if ($submitonly eq 'graded' ) { $submissions = 'ungraded submissions'; } $gradeTable='<br /> <font color="red">'. - 'No submissions found for this resource for any students. ('.$num_students. - ' checked for submissions)</font><br />'; + 'No '.$submissions.' found for this resource for any students. ('.$num_students. + ' students checked for '.$submissions.')</font><br />'; } } elsif ($ctr == 1) { $gradeTable =~ s/type=checkbox/type=checkbox checked/; @@ -1273,10 +1295,10 @@ sub gradeBox { my $ctr = 0; $result.='<table border="0"><tr>'."\n"; # display radio buttons in a nice table 10 across while ($ctr<=$wgt) { - $result.= '<td><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '. + $result.= '<td><nobr><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '. 'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','. $ctr.')" value="'.$ctr.'" '. - ($score eq $ctr ? 'checked':'').' /> '.$ctr."</td>\n"; + ($score eq $ctr ? 'checked':'').' /> '.$ctr."</nobr></td>\n"; $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : ''); $ctr++; } @@ -1677,12 +1699,15 @@ KEYWORDS my $toGrade.='<input type="button" value="Grade Student" '. 'onClick="javascript:checksubmit(this.form,\'Grade Student\',\'' .$counter.'\');" TARGET=_self> '."\n" if (&canmodify($usec)); - $toGrade.='</td></tr></table></td></tr></table></form>'."\n"; - $toGrade.=&show_grading_menu_form($symb,$url) - if (($ENV{'form.command'} eq 'submission') || - ($ENV{'form.command'} eq 'processGroup' && $counter == $total)); - $request = print($toGrade); + $toGrade.='</td></tr></table></td></tr></table>'."\n"; + if (($ENV{'form.command'} eq 'submission') || + ($ENV{'form.command'} eq 'processGroup' && $counter == $total)) { + $toGrade.='</form>'.&show_grading_menu_form($symb,$url) + } + $request->print($toGrade); return; + } else { + $request->print('</td></tr></table></td></tr></table>'."\n"); } # essay grading message center @@ -2246,8 +2271,14 @@ sub viewgrades { &viewgrades_js($request); my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); - my $result='<h3><font color="#339933">Manual Grading</font></h3>'; + #need to make sure we have the correct data for later EXT calls, + #thus invalidate the cache + &Apache::lonnet::devalidatecourseresdata( + $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, + $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}); + &Apache::lonnet::clear_EXT_cache_status(); + my $result='<h3><font color="#339933">'.&mt('Manual Grading').'</font></h3>'; $result.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n"; #view individual student submission form - called using Javascript viewOneStudent @@ -3030,6 +3061,14 @@ sub displayPage { my ($classlist,undef,$fullname) = &getclasslist($getsec,'1'); my ($uname,$udom) = split(/:/,$ENV{'form.student'}); my $usec=$classlist->{$ENV{'form.student'}}[5]; + + #need to make sure we have the correct data for later EXT calls, + #thus invalidate the cache + &Apache::lonnet::devalidatecourseresdata( + $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, + $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}); + &Apache::lonnet::clear_EXT_cache_status(); + if (!&canview($usec)) { $request->print('<font color="red">Unable to view requested student.('.$ENV{'form.student'}.')</font>'); $request->print(&show_grading_menu_form($symb,$url)); @@ -3201,7 +3240,7 @@ sub displaySubByDates { } if (exists $$record{"$version:resource.$partid.regrader"}) { $displaySub[2].=$$record{"$version:resource.$partid.regrader"}. - ' (<b>Part:</b> '.$partid.')'; + ' (<b>'.&mt('Part').':</b> '.$partid.')'; } } # needed because old essay regrader has not parts info @@ -3383,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 ; } @@ -3396,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; } @@ -3406,6 +3447,31 @@ sub scantron_scantab { return $result; } +sub scantron_CODElist { + my $cdom = $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; + 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) { + if ($name =~ /^error: 2 /) { next; } + $namechoice.='<option value="'.$name.'">'.$name.'</option>'; + } + $namechoice='<select name="scantron_CODElist">'.$namechoice.'</select>'; + return $namechoice; +} + +sub scantron_CODEunique { + my $result='<nobr> + <input type="radio" name="scantron_CODEunique" + value="Yes" checked="on" /> Yes + </nobr> + <nobr> + <input type="radio" name="scantron_CODEunique" + value="No" /> No + </nobr>'; + return $result; +} + sub scantron_selectphase { my ($r) = @_; my ($symb,$url)=&get_symb_and_url($r); @@ -3415,6 +3481,8 @@ sub scantron_selectphase { my $grading_menu_button=&show_grading_menu_form($symb,$url); my $file_selector=&scantron_uploads(); my $format_selector=&scantron_scantab(); + my $CODE_selector=&scantron_CODElist(); + my $CODE_unique=&scantron_CODEunique(); my $result; #FIXME allow instructor to be able to download the scantron file # and to upload it, @@ -3422,39 +3490,39 @@ 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"> <tr bgcolor="#e6ffff"> - <td> - <b>Specify file location and which Folder/Sequence to grade</b> + <td colspan="2"> + <b>Specify file and which Folder/Sequence to grade</b> </td> </tr> <tr bgcolor="#ffffe6"> - <td> - Sequence to grade: $sequence_selector - </td> + <td> Sequence to grade: </td><td> $sequence_selector </td> </tr> <tr bgcolor="#ffffe6"> - <td> - Filename of scoring office file: $file_selector - </td> + <td> Filename of scoring office file: </td><td> $file_selector </td> </tr> <tr bgcolor="#ffffe6"> - <td> - Format of data file: $format_selector - </td> + <td> Format of data file: </td><td> $format_selector </td> </tr> <tr bgcolor="#ffffe6"> + <td> Saved CODEs to validate against: </td><td> $CODE_selector</td> + </tr> + <tr bgcolor="#ffffe6"> + <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: - <input type="text" name="scantron_maxbubble" /> + <input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Redo skipped records <br /> + <input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Ignore Original Corrections </td> </tr> <tr bgcolor="#ffffe6"> - <td> + <td colspan="2"> <input type="submit" value="Validate Scantron Records" /> </td> </tr> @@ -3475,13 +3543,36 @@ SCANTRONFORM <table width="100%" border="0"> <tr bgcolor="#e6ffff"> <td> - Specify a Scantron data file to upload. + <b>Specify a Scantron data file to upload.</b> </td> </tr> <tr bgcolor="#ffffe6"> <td> SCANTRONFORM - &scantron_upload_scantron_data($r); + my $default_form_data=&defaultFormData(&get_symb_and_url($r,1)); + my $cdom= $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; + my $cnum= $ENV{'course.'.$ENV{'request.course.id'}.'.num'}; + $r->print(<<UPLOAD); + <script type="text/javascript" language="javascript"> + function checkUpload(formname) { + if (formname.upfile.value == "") { + alert("Please use the browse button to select a file from your local directory."); + return false; + } + formname.submit(); + } + </script> + + <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'> + $default_form_data + <input name='courseid' type='hidden' value='$cnum' /> + <input name='domainid' type='hidden' value='$cdom' /> + <input name='command' value='scantronupload_save' type='hidden' /> + File to upload:<input type="file" name="upfile" size="50" /> + <br /> + <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" /> + </form> +UPLOAD $r->print(<<SCANTRONFORM); </td> @@ -3491,6 +3582,40 @@ SCANTRONFORM </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"> + <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> @@ -3547,7 +3672,7 @@ sub scantron_fixup_scanline { my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_; if ($field eq 'ID') { if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) { - return ($line,1,'New value to large'); + return ($line,1,'New value too large'); } if (length($args->{'newid'}) < $$scantron_config{'IDlength'}) { $args->{'newid'}=sprintf('%-'.$$scantron_config{'IDlength'}.'s', @@ -3559,6 +3684,22 @@ sub scantron_fixup_scanline { &scan_data($scan_data,"$whichline.user", $args->{'username'}.':'.$args->{'domain'}); } + } elsif ($field eq 'CODE') { + if ($args->{'CODE'} eq 'use_unfound') { + &scan_data($scan_data,"$whichline.useCODE",'1'); + } else { + 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'}; +# if ($args->{'CODE'}=~/^\s*$/) { +# &scan_data($scan_data,"$whichline.CODE",$args->{'CODE'}); +# } + } } elsif ($field eq 'answer') { my $length=$scantron_config->{'Qlength'}; my $off=$scantron_config->{'Qoff'}; @@ -3589,18 +3730,23 @@ 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; + } } 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'}= @@ -3692,6 +3838,19 @@ sub scantron_process_corrections { 'ID',{'newid'=>$newid, 'username'=>$ENV{'form.scantron_username'}, 'domain'=>$ENV{'form.scantron_domain'}}); + } elsif ($ENV{'form.scantron_corrections'} =~ /^(duplicate|incorrect)CODE$/) { + my $resolution=$ENV{'form.scantron_CODE_resolution'}; + my $newCODE; + 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'}; + } + ($line,$err,$errmsg)= + &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which, + 'CODE',{'CODE'=>$newCODE}); } elsif ($ENV{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) { foreach my $question (split(',',$ENV{'form.scantron_questions'})) { ($line,$err,$errmsg)= @@ -3720,6 +3879,8 @@ sub scantron_validate_file { 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; @@ -3727,7 +3888,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_optiond_redo'}" /> + <input type="hidden" name="scantron_options_ignore" value="$ENV{'form.scantron_optiond_ignore'}" /> $default_form_data SCANTRONFORM $r->print($result); @@ -3801,7 +3966,7 @@ sub scantron_getfile { } else { $scanlines{'skipped'}=[(split("\n",$lines,-1))]; } - my @tmp=&Apache::lonnet::dump('scantrondata',$cdom,$cname); + my @tmp=&Apache::lonnet::dump('nohist_scantrondata',$cdom,$cname); if ($tmp[0] =~ /^(error:|no_such_host)/) { @tmp=(); } my %scan_data = @tmp; return (\%scanlines,\%scan_data); @@ -3833,7 +3998,7 @@ sub scantron_putfile { &lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}), $prefix.'skipped_'. $ENV{'form.scantron_selectfile'}); - &Apache::lonnet::put('scantrondata',$scan_data,$cdom,$cname); + &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname); } sub scantron_get_line { @@ -3885,7 +4050,7 @@ sub scantron_validate_ID { $line,'duplicateID',$username); return(1); } - #FIXME store away line we prviously saw the ID on to use above + #FIXME store away line we previously saw the ID on to use above $found{'ids'}{$found}++; $found{'usernames'}{$username}++; } else { @@ -3932,7 +4097,7 @@ sub scantron_get_correction { $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n"); $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n"); if ($error =~ /ID$/) { - if ($error eq 'unknownID') { + if ($error eq 'incorrectID') { $r->print("The encoded ID is not in the classlist</p>\n"); } elsif ($error eq 'duplicateID') { $r->print("The encoded ID has also been used by a previous paper $arg</p>\n"); @@ -3950,9 +4115,47 @@ sub scantron_get_correction { 'scantron_username','scantron_domain')); $r->print(": <input type='text' name='scantron_username' value='' />"); $r->print("\n@". - &Apache::loncommon::select_dom_form(undef,'scantron_domain')); + &Apache::loncommon::select_dom_form($ENV{'request.role.domain'},'scantron_domain')); $r->print('</li>'); + } elsif ($error =~ /CODE$/) { + 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>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<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"); @@ -4004,6 +4207,47 @@ sub scantron_bubble_selector { 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 (!defined($ENV{'form.scantron_CODElist'})) { + &FIXME_blow_up() + } + } else { + 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 {(&Apache::lonprintout::num_to_letters($_),1)} split(',',$result{$old_name}); + + my ($scanlines,$scan_data)=&scantron_getfile(); + for (my $i=0;$i<=$scanlines->{'count'};$i++) { + my $line=&scantron_get_line($scanlines,$i); + if ($line=~/^[\s\cz]*$/) { next; } + my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config, + $scan_data); + my $CODE=$$scan_record{'scantron.CODE'}; + my $error=0; + 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'}) { + &scantron_get_correction($r,$i,$scan_record, + \%scantron_config, + $line,'duplicateCODE',$CODE); + return(1); + } + $usedCODEs{$CODE}++; + } return (0,$currentphase+1); } @@ -4030,6 +4274,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 @@ -4039,7 +4308,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); @@ -4135,11 +4404,11 @@ SCANTRONFORM 'last student'); } &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); - my $lasttime = &Time::HiRes::time()-$start; - $r->print("<p>took $lasttime</p>"); +# my $lasttime = &Time::HiRes::time()-$start; +# $r->print("<p>took $lasttime</p>"); $navmap->untieHashes(); - $r->print("<p>Done</p>"); + $r->print("</form><p>Done</p>"); $r->print(&show_grading_menu_form($symb,$url)); return ''; } @@ -4148,10 +4417,11 @@ sub scantron_upload_scantron_data { my ($r)=@_; $r->print(&Apache::loncommon::coursebrowser_javascript($ENV{'request.role.domain'})); my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid', - 'domainid'); + 'domainid', + 'coursename'); my $domsel=&Apache::loncommon::select_dom_form($ENV{'request.role.domain'}, 'domainid'); - my $default_form_data=&defaultFormData(&get_symb_and_url($r)); + my $default_form_data=&defaultFormData(&get_symb_and_url($r,1)); $r->print(<<UPLOAD); <script type="text/javascript" language="javascript"> function checkUpload(formname) { @@ -4165,12 +4435,14 @@ sub scantron_upload_scantron_data { <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'> $default_form_data -Course: <input name='courseid' type='text' /> -Domain: $domsel $select_link -<br /> +<table> +<tr><td>$select_link </td></tr> +<tr><td>Course ID: </td><td><input name='courseid' type='text' /> </td></tr> +<tr><td>Course Name: </td><td><input name='coursename' type='text' /></td></tr> +<tr><td>Domain: </td><td>$domsel </td></tr> +<tr><td>File to upload:</td><td><input type="file" name="upfile" size="50" /></td></tr> +</table> <input name='command' value='scantronupload_save' type='hidden' /> -File to upload:<input type="file" name="upfile" size="50" /> -<br /> <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" /> </form> UPLOAD @@ -4179,11 +4451,21 @@ UPLOAD sub scantron_upload_scantron_data_save { my($r)=@_; + my ($symb,$url)=&get_symb_and_url($r,1); + my $doanotherupload= + '<br /><form action="/adm/grades" method="post">'."\n". + '<input type="hidden" name="command" value="scantronupload" />'."\n". + '<input type="submit" name="submit" value="Do Another Upload" />'."\n". + '</form>'."\n"; if (!&Apache::lonnet::allowed('usc',$ENV{'form.domainid'}) && !&Apache::lonnet::allowed('usc', $ENV{'form.domainid'}.'_'.$ENV{'form.courseid'})) { $r->print("You are not allowed to upload Scantron data to the requested course.<br />"); - $r->print(&show_grading_menu_form(&get_symb_and_url($r))); + if ($symb) { + $r->print(&show_grading_menu_form($symb,$url)); + } else { + $r->print($doanotherupload); + } return ''; } $r->print("Doing upload to ".$ENV{'form.courseid'}." <br />"); @@ -4204,10 +4486,21 @@ sub scantron_upload_scantron_data_save { # See if there is anything left unless ($fname) { return 'error: no uploaded file'; } $fname='scantron_orig_'.$fname; - $r->print(&Apache::lonnet::finishuserfileupload($ENV{'form.courseid'}, - $ENV{'form.domainid'}, - $home,'upfile',$fname)); - $r->print(&show_grading_menu_form(&get_symb_and_url($r))); + if (length($ENV{'form.upfile'}) < 2) { + $r->print("<font color='red'>Error:</font> The file you attempted to upload, <tt>".&HTML::Entities::encode($ENV{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename."); + } else { + my $result=&Apache::lonnet::finishuserfileupload($ENV{'form.courseid'},$ENV{'form.domainid'},$home,'upfile',$fname); + if ($result =~ m|^/uploaded/|) { + $r->print("<font color='green'>Success:</font> Successfully uploaded ".(length($ENV{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>"); + } else { + $r->print("<font color='red'>Error:</font> An error (".$result.") occured when attempting to upload the file, <tt>".&HTML::Entities::encode($ENV{'form.upfile.filename'},'<>&"')."</tt>"); + } + } + if ($symb) { + $r->print(&show_grading_menu_form($symb,$url)); + } else { + $r->print($doanotherupload); + } return ''; } @@ -4314,7 +4607,7 @@ GRADINGMENUJS $result.='<table width="100%" border=0>'; $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n". - ' Select Section: <select name="section">'."\n"; + ' '.&mt('Select Section').': <select name="section">'."\n"; if (ref($sections)) { foreach (sort (@$sections)) { $result.='<option value="'.$_.'" '. @@ -4323,7 +4616,7 @@ GRADINGMENUJS } $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> '; - $result.='Student Status:</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef); + $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef); if (ref($sections) && (grep /no/,@$sections)) { $result.=' (Section "no" implies the students were not assigned a section.)<br />'; @@ -4332,8 +4625,8 @@ GRADINGMENUJS $result.='<tr bgcolor="#ffffe6"valign="top"><td>'. '<input type="radio" name="radioChoice" value="submission" '. - ($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>Current Resource:</b> For one or more students '. - '<select name="submitonly">'. + ($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students'). + ' <select name="submitonly">'. '<option value="yes" '. ($saveSub eq 'yes' ? 'selected="on"' : '').'>with submissions</option>'. '<option value="graded" '. @@ -4361,17 +4654,18 @@ GRADINGMENUJS $result.='<table width="100%" border=0>'; $result.='<tr bgcolor="#ffffe6"><td>'. - '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="Upload" />'. - ' scores from file </td></tr>'."\n"; + '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'. + ' '.&mt('scores from file').' </td></tr>'."\n"; $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'. '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'. - '" value="Grade" /> scantron forms</td></tr>'."\n"; + '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n"; if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb)) { $result.='<tr bgcolor="#ffffe6"valign="top"><td>'. - '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="Verify" />'. - ' submission Receipt no: '.unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}). + '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="'.&mt('Verify').'" />'. + ' '.&mt('receipt').': '. + &Apache::lonnet::recprefix($ENV{'request.course.id'}). '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'. '</td></tr>'."\n"; }