--- loncom/homework/grades.pm 2004/09/27 20:59:21 1.216 +++ loncom/homework/grades.pm 2005/02/17 08:58:16 1.247 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.216 2004/09/27 20:59:21 www Exp $ +# $Id: grades.pm,v 1.247 2005/02/17 08:58:16 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,7 +25,6 @@ # # http://www.lon-capa.org/ # -### package Apache::grades; use strict; @@ -92,25 +91,6 @@ sub get_symb_and_url { return ($symb,$url); } -# --- Retrieve the fullname for a user. Return lastname, first middle --- -# --- Generation is attached next to the lastname if it exists. --- -sub get_fullname { - my ($uname,$udom) = @_; - my %name=&Apache::lonnet::get('environment', ['lastname','generation', - 'firstname','middlename'], - $udom,$uname); - my $fullname; - my ($tmp) = keys(%name); - if ($tmp !~ /^(con_lost|error|no_such_host)/i) { - $fullname = &Apache::loncoursedata::ProcessFullName - (@name{qw/lastname generation firstname middlename/}); - } else { - &Apache::lonnet::logthis('grades.pm: no name data for '.$uname. - '@'.$udom.':'.$tmp); - } - return $fullname; -} - #--- Format fullname, username:domain if different for display #--- Use anywhere where the student names are listed sub nameUserString { @@ -992,7 +972,8 @@ sub sub_page_kw_js { my $request = shift; my $iconpath = $request->dir_config('lonIconsURL'); &commonJSfunctions($request); - my $nothing= &Apache::lonhtmlcommon::javascript_nothing(); + my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); + $docopen=~s/^document\.//; $request->print(<<SUBJAVASCRIPT); <script type="text/javascript" language="javascript"> @@ -1105,7 +1086,7 @@ sub sub_page_kw_js { 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($nothing,'replace'); + pDoc.$docopen; pDoc.write("<html><head>"); pDoc.write("<title>Message Central</title>"); @@ -1236,7 +1217,7 @@ sub sub_page_kw_js { 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($nothing,'replace'); + hDoc.$docopen; hDoc.write("<html><head>"); hDoc.write("<title>Highlight Central</title>"); @@ -1405,14 +1386,15 @@ sub submission { my ($uname,$udom) = ($ENV{'form.student'},$ENV{'form.userdom'}); $udom = ($udom eq '' ? $ENV{'user.domain'} : $udom); #has form.userdom changed for a student? my $usec = &Apache::lonnet::getsection($udom,$uname,$ENV{'request.course.id'}); - $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq ''; + $ENV{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $ENV{'form.fullname'} eq ''; 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 (!&canview($usec)) { $request->print('<font color="red">Unable to view requested student.('. - $uname.$udom.$usec.$ENV{'request.course.id'}.')</font>'); + $uname.'@'.$udom.' in section '.$usec.' in course id '. + $ENV{'request.course.id'}.')</font>'); $request->print(&show_grading_menu_form($symb,$url)); return; } @@ -1689,10 +1671,26 @@ KEYWORDS $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '. $display_part.' <font color="#999999">( ID '.$respid. ' )</font> '; + my @files; + if ($record{"resource.$partid.$respid.portfiles"}) { + my $file_url = '/uploaded/'.$udom.'/'.$uname.'/portfolio'; + foreach my $file (split(',',$record{"resource.$partid.$respid.portfiles"})) { + push(@files,$file_url.$file); + + &Apache::lonnet::logthis("found a portfolio file".$record{"resource.$partid.$respid.portfiles"}); + &Apache::lonnet::logthis("uploaded URL file".$record{"resource.$partid.$respid.uploadedurl"}); + } + } if ($record{"resource.$partid.$respid.uploadedurl"}) { - &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 />'; + push(@files,$record{"resource.$partid.$respid.uploadedurl"}); + } + if (@files) { + $lastsubonly.='<br /><font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />'; + foreach my $file (@files) { + &Apache::lonnet::allowuploaded('/adm/grades',$file); + $lastsubonly.='<br /><a href="'.$file.'" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>'; + } + $lastsubonly.='<br />'; } $lastsubonly.='<b>Submitted Answer: </b>'. &cleanRecord($subval,$responsetype,$symb,$partid, @@ -2406,9 +2404,6 @@ sub viewgrades { my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1'); my $ctr = 0; foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { - my $uname = $_; - $uname=~s/:/_/; - $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n"; $ctr++; $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'}, $_,$$fullname{$_},\@parts,\%weight,$ctr); @@ -2432,18 +2427,21 @@ sub viewstudentgrade { my ($uname,$udom) = split(/:/,$student); $student=~s/:/_/; my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname); - my $result='<tr bgcolor="#ffffdd"><td align="right">'.$ctr.' </td><td> '. + my $result='<tr bgcolor="#ffffdd"><td align="right">'. + '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'. + "\n".$ctr.' </td><td> '. '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom. '\')"; TARGET=_self>'.$fullname.'</a> '. '<font color="#999999">('.$uname.($ENV{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n"; foreach my $apart (@$parts) { my ($part,$type) = &split_part_type($apart); my $score=$record{"resource.$part.$type"}; + $result.='<td align="middle">'; if ($type eq 'awarded') { my $pts = $score eq '' ? '' : $score*$$weight{$part}; $result.='<input type="hidden" name="'. 'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n"; - $result.='<td align="middle"><input type="text" name="'. + $result.='<input type="text" name="'. 'GD_'.$student.'_'.$part.'_awarded" '. 'onChange="javascript:changeSelect(\''.$part.'\',\''.$student. '\')" value="'.$pts.'" size="4" /></td>'."\n"; @@ -2452,7 +2450,7 @@ sub viewstudentgrade { $status = 'nothing' if ($status eq ''); $result.='<input type="hidden" name="'.'GD_'.$student.'_'. $part.'_solved_s" value="'.$status.'" />'."\n"; - $result.='<td align="middle"> <select name="'. + $result.=' <select name="'. 'GD_'.$student.'_'.$part.'_solved" '. 'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n"; $result.= (($status eq 'excused') ? '<option> </option><option selected="on">excused</option>' @@ -2463,7 +2461,7 @@ sub viewstudentgrade { $result.='<input type="hidden" name="'. 'GD_'.$student.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'. "\n"; - $result.='<td align="middle"><input type="text" name="'. + $result.='<input type="text" name="'. 'GD_'.$student.'_'.$part.'_'.$type.'" '. 'value="'.$score.'" size="4" /></td>'."\n"; } @@ -2653,24 +2651,26 @@ sub split_part_type { # #--- Javascript to handle csv upload sub csvupload_javascript_reverse_associate { + my $error1=&mt('You need to specify the username or ID'); + my $error2=&mt('You need to specify at least one grading field'); return(<<ENDPICK); function verify(vf) { var foundsomething=0; var founduname=0; - var founddomain=0; + var foundID=0; for (i=0;i<=vf.nfields.value;i++) { tw=eval('vf.f'+i+'.selectedIndex'); - if (i==0 && tw!=0) { founduname=1; } - if (i==1 && tw!=0) { founddomain=1; } - if (i!=0 && i!=1 && tw!=0) { foundsomething=1; } - } - if (founduname==0 || founddomain==0) { - alert('You need to specify at both the username and domain'); - return; + if (i==0 && tw!=0) { foundID=1; } + if (i==1 && tw!=0) { founduname=1; } + if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; } + } + if (founduname==0 && foundID==0) { + alert('$error1'); + return; } if (foundsomething==0) { - alert('You need to specify at least one grading field'); - return; + alert('$error2'); + return; } vf.submit(); } @@ -2691,24 +2691,26 @@ ENDPICK } sub csvupload_javascript_forward_associate { + my $error1=&mt('You need to specify the username or ID'); + my $error2=&mt('You need to specify at least one grading field'); return(<<ENDPICK); function verify(vf) { var foundsomething=0; var founduname=0; - var founddomain=0; + var foundID=0; for (i=0;i<=vf.nfields.value;i++) { tw=eval('vf.f'+i+'.selectedIndex'); - if (tw==1) { founduname=1; } - if (tw==2) { founddomain=1; } - if (tw>2) { foundsomething=1; } - } - if (founduname==0 || founddomain==0) { - alert('You need to specify at both the username and domain'); - return; + if (tw==1) { foundID=1; } + if (tw==2) { founduname=1; } + if (tw>3) { foundsomething=1; } + } + if (founduname==0 && foundID==0) { + alert('$error1'); + return; } if (foundsomething==0) { - alert('You need to specify at least one grading field'); - return; + alert('$error2'); + return; } vf.submit(); } @@ -2735,7 +2737,8 @@ sub csvuploadmap_header { } my ($result) = &showResourceInfo($url,$ENV{'form.probTitle'}); - + my $checked=(($ENV{'form.noFirstLine'})?' checked="checked"':''); + my $ignore=&mt('Ignore First Line'); $request->print(<<ENDPICK); <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload"> <h3><font color="#339933">Uploading Class Grades</font></h3> @@ -2746,6 +2749,7 @@ Total number of records found in file: $ Enter as many fields as you can. The system will inform you and bring you back to this page if the data selected is insufficient to run your class.<hr /> <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" /> +<label><input type="checkbox" name="noFirstLine" $checked />$ignore</label> <input type="hidden" name="associate" value="" /> <input type="hidden" name="phase" value="three" /> <input type="hidden" name="datatoken" value="$datatoken" /> @@ -2757,7 +2761,7 @@ to this page if the data selected is ins <input type="hidden" name="url" value="$url" /> <input type="hidden" name="saveState" value="$ENV{'form.saveState'}" /> <input type="hidden" name="probTitle" value="$ENV{'form.probTitle'}" /> -<input type="hidden" name="command" value="csvuploadassign" /> +<input type="hidden" name="command" value="csvuploadoptions" /> <hr /> <script type="text/javascript" language="Javascript"> $javascript @@ -2770,13 +2774,18 @@ ENDPICK sub csvupload_fields { my ($url,$symb) = @_; my (@parts) = &getpartlist($url,$symb); - my @fields=(['username','Student Username'],['domain','Student Domain']); + my @fields=(['ID','Student ID'], + ['username','Student Username'], + ['domain','Student Domain']); foreach my $part (sort(@parts)) { my @datum; my $display=&Apache::lonnet::metadata($url,$part.'.display'); my $name=$part; if (!$display) { $display = $name; } @datum=($name,$display); + if ($name=~/^stores_(.*)_awarded/) { + push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]); + } push(@fields,\@datum); } return (@fields); @@ -2817,6 +2826,7 @@ CSVFORMJS '.</b></td></tr>'."\n"; $result.='<tr bgcolor=#ffffe6><td>'."\n"; my $upfile_select=&Apache::loncommon::upfile_select_html(); + my $ignore=&mt('Ignore First Line'); $result.=<<ENDUPFORM; <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload"> <input type="hidden" name="symb" value="$symb" /> @@ -2826,7 +2836,7 @@ CSVFORMJS <input type="hidden" name="saveState" value="$ENV{'form.saveState'}" /> $upfile_select <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" /> - +<label><input type="checkbox" name="noFirstLine" />$ignore</lable> </form> ENDUPFORM $result.='</td></tr></table>'."\n"; @@ -2849,6 +2859,7 @@ sub csvuploadmap { &Apache::loncommon::load_tmp_file($request); } my @records=&Apache::loncommon::upfile_record_sep(); + if ($ENV{'form.noFirstLine'}) { shift(@records); } &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1); my ($i,$keyfields); if (@records) { @@ -2874,14 +2885,51 @@ sub csvuploadmap { return ''; } -sub csvuploadassign { +sub csvuploadoptions { my ($request)= @_; my ($symb,$url)=&get_symb_and_url($request); - if (!$symb) {return '';} - &Apache::loncommon::load_tmp_file($request); - my @gradedata = &Apache::loncommon::upfile_record_sep(); + my $checked=(($ENV{'form.noFirstLine'})?'1':'0'); + my $ignore=&mt('Ignore First Line'); + $request->print(<<ENDPICK); +<form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload"> +<h3><font color="#339933">Uploading Class Grade Options</font></h3> +<input type="hidden" name="command" value="csvuploadassign" /> +<input type="submit" value="Assign Grades" /><br /> +<p> +<label> + <input type="checkbox" name="show_full_results" /> + Show a table of all changes +</label> +</p> +<p> +<label> + <input type="checkbox" name="overwite_scores" checked="checked" /> + Overwrite any existing score +</label> +</p> +ENDPICK + my %fields=&get_fields(); + if (!defined($fields{'domain'})) { + my $domform = &Apache::loncommon::select_dom_form($ENV{'request.role.domain'},'default_domain'); + $request->print("\n<p> Users are in domain: ".$domform."</p>\n"); + } + foreach my $key (sort(keys(%ENV))) { + if ($key !~ /^form\.(.*)$/) { next; } + my $cleankey=$1; + if ($cleankey eq 'command') { next; } + $request->print('<input type="hidden" name="'.$cleankey. + '" value="'.$ENV{$key}.'" />'."\n"); + } + # FIXME do a check for any duplicated user ids... + # FIXME do a check for any invalid user ids?... + $request->print("<hr /></form>\n"); + $request->print(&show_grading_menu_form($symb,$url)); + return ''; +} + +sub get_fields { + my %fields; my @keyfields = split(/\,/,$ENV{'form.keyfields'}); - my %fields=(); for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) { if ($ENV{'form.upfile_associate'} eq 'reverse') { if ($ENV{'form.f'.$i} ne 'none') { @@ -2893,6 +2941,17 @@ sub csvuploadassign { } } } + return %fields; +} + +sub csvuploadassign { + my ($request)= @_; + my ($symb,$url)=&get_symb_and_url($request); + if (!$symb) {return '';} + &Apache::loncommon::load_tmp_file($request); + my @gradedata = &Apache::loncommon::upfile_record_sep(); + if ($ENV{'form.noFirstLine'}) { shift(@gradedata); } + my %fields=&get_fields(); $request->print('<h3>Assigning Grades</h3>'); my $courseid=$ENV{'request.course.id'}; my ($classlist) = &getclasslist('all',0); @@ -2901,12 +2960,29 @@ sub csvuploadassign { my $countdone=0; foreach my $grade (@gradedata) { my %entries=&Apache::loncommon::record_sep($grade); + my $domain; + if ($entries{$fields{'domain'}}) { + $domain=$entries{$fields{'domain'}}; + } else { + $domain=$ENV{'form.default_domain'}; + } + $domain=~s/\s//g; my $username=$entries{$fields{'username'}}; $username=~s/\s//g; - my $domain=$entries{$fields{'domain'}}; - $domain=~s/\s//g; + if (!$username) { + my $id=$entries{$fields{'ID'}}; + $id=~s/\s//g; + my %ids=&Apache::lonnet::idget($domain,$id); + $username=$ids{$id}; + } if (!exists($$classlist{"$username:$domain"})) { - push(@skipped,"$username:$domain"); + my $id=$entries{$fields{'ID'}}; + $id=~s/\s//g; + if ($id) { + push(@skipped,"$id:$domain"); + } else { + push(@skipped,"$username:$domain"); + } next; } my $usec=$classlist->{"$username:$domain"}[5]; @@ -2914,16 +2990,34 @@ sub csvuploadassign { push(@notallowed,"$username:$domain"); next; } + my %points; my %grades; foreach my $dest (keys(%fields)) { - if ($dest eq 'username' || $dest eq 'domain') { next; } - if ($entries{$fields{$dest}} eq '') { next; } - my $store_key=$dest; - $store_key=~s/^stores/resource/; - $store_key=~s/_/\./g; - $grades{$store_key}=$entries{$fields{$dest}}; + if ($dest eq 'ID' || $dest eq 'username' || + $dest eq 'domain') { next; } + if ($entries{$fields{$dest}} =~ /^\s*$/) { next; } + if ($dest=~/stores_(.*)_points/) { + my $part=$1; + my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight', + $symb,$domain,$username); + $entries{$fields{$dest}}=~s/\s//g; + my $pcr=$entries{$fields{$dest}} / $wgt; + my $award='correct_by_override'; + $grades{"resource.$part.awarded"}=$pcr; + $grades{"resource.$part.solved"}=$award; + $points{$part}=1; + } else { + if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} } + if ($dest=~/stores_(.*)_solved/) { if ($points{$1}) {next;} } + my $store_key=$dest; + $store_key=~s/^stores/resource/; + $store_key=~s/_/\./g; + $grades{$store_key}=$entries{$fields{$dest}}; + } } + if (! %grades) { push(@skipped,"$username:$domain no data to store"); } $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}"; +# &Apache::lonnet::logthis(" storing ".(join('-',%grades))); &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'}, $domain,$username); $request->print('.'); @@ -3062,7 +3156,8 @@ sub getSymbMap { my $minder = 0; # Gather every sequence that has problems. - my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, 1); + my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, + 1,0,1); for my $sequence ($navmap->getById('0.0'), @sequences) { if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) { my $title = $minder.'.'.$sequence->compTitle(); @@ -3071,8 +3166,6 @@ sub getSymbMap { $minder++; } } - - $navmap->untieHashes(); return \@titles,\%symbx; } @@ -3143,7 +3236,7 @@ sub displayPage { if($curRes == $iterator->BEGIN_MAP) { $depth++; } if($curRes == $iterator->END_MAP) { $depth--; } - if (ref($curRes) && $curRes->is_problem()) { + if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) { my $parts = $curRes->parts(); my $title = $curRes->compTitle(); my $symbx = $curRes->symb(); @@ -3205,8 +3298,6 @@ sub displayPage { $curRes = $iterator->next(); } - $navmap->untieHashes(); - $studentTable.='</td></tr></table></td></tr></table>'."\n". '<input type="button" value="Save" '. 'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'. @@ -3219,9 +3310,12 @@ sub displayPage { sub displaySubByDates { my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_; + my $isCODE=0; + if (exists($record->{'resource.CODE'})) { $isCODE=1; } my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'. '<table border="0" width="100%"><tr bgcolor="#e6ffff">'. '<td><b>Date/Time</b></td>'. + ($isCODE?'<td><b>CODE</b></td>':''). '<td><b>Submission</b></td>'. '<td><b>Status </b></td></tr>'; my ($version); @@ -3234,6 +3328,9 @@ sub displaySubByDates { for ($version=1;$version<=$$record{'version'};$version++) { my $timestamp = scalar(localtime($$record{$version.':timestamp'})); $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>'; + if ($isCODE) { + $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>'; + } my @versionKeys = split(/\:/,$$record{$version.':keys'}); my @displaySub = (); foreach my $partid (@{$parts}) { @@ -3402,8 +3499,6 @@ sub updateGradeByPage { $curRes = $iterator->next(); } - $navmap->untieHashes(); - $studentTable.='</td></tr></table></td></tr></table>'; $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'}); my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' : @@ -3493,7 +3588,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 (sort(@names)) { + foreach my $name (sort {uc($a) cmp uc($b)} @names) { if ($name =~ /^error: 2 /) { next; } $namechoice.='<option value="'.$name.'">'.$name.'</option>'; } @@ -3530,8 +3625,8 @@ sub scantron_selectphase { $result.= <<SCANTRONFORM; <table width="100%" border="0"> <tr> + <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process"> <td bgcolor="#777777"> - <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process"> <input type="hidden" name="command" value="scantron_warning" /> $default_form_data <table width="100%" border="0"> @@ -3568,8 +3663,8 @@ sub scantron_selectphase { </td> </tr> </table> - </form> - </td> + </td> + </form> </tr> SCANTRONFORM @@ -3625,8 +3720,8 @@ SCANTRONFORM } $r->print(<<SCANTRONFORM); <tr> - <td bgcolor="#777777"> - <form action='/adm/grades' name='scantron_download'> + <form action='/adm/grades' name='scantron_download'> + <td bgcolor="#777777"> <input type="hidden" name="command" value="scantron_download" /> <table width="100%" border="0"> <tr bgcolor="#e6ffff"> @@ -3643,14 +3738,13 @@ SCANTRONFORM </td> </tr> </table> - </form> - </td> + </td> + </form> </tr> SCANTRONFORM $r->print(<<SCANTRONFORM); </table> -</form> $grading_menu_button SCANTRONFORM @@ -3799,25 +3893,49 @@ sub scantron_parse_scanline { my $currentquest=substr($questions,0,$$scantron_config{'Qlength'}); substr($questions,0,$$scantron_config{'Qlength'})=''; if (length($currentquest) < $$scantron_config{'Qlength'}) { next; } - my @array=split($$scantron_config{'Qon'},$currentquest,-1); - if (length($array[0]) eq $$scantron_config{'Qlength'}) { - $record{"scantron.$questnum.answer"}=''; - if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) { - push(@{$record{"scantron.missingerror"}},$questnum); - } + if ($$scantron_config{'Qon'} eq 'letter') { + if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} || + $currentquest !~ /^[A-Z]$/) { + $record{"scantron.$questnum.answer"}=''; + if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) { + push(@{$record{"scantron.missingerror"}},$questnum); + } + } else { + $record{"scantron.$questnum.answer"}=$currentquest; + } + } elsif ($$scantron_config{'Qon'} eq 'number') { + if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} || + $currentquest !~ /^\d$/) { + $record{"scantron.$questnum.answer"}=''; + if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) { + push(@{$record{"scantron.missingerror"}},$questnum); + } + } else { + $record{"scantron.$questnum.answer"}= + $alphabet[$currentquest-1]; + } } else { - $record{"scantron.$questnum.answer"}=$alphabet[length($array[0])]; + my @array=split($$scantron_config{'Qon'},$currentquest,-1); + if (length($array[0]) eq $$scantron_config{'Qlength'}) { + $record{"scantron.$questnum.answer"}=''; + if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) { + push(@{$record{"scantron.missingerror"}},$questnum); + } + } else { + $record{"scantron.$questnum.answer"}= + $alphabet[length($array[0])]; + } + if (scalar(@array) gt 2) { + push(@{$record{'scantron.doubleerror'}},$questnum); + my @ans=@array; + my $i=length($ans[0]);shift(@ans); + while ($#ans) { + $i+=length($ans[0])+1; + $record{"scantron.$questnum.answer"}.=$alphabet[$i]; + shift(@ans); + } + } } - if (scalar(@array) gt 2) { - push(@{$record{'scantron.doubleerror'}},$questnum); - my @ans=@array; - my $i=length($ans[0]);shift(@ans); - while ($#ans) { - $i+=length($ans[0])+1; - $record{"scantron.$questnum.answer"}.=$alphabet[$i]; - shift(@ans); - } - } } $record{'scantron.maxquest'}=$questnum; return \%record; @@ -3982,14 +4100,28 @@ sub scantron_do_warning { 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); + if ( $ENV{'form.selectpage'} eq '' || + $ENV{'form.scantron_selectfile'} eq '' || + $ENV{'form.scantron_format'} eq '' ) { + $r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>"); + if ( $ENV{'form.selectpage'} eq '') { + $r->print('<p><font color="red">You have not selected a Sequence to grade</font></p>'); + } + if ( $ENV{'form.scantron_selectfile'} eq '') { + $r->print('<p><font color="red">You have not selected a file that contains the student\'s response data.</font></p>'); + } + if ( $ENV{'form.scantron_format'} eq '') { + $r->print('<p><font color="red">You have not selected a the format of the student\'s response data.</font></p>'); + } + } else { + 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>"); + } + $r->print("</form><br />".&show_grading_menu_form($symb,$url)."</body></html>"); return ''; } @@ -4000,7 +4132,7 @@ sub scantron_form_start { <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="$max_bubble'" /> + <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'}" /> @@ -4290,6 +4422,12 @@ sub scantron_get_correction { $r->print(" in scanline $i <pre>". $line."</pre> \n"); } + my $message="<p>The ID on the form is <tt>". + $$scan_record{'scantron.ID'}."</tt><br />\n". + "The name on the paper is ". + $$scan_record{'scantron.LastName'}.",". + $$scan_record{'scantron.FirstName'}."</p>"; + $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$/) { @@ -4298,11 +4436,7 @@ sub scantron_get_correction { } elsif ($error eq 'duplicateID') { $r->print("The encoded ID has also been used by a previous paper $arg</p>\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($message); $r->print("<p>How should I handle this? <br /> \n"); $r->print("\n<ul><li> "); #FIXME it would be nice if this sent back the user ID and @@ -4320,13 +4454,9 @@ sub scantron_get_correction { } elsif ($error eq 'duplicateCODE') { $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"); - $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>The CODE on the form is <tt>'". + $$scan_record{'scantron.CODE'}."'</tt><br />\n"); + $r->print($message); $r->print("<p>How should I handle this? <br /> \n"); $r->print("\n<br /> "); my $i=0; @@ -4369,6 +4499,7 @@ ENDSCRIPT $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}).'" />'); + $r->print($message); $r->print("<p>Please indicate which bubble should be used for grading</p>"); foreach my $question (@{$arg}) { my $selected=$$scan_record{"scantron.$question.answer"}; @@ -4376,6 +4507,7 @@ ENDSCRIPT } } elsif ($error eq 'missingbubble') { $r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n"); + $r->print($message); $r->print("<p>Please indicate which bubble should be used for grading</p>"); $r->print("Some questions have no scanned bubbles\n"); $r->print('<input type="hidden" name="scantron_questions" value="'. @@ -4467,7 +4599,14 @@ sub scantron_validate_CODE { $scan_data); my $CODE=$$scan_record{'scantron.CODE'}; my $error=0; - if (!exists($allcodes{$CODE}) && !$$scan_record{'scantron.useCODE'}) { + if (!&Apache::lonnet::validCODE($CODE)) { + &scantron_get_correction($r,$i,$scan_record, + \%scantron_config, + $line,'incorrectCODE',\%allcodes); + return(1,$currentphase); + } + if (%allcodes && !exists($allcodes{$CODE}) + && !$$scan_record{'scantron.useCODE'}) { &scantron_get_correction($r,$i,$scan_record, \%scantron_config, $line,'incorrectCODE',\%allcodes); @@ -4522,7 +4661,7 @@ sub scantron_get_maxbubble { 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()); + my $result=&Apache::lonnet::ssi($resource->src().'?symb='.&Apache::lonnet::escape($resource->symb())); } &Apache::lonnet::delenv('scantron\.'); my $envfile=$ENV{'user.environment'}; @@ -4639,8 +4778,14 @@ SCANTRONFORM if (exists($scan_record->{'scantron.CODE'}) && $scan_record->{'scantron.CODE'}) { $form{'CODE'}=$scan_record->{'scantron.CODE'}; + } else { + $form{'CODE'}=''; } my $result=&Apache::lonnet::ssi($resource->src(),%form); + if ($result ne '') { + &Apache::lonnet::logthis("scantron grading error -> $result"); + &Apache::lonnet::logthis("scantron grading error info name $uname domain $udom course $ENV{'request.course.id'} url ".$resource->src()); + } if (&Apache::loncommon::connection_aborted($r)) { last; } } $completedstudents{$uname}={'line'=>$line}; @@ -4653,7 +4798,6 @@ SCANTRONFORM # my $lasttime = &Time::HiRes::time()-$start; # $r->print("<p>took $lasttime</p>"); - $navmap->untieHashes(); $r->print("</form>"); $r->print(&show_grading_menu_form($symb,$url)); return ''; @@ -4801,7 +4945,6 @@ DOWNLOAD # #------------------------------------------------------------------- - #-------------------------- Menu interface ------------------------- # #--- Show a Grading Menu button - Calls the next routine --- @@ -4854,6 +4997,7 @@ sub gradingmenu { if (!checkReceiptNo(formname,'notOK')) { return false;} formname.submit(); } + if (val < 7) formname.submit(); } function checkReceiptNo(formname,nospace) { @@ -4906,7 +5050,7 @@ GRADINGMENUJS ($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n"; } } - $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> '; + $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</option></select> '; $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef); @@ -4958,6 +5102,9 @@ GRADINGMENUJS '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'. '</td></tr>'."\n"; } + $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'. + '<input type="button" onClick="javascript:this.form.action=\'/adm/helper/resettimes.helper\';this.form.submit();'. + '" value="'.&mt('Manage').'" /> access times.</td></tr>'."\n"; $result.='</form></td></tr></table>'."\n". '</td></tr></table>'."\n". @@ -5057,9 +5204,9 @@ sub handler { $request->print(&csvupload($request)); } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) { $request->print(&csvuploadmap($request)); - } elsif ($command eq 'csvuploadassign' && $perm{'mgr'}) { + } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) { if ($ENV{'form.associate'} ne 'Reverse Association') { - $request->print(&csvuploadassign($request)); + $request->print(&csvuploadoptions($request)); } else { if ( $ENV{'form.upfile_associate'} ne 'reverse' ) { $ENV{'form.upfile_associate'} = 'reverse'; @@ -5068,6 +5215,8 @@ sub handler { } $request->print(&csvuploadmap($request)); } + } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) { + $request->print(&csvuploadassign($request)); } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) { $request->print(&scantron_selectphase($request)); } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {