--- loncom/homework/grades.pm 2014/01/29 16:31:20 1.715 +++ loncom/homework/grades.pm 2017/05/19 16:40:50 1.739 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.715 2014/01/29 16:31:20 bisitz Exp $ +# $Id: grades.pm,v 1.739 2017/05/19 16:40:50 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -330,6 +330,8 @@ sub cleanRecord { my $grayFont = '<span class="LC_internal_info">'; if ($response =~ /^(option|rank)$/) { my %answer=&Apache::lonnet::str2hash($answer); + my @answer = %answer; + %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer; my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); my ($toprow,$bottomrow); foreach my $foil (@$order) { @@ -346,6 +348,8 @@ sub cleanRecord { $bottomrow.'</tr></table></blockquote>'; } elsif ($response eq 'match') { my %answer=&Apache::lonnet::str2hash($answer); + my @answer = %answer; + %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer; my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"}); my ($toprow,$middlerow,$bottomrow); @@ -368,6 +372,8 @@ sub cleanRecord { $bottomrow.'</tr></table></blockquote>'; } elsif ($response eq 'radiobutton') { my %answer=&Apache::lonnet::str2hash($answer); + my @answer = %answer; + %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer; my ($toprow,$bottomrow); my $correct = &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed); @@ -400,10 +406,11 @@ 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. } - $answer =~ s-\n-<br />-g; return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>'; + } elsif ( $response eq 'organic') { - my $result='Smile representation: "<tt>'.$answer.'</tt>"'; + my $result=&mt('Smile representation: [_1]', + '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"'); my $jme=$record->{$version."resource.$partid.$respid.molecule"}; $result.=&Apache::chemresponse::jme_img($jme,$answer,400); return $result; @@ -437,12 +444,14 @@ sub cleanRecord { $result.='</ul>'; return $result; } - } elsif ( $response =~ m/(?:numerical|formula)/) { + } elsif ( $response =~ m/(?:numerical|formula|custom)/) { + # Respect multiple input fields, see Bug #5409 $answer = &Apache::loncommon::format_previous_attempt_value('submission', $answer); + return $answer; } - return $answer; + return &HTML::Entities::encode($answer, '"<>&'); } #-- A couple of common js functions @@ -848,10 +857,11 @@ sub listStudents { my $res_error; my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error); - my %lt = &Apache::lonlocal::texthash ( + my %js_lt = &Apache::lonlocal::texthash ( 'multiple' => 'Please select a student or group of students before clicking on the Next button.', 'single' => 'Please select the student before clicking on the Next button.', ); + &js_escape(\%js_lt); $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT)); function checkSelect(checkBox) { var ctr=0; @@ -862,12 +872,12 @@ sub listStudents { ctr++; } } - sense = '$lt{'multiple'}'; + sense = '$js_lt{'multiple'}'; } else { if (checkBox.checked) { ctr = 1; } - sense = '$lt{'single'}'; + sense = '$js_lt{'single'}'; } if (ctr == 0) { alert(sense); @@ -1168,7 +1178,8 @@ sub processGroup { #--- Javascript to handle the submission page functionality --- sub sub_page_js { my $request = shift; - my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); + my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); + &js_escape(\$alertmsg); $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT)); function updateRadio(formname,id,weight) { var gradeBox = formname["GD_BOX"+id]; @@ -1286,10 +1297,8 @@ sub sub_page_js { } } } - } } - } formname.submit(); } @@ -1415,10 +1424,21 @@ INNERJS my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); $docopen=~s/^document\.//; - my %lt = &Apache::lonlocal::texthash( + my %js_lt = &Apache::lonlocal::texthash( keyw => 'Keywords list, separated by a space. Add/delete to list if desired.', plse => 'Please select a word or group of words from document and then click this link.', adds => 'Add selection to keyword list? Edit if desired.', + col1 => 'red', + col2 => 'green', + col3 => 'blue', + siz1 => 'normal', + siz2 => '+1', + siz3 => '+2', + sty1 => 'normal', + sty2 => 'italic', + sty3 => 'bold', + ); + my %html_js_lt = &Apache::lonlocal::texthash( comp => 'Compose Message for: ', incl => 'Include', type => 'Type', @@ -1432,11 +1452,14 @@ INNERJS font => 'Font Size', fnst => 'Font Style', ); + &js_escape(\%js_lt); + &html_escape(\%html_js_lt); + &js_escape(\%html_js_lt); $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT)); //===================== Show list of keywords ==================== function keywords(formname) { - var nret = prompt("$lt{'keyw'}",formname.keywords.value); + var nret = prompt("$js_lt{'keyw'}",formname.keywords.value); if (nret==null) return; formname.keywords.value = nret; @@ -1463,10 +1486,10 @@ INNERJS else return; var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," "); if (cleantxt=="") { - alert("$lt{'plse'}"); + alert("$js_lt{'plse'}"); return; } - var nret = prompt("$lt{'adds'}",cleantxt); + var nret = prompt("$js_lt{'adds'}",cleantxt); if (nret==null) return; document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret; if (document.SCORE.keywords.value != "") { @@ -1546,16 +1569,16 @@ INNERJS pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">"); pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">"); - pDoc.write("<h1> $lt{'comp'}\"+fullname+\"<\\/h1>"); + pDoc.write("<h1> $html_js_lt{'comp'}\"+fullname+\"<\\/h1>"); pDoc.write('<table style="border:1px solid black;"><tr>'); - pDoc.write("<td><b>$lt{'incl'}<\\/b><\\/td><td><b>$lt{'type'}<\\/b><\\/td><td><b>$lt{'mesa'}<\\/td><\\/tr>"); + pDoc.write("<td><b>$html_js_lt{'incl'}<\\/b><\\/td><td><b>$html_js_lt{'type'}<\\/b><\\/td><td><b>$html_js_lt{'mesa'}<\\/td><\\/tr>"); } function displaySubject(msg,shwsel) { pDoc = pWin.document; pDoc.write("<tr>"); pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>"); - pDoc.write("<td>$lt{'subj'}<\\/td>"); + pDoc.write("<td>$html_js_lt{'subj'}<\\/td>"); pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"40\\" maxlength=\\"80\\"><\\/td><\\/tr>"); } @@ -1571,7 +1594,7 @@ INNERJS pDoc = pWin.document; pDoc.write("<tr>"); pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>"); - pDoc.write("<td align=\\"center\\">$lt{'new'}<\\/td>"); + pDoc.write("<td align=\\"center\\">$html_js_lt{'new'}<\\/td>"); pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"<\\/textarea><\\/td><\\/tr>"); } @@ -1579,8 +1602,8 @@ INNERJS pDoc = pWin.document; //pDoc.write("<\\/table>"); pDoc.write("<\\/td><\\/tr><\\/table> "); - pDoc.write("<input type=\\"button\\" value=\\"$lt{'save'}\\" onclick=\\"javascript:checkInput()\\"> "); - pDoc.write("<input type=\\"button\\" value=\\"$lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />"); + pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'save'}\\" onclick=\\"javascript:checkInput()\\"> "); + pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />"); pDoc.write("<\\/form>"); pDoc.write('$end_page_msg_central'); pDoc.close(); @@ -1594,25 +1617,34 @@ INNERJS var redsel = ""; var grnsel = ""; var blusel = ""; - if (kwclr=="red") {var redsel="checked"}; - if (kwclr=="green") {var grnsel="checked"}; - if (kwclr=="blue") {var blusel="checked"}; + var txtcol1 = "$js_lt{'col1'}"; + var txtcol2 = "$js_lt{'col2'}"; + var txtcol3 = "$js_lt{'col3'}"; + var txtsiz1 = "$js_lt{'siz1'}"; + var txtsiz2 = "$js_lt{'siz2'}"; + var txtsiz3 = "$js_lt{'siz3'}"; + var txtsty1 = "$js_lt{'sty1'}"; + var txtsty2 = "$js_lt{'sty2'}"; + var txtsty3 = "$js_lt{'sty3'}"; + if (kwclr=="red") {var redsel="checked='checked'"}; + if (kwclr=="green") {var grnsel="checked='checked'"}; + if (kwclr=="blue") {var blusel="checked='checked'"}; var sznsel = ""; var sz1sel = ""; var sz2sel = ""; - if (kwsize=="0") {var sznsel="checked"}; - if (kwsize=="+1") {var sz1sel="checked"}; - if (kwsize=="+2") {var sz2sel="checked"}; + if (kwsize=="0") {var sznsel="checked='checked'"}; + if (kwsize=="+1") {var sz1sel="checked='checked'"}; + if (kwsize=="+2") {var sz2sel="checked='checked'"}; var synsel = ""; var syisel = ""; var sybsel = ""; - if (kwstyle=="") {var synsel="checked"}; - if (kwstyle=="<i>") {var syisel="checked"}; - if (kwstyle=="<b>") {var sybsel="checked"}; + if (kwstyle=="") {var synsel="checked='checked'"}; + if (kwstyle=="<i>") {var syisel="checked='checked'"}; + if (kwstyle=="<b>") {var sybsel="checked='checked'"}; highlightCentral(); - highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel); - highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel); - highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel); + highlightbody('red',txtcol1,redsel,'0',txtsiz1,sznsel,'',txtsty1,synsel); + highlightbody('green',txtcol2,grnsel,'+1',txtsiz2,sz1sel,'<i>',txtsty2,syisel); + highlightbody('blue',txtcol3,blusel,'+2',txtsiz3,sz2sel,'<b>',txtsty3,sybsel); highlightend(); return; } @@ -1630,31 +1662,29 @@ INNERJS hDoc.$docopen; hDoc.write('$start_page_highlight_central'); hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">"); - hDoc.write("<h3><span class=\\"LC_info\\"> $lt{'kehi'}<\\/span><\\/h3><br /><br />"); + hDoc.write("<h1>$html_js_lt{'kehi'}<\\/h1>"); - hDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">'); - hDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">'); - hDoc.write("<td><b>$lt{'txtc'}<\\/b><\\/td><td><b>$lt{'font'}<\\/b><\\/td><td><b>$lt{'fnst'}<\\/td><\\/tr>"); + hDoc.write('<table border="0" width="100%"><tr style="background-color:#A1D676">'); + hDoc.write("<th>$html_js_lt{'txtc'}<\\/th><th>$html_js_lt{'font'}<\\/th><th>$html_js_lt{'fnst'}<\\/th><\\/tr>"); } function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { var hDoc = hwdWin.document; - hDoc.write("<tr bgcolor=\\"#ffffdd\\">"); + hDoc.write("<tr>"); hDoc.write("<td align=\\"left\\">"); - hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+"> "+clrtxt+"<\\/td>"); + hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+" \\/> "+clrtxt+"<\\/td>"); hDoc.write("<td align=\\"left\\">"); - hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+"> "+sztxt+"<\\/td>"); + hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+" \\/> "+sztxt+"<\\/td>"); hDoc.write("<td align=\\"left\\">"); - hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+"> "+sytxt+"<\\/td>"); + hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+" \\/> "+sytxt+"<\\/td>"); hDoc.write("<\\/tr>"); } function highlightend() { var hDoc = hwdWin.document; - hDoc.write("<\\/table>"); - hDoc.write("<\\/td><\\/tr><\\/table> "); - hDoc.write("<input type=\\"button\\" value=\\"$lt{'save'}\\" onclick=\\"javascript:updateChoice(1)\\"> "); - hDoc.write("<input type=\\"button\\" value=\\"$lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />"); + hDoc.write("<\\/table><br \\/>"); + hDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'save'}\\" onclick=\\"javascript:updateChoice(1)\\" \\/> "); + hDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'canc'}\\" onclick=\\"self.close()\\" \\/><br /><br />"); hDoc.write("<\\/form>"); hDoc.write('$end_page_highlight_central'); hDoc.close(); @@ -1788,7 +1818,7 @@ sub handback_box { if ($file =~ /\/portfolio\//) { $file_counter++; my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|); - my ($name,$version,$ext) = &file_name_version_ext($file_disp); + my ($name,$version,$ext) = &Apache::lonnet::file_name_version_ext($file_disp); $file_disp = "$name.$ext"; $file = $file_path.$file_disp; $result.=&mt('Return commented version of [_1] to student.', @@ -2044,6 +2074,7 @@ sub submission { if (1) { my %lt = &Apache::lonlocal::texthash( + keyh => 'Keyword Highlighting for Essays', keyw => 'Keyword Options', list => 'List', past => 'Paste Selection to List', @@ -2052,13 +2083,18 @@ sub submission { # # Print out the keyword options line # - $request->print(<<KEYWORDS); -<br /><b>$lt{'keyw'}:</b> -<a href="javascript:keywords(document.SCORE);" target="_self">$lt{'list'}</a> -<a href="#" onmousedown="javascript:getSel(); return false" - class="page">$lt{'past'}</a> -<a href="javascript:kwhighlight();" target="_self">$lt{'high'}</a><br /><br /> -KEYWORDS + $request->print( + '<div class="LC_columnSection">' + .'<fieldset><legend>'.$lt{'keyh'}.'</legend>' + .&Apache::lonhtmlcommon::funclist_from_array( + ['<a href="javascript:keywords(document.SCORE);" target="_self">'.$lt{'list'}.'</a>', + '<a href="#" onmousedown="javascript:getSel(); return false" + class="page">'.$lt{'past'}.'</a>', + '<a href="javascript:kwhighlight();" target="_self">'.$lt{'high'}.'</a>'], + {legend => $lt{'keyw'}}) + .'</fieldset></div>' + ); + # # Load the other essays for similarity check # @@ -2195,7 +2231,7 @@ KEYWORDS foreach my $submission (@$string) { my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/); if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; } - my ($ressub,$hide,$subval) = split(/:/,$submission,3); + my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4); # Similarity check my $similar=''; my ($type,$trial,$rndseed); @@ -2264,9 +2300,17 @@ KEYWORDS if ($hide eq 'anon') { $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; } else { - $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>'. + $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>'; + if ($draft) { + $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>'; + } + $subval = &cleanRecord($subval,$responsetype,$symb,$partid, $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed); + if ($responsetype eq 'essay') { + $subval =~ s{\n}{<br />}g; + } + $lastsubonly.=$subval."\n"; } if ($similar) {$lastsubonly.="<br /><br />$similar\n";} $lastsubonly.='</div>'; @@ -2279,12 +2323,15 @@ KEYWORDS if ($env{'form.lastSub'} eq 'datesub') { my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error); $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom)); + } if ($env{'form.lastSub'} =~ /^(last|all)$/) { + my $identifier = (&canmodify($usec)? $counter : ''); $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom, $env{'request.course.id'}, $last,'.submission', - 'Apache::grades::keywords_highlight')); + 'Apache::grades::keywords_highlight', + $usec,$identifier)); } $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':' .$udom.'" />'."\n"); @@ -2503,7 +2550,7 @@ sub get_last_submission { } unless ($hide) { if (@randomize) { - foreach my $id (@hidden) { + foreach my $id (@randomize) { if ($key =~ /^\Q$id\E/) { $hide = 'rand'; last; @@ -2512,9 +2559,10 @@ sub get_last_submission { } } my ($partid,$foo) = split(/submission$/,$key); - my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? - '<span class="LC_warning">Draft Copy</span> ' : ''; - push(@string, join(':', $key, $hide, $draft.$lasthash{$key})); + my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? 1 : 0; + push(@string, join(':', $key, $hide, $draft, ( + ref($lasthash{$key}) eq 'ARRAY' ? + join(',', @{$lasthash{$key}}) : $lasthash{$key}) )); } } if (!@string) { @@ -2542,8 +2590,10 @@ sub keywords_highlight { sub show_previous_task_version { my ($request,$symb) = @_; if ($symb eq '') { - $request->print("Unable to handle ambiguous references."); - + $request->print( + '<span class="LC_error">'. + &mt('Unable to handle ambiguous references.'). + '</span>'); return ''; } my ($uname,$udom) = ($env{'form.student'},$env{'form.userdom'}); @@ -2731,16 +2781,26 @@ sub processHandGrade { my $ctr = 0; while ($ctr < $ngrade) { my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr}); - my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$symb,$uname,$udom,$ctr); + my ($errorflag,$pts,$wgt,$numhidden) = + &saveHandGrade($request,$symb,$uname,$udom,$ctr); if ($errorflag eq 'no_score') { $ctr++; next; } if ($errorflag eq 'not_allowed') { - $request->print("<span class=\"LC_warning\">Not allowed to modify grades for $uname:$udom</span>"); + $request->print( + '<span class="LC_error">' + .&mt('Not allowed to modify grades for [_1]',"$uname:$udom") + .'</span>'); $ctr++; next; } + if ($numhidden) { + $request->print( + '<span class="LC_info">' + .&mt('For [_1]: [quant,_2,transaction] hidden',"$uname:$udom",$numhidden) + .'</span><br />'); + } my $includemsg = $env{'form.includemsg'.$ctr}; my ($subject,$message,$msgstatus) = ('','',''); my $restitle = &Apache::lonnet::gettitle($symb); @@ -2962,9 +3022,14 @@ sub saveHandGrade { my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname); my @parts_graded; my %newrecord = (); - my ($pts,$wgt) = ('',''); + my ($pts,$wgt,$totchg) = ('','',0); my %aggregate = (); my $aggregateflag = 0; + if ($env{'form.HIDE'.$newflg}) { + my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2); + my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1); + $totchg += $numchgs; + } my @parts = split(/:/,$env{'form.partlist'.$newflg}); foreach my $new_part (@parts) { #collaborator ($submi may vary for different parts @@ -3067,7 +3132,37 @@ sub saveHandGrade { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, $cdom,$cnum); } - return ('',$pts,$wgt); + return ('',$pts,$wgt,$totchg); +} + +sub makehidden { + my ($version,$parts,$record,$symb,$domain,$stuname,$tolog) = @_; + return unless (ref($record) eq 'HASH'); + my %modified; + my $numchanged = 0; + if (exists($record->{$version.':keys'})) { + my $partsregexp = $parts; + $partsregexp =~ s/,/|/g; + foreach my $key (split(/\:/,$record->{$version.':keys'})) { + if ($key =~ /^resource\.(?:$partsregexp)\.([^\.]+)$/) { + my $item = $1; + unless (($item eq 'solved') || ($item =~ /^award(|msg|ed)$/)) { + $modified{$key} = $record->{$version.':'.$key}; + } + } elsif ($key =~ m{^(resource\.(?:$partsregexp)\.[^\.]+\.)(.+)$}) { + $modified{$1.'hidden'.$2} = $record->{$version.':'.$key}; + } elsif ($key =~ /^(ip|timestamp|host)$/) { + $modified{$key} = $record->{$version.':'.$key}; + } + } + if (keys(%modified)) { + if (&Apache::lonnet::putstore($env{'request.course.id'},$symb,$version,\%modified, + $domain,$stuname,$tolog) eq 'ok') { + $numchanged ++; + } + } + } + return $numchanged; } sub check_and_remove_from_queue { @@ -3111,13 +3206,13 @@ sub handback_files { my ($directory,$answer_file) = ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/); my ($answer_name,$answer_ver,$answer_ext) = - &file_name_version_ext($answer_file); + &Apache::lonnet::file_name_version_ext($answer_file); my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/); my $getpropath = 1; my ($dir_list,$listerror) = &Apache::lonnet::dirlist($portfolio_root.$portfolio_path, $domain,$stuname,$getpropath); - my $version = &get_next_version($answer_name,$answer_ext,$dir_list); + my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list); # fix filename my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/); my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain, @@ -3266,29 +3361,14 @@ sub version_portfiles { my $version_parts = join('|',@$v_flag); my @returned_keys; my $parts = join('|', @$parts_graded); - my $portfolio_root = '/userfiles/portfolio'; foreach my $key (keys(%$record)) { my $new_portfiles; if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) { my @versioned_portfiles; my @portfiles = split(/\s*,\s*/,$$record{$key}); - foreach my $file (@portfiles) { - &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file); - my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/); - my ($answer_name,$answer_ver,$answer_ext) = - &file_name_version_ext($answer_file); - my $getpropath = 1; - my ($dir_list,$listerror) = - &Apache::lonnet::dirlist($portfolio_root.$directory,$domain, - $stu_name,$getpropath); - my $version = &get_next_version($answer_name,$answer_ext,$dir_list); - my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version); - if ($new_answer ne 'problem getting file') { - push(@versioned_portfiles, $directory.$new_answer); - &Apache::lonnet::mark_as_readonly($domain,$stu_name, - [$directory.$new_answer], - [$symb,$env{'request.course.id'},'graded']); - } + if (@portfiles) { + &Apache::lonnet::portfiles_versioning($symb,$domain,$stu_name,\@portfiles, + \@versioned_portfiles); } $$record{$key} = join(',',@versioned_portfiles); push(@returned_keys,$key); @@ -3297,64 +3377,6 @@ sub version_portfiles { return (@returned_keys); } -sub get_next_version { - my ($answer_name, $answer_ext, $dir_list) = @_; - my $version; - if (ref($dir_list) eq 'ARRAY') { - foreach my $row (@{$dir_list}) { - my ($file) = split(/\&/,$row,2); - my ($file_name,$file_version,$file_ext) = - &file_name_version_ext($file); - if (($file_name eq $answer_name) && - ($file_ext eq $answer_ext)) { - # gets here if filename and extension match, - # regardless of version - if ($file_version ne '') { - # a versioned file is found so save it for later - if ($file_version > $version) { - $version = $file_version; - } - } - } - } - } - $version ++; - return($version); -} - -sub version_selected_portfile { - my ($domain,$stu_name,$directory,$file_name,$version) = @_; - my ($answer_name,$answer_ver,$answer_ext) = - &file_name_version_ext($file_name); - my $new_answer; - $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name"); - if($env{'form.copy'} eq '-1') { - $new_answer = 'problem getting file'; - } else { - $new_answer = $answer_name.'.'.$version.'.'.$answer_ext; - my $copy_result = &Apache::lonnet::finishuserfileupload( - $stu_name,$domain,'copy', - '/portfolio'.$directory.$new_answer); - } - return ($new_answer); -} - -sub file_name_version_ext { - my ($file)=@_; - my @file_parts = split(/\./, $file); - my ($name,$version,$ext); - if (@file_parts > 1) { - $ext=pop(@file_parts); - if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) { - $version=pop(@file_parts); - } - $name=join('.',@file_parts); - } else { - $name=join('.',@file_parts); - } - return($name,$version,$ext); -} - #-------------------------------------------------------------------------------------- # #-------------------------- Next few routines handles grading by section or whole class @@ -3364,6 +3386,7 @@ sub viewgrades_js { my ($request) = shift; my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = '); + &js_escape(\$alertmsg); $request->print(&Apache::lonhtmlcommon::scripttag(<<VIEWJAVASCRIPT)); function writePoint(partid,weight,point) { var radioButton = document.classgrade["RADVAL_"+partid]; @@ -3553,19 +3576,67 @@ sub viewgrades { &build_section_inputs(). '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n". - my ($common_header,$specific_header); - if ($env{'form.section'} eq 'all') { - $common_header = &mt('Assign Common Grade to Class'); - $specific_header = &mt('Assign Grade to Specific Students in Class'); - } elsif ($env{'form.section'} eq 'none') { - $common_header = &mt('Assign Common Grade to Students in no Section'); - $specific_header = &mt('Assign Grade to Specific Students in no Section'); - } else { - my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); - $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display); - $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display); + #retrieve selected groups + my (@groups,$group_display); + @groups = &Apache::loncommon::get_env_multiple('form.group'); + if (grep(/^all$/,@groups)) { + @groups = ('all'); + } elsif (grep(/^none$/,@groups)) { + @groups = ('none'); + } elsif (@groups > 0) { + $group_display = join(', ',@groups); + } + + my ($common_header,$specific_header,@sections,$section_display); + @sections = &Apache::loncommon::get_env_multiple('form.section'); + if (grep(/^all$/,@sections)) { + @sections = ('all'); + if ($group_display) { + $common_header = &mt('Assign Common Grade to Students in Group(s) [_1]',$group_display); + $specific_header = &mt('Assign Grade to Specific Students in Group(s) [_1]',$group_display); + } elsif (grep(/^none$/,@groups)) { + $common_header = &mt('Assign Common Grade to Students not assigned to any groups'); + $specific_header = &mt('Assign Grade to Specific Students not assigned to any groups'); + } else { + $common_header = &mt('Assign Common Grade to Class'); + $specific_header = &mt('Assign Grade to Specific Students in Class'); + } + } elsif (grep(/^none$/,@sections)) { + @sections = ('none'); + if ($group_display) { + $common_header = &mt('Assign Common Grade to Students in no Section and in Group(s) [_1]',$group_display); + $specific_header = &mt('Assign Grade to Specific Students in no Section and in Group(s)',$group_display); + } elsif (grep(/^none$/,@groups)) { + $common_header = &mt('Assign Common Grade to Students in no Section and in no Group'); + $specific_header = &mt('Assign Grade to Specific Students in no Section and in no Group'); + } else { + $common_header = &mt('Assign Common Grade to Students in no Section'); + $specific_header = &mt('Assign Grade to Specific Students in no Section'); + } + } else { + $section_display = join (", ",@sections); + if ($group_display) { + $common_header = &mt('Assign Common Grade to Students in Section(s) [_1], and in Group(s) [_2]', + $section_display,$group_display); + $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1], and in Group(s) [_2]', + $section_display,$group_display); + } elsif (grep(/^none$/,@groups)) { + $common_header = &mt('Assign Common Grade to Students in Section(s) [_1] and no Group',$section_display); + $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1] and no Group',$section_display); + } else { + $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display); + $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display); + } + } + my %submit_types = &substatus_options(); + my $submission_status = $submit_types{$env{'form.submitonly'}}; + + if ($env{'form.submitonly'} eq 'all') { + $result.= '<h3>'.$common_header.'</h3>'; + } else { + $result.= '<h3>'.$common_header.' '.&mt('(submission status: "[_1]")',$submission_status).'</h3>'; } - $result.= '<h3>'.$common_header.'</h3>'.&Apache::loncommon::start_data_table(); + $result .= &Apache::loncommon::start_data_table(); #radio buttons/text box for assigning points for a section or class. #handles different parts of a problem my $res_error; @@ -3628,8 +3699,12 @@ sub viewgrades { #table listing all the students in a section/class #header of table - $result.= '<h3>'.$specific_header.'</h3>'. - &Apache::loncommon::start_data_table(). + if ($env{'form.submitonly'} eq 'all') { + $result.= '<h3>'.$specific_header.'</h3>'; + } else { + $result.= '<h3>'.$specific_header.' '.&mt('(submission status: "[_1]")',$submission_status).'</h3>'; + } + $result.= &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). '<th>'.&mt('No.').'</th>'. '<th>'.&nameUserString('header')."</th>\n"; @@ -3675,7 +3750,7 @@ sub viewgrades { #get info for each student #list all the students - with points and grade status - my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1'); + my (undef,undef,$fullname) = &getclasslist(\@sections,'1',\@groups); my $ctr = 0; foreach (sort { @@ -3684,35 +3759,142 @@ sub viewgrades { } return $a cmp $b; } (keys(%$fullname))) { - $ctr++; $result.=&viewstudentgrade($symb,$env{'request.course.id'}, - $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets); + $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets); } $result.=&Apache::loncommon::end_data_table(); $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n"; $result.='<input type="button" value="'.&mt('Save').'" '. 'onclick="javascript:submit();" target="_self" /></form>'."\n"; - if (scalar(%$fullname) eq 0) { - my $colspan=3+scalar(@parts); - my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section')); + if ($ctr == 0) { my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status')); - $result='<span class="LC_warning">'. - &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade.', - $section_display, $stu_status). - '</span>'; + $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>'. + '<span class="LC_warning">'; + if ($env{'form.submitonly'} eq 'all') { + if (grep(/^all$/,@sections)) { + if (grep(/^all$/,@groups)) { + $result .= &mt('There are no students with enrollment status [_1] to modify or grade.', + $stu_status); + } elsif (grep(/^none$/,@groups)) { + $result .= &mt('There are no students with no group assigned and with enrollment status [_1] to modify or grade.', + $stu_status); + } else { + $result .= &mt('There are no students in group(s) [_1] with enrollment status [_2] to modify or grade.', + $group_display,$stu_status); + } + } elsif (grep(/^none$/,@sections)) { + if (grep(/^all$/,@groups)) { + $result .= &mt('There are no students in no section with enrollment status [_1] to modify or grade.', + $stu_status); + } elsif (grep(/^none$/,@groups)) { + $result .= &mt('There are no students in no section and no group with enrollment status [_1] to modify or grade.', + $stu_status); + } else { + $result .= &mt('There are no students in no section in group(s) [_1] with enrollment status [_2] to modify or grade.', + $group_display,$stu_status); + } + } else { + if (grep(/^all$/,@groups)) { + $result .= &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade.', + $section_display,$stu_status); + } elsif (grep(/^none$/,@groups)) { + $result .= &mt('There are no students in section(s) [_1] and no group with enrollment status [_2] to modify or grade.', + $section_display,$stu_status); + } else { + $result .= &mt('There are no students in section(s) [_1] and group(s) [_2] with enrollment status [_3] to modify or grade.', + $section_display,$group_display,$stu_status); + } + } + } else { + if (grep(/^all$/,@sections)) { + if (grep(/^all$/,@groups)) { + $result .= &mt('There are no students with enrollment status [_1] and submission status "[_2]" to modify or grade.', + $stu_status,$submission_status); + } elsif (grep(/^none$/,@groups)) { + $result .= &mt('There are no students with no group assigned with enrollment status [_1] and submission status "[_2]" to modify or grade.', + $stu_status,$submission_status); + } else { + $result .= &mt('There are no students in group(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.', + $group_display,$stu_status,$submission_status); + } + } elsif (grep(/^none$/,@sections)) { + if (grep(/^all$/,@groups)) { + $result .= &mt('There are no students in no section with enrollment status [_1] and submission status "[_2]" to modify or grade.', + $stu_status,$submission_status); + } elsif (grep(/^none$/,@groups)) { + $result .= &mt('There are no students in no section and no group with enrollment status [_1] and submission status "[_2]" to modify or grade.', + $stu_status,$submission_status); + } else { + $result .= &mt('There are no students in no section in group(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.', + $group_display,$stu_status,$submission_status); + } + } else { + if (grep(/^all$/,@groups)) { + $result .= &mt('There are no students in section(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.', + $section_display,$stu_status,$submission_status); + } elsif (grep(/^none$/,@groups)) { + $result .= &mt('There are no students in section(s) [_1] and no group with enrollment status [_2] and submission status "[_3]" to modify or grade.', + $section_display,$stu_status,$submission_status); + } else { + $result .= &mt('There are no students in section(s) [_1] and group(s) [_2] with enrollment status [_3] and submission status "[_4]" to modify or grade.', + $section_display,$group_display,$stu_status,$submission_status); + } + } + } + $result .= '</span><br />'; } return $result; } -#--- call by previous routine to display each student +#--- call by previous routine to display each student who satisfies submission filter. sub viewstudentgrade { my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_; my ($uname,$udom) = split(/:/,$student); my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname); - my %aggregates = (); + my $submitonly = $env{'form.submitonly'}; + unless (($submitonly eq 'all') || ($submitonly eq 'queued')) { + my %partstatus = (); + if (ref($parts) eq 'ARRAY') { + foreach my $apart (@{$parts}) { + my ($part,$type) = &split_part_type($apart); + my ($status,undef) = split(/_/,$record{"resource.$part.solved"},2); + $status = 'nothing' if ($status eq ''); + $partstatus{$part} = $status; + my $subkey = "resource.$part.submitted_by"; + $partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne ''); + } + my $submitted = 0; + my $graded = 0; + my $incorrect = 0; + foreach my $key (keys(%partstatus)) { + $submitted = 1 if ($partstatus{$key} ne 'nothing'); + $graded = 1 if ($partstatus{$key} =~ /^ungraded/); + $incorrect = 1 if ($partstatus{$key} =~ /^incorrect/); + + my $partid = (split(/\./,$key))[1]; + if ($partstatus{'resource.'.$partid.'.'.$key.'.submitted_by'} ne '') { + $submitted = 0; + } + } + return if (!$submitted && ($submitonly eq 'yes' || + $submitonly eq 'incorrect' || + $submitonly eq 'graded')); + return if (!$graded && ($submitonly eq 'graded')); + return if (!$incorrect && $submitonly eq 'incorrect'); + } + } + if ($submitonly eq 'queued') { + my ($cdom,$cnum) = split(/_/,$courseid); + my %queue_status = + &Apache::bridgetask::get_student_status($symb,$cdom,$cnum, + $udom,$uname); + return if (!defined($queue_status{'gradingqueue'})); + } + $$ctr++; + my %aggregates = (); my $result=&Apache::loncommon::start_data_table_row().'<td align="right">'. - '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'. - "\n".$ctr.' </td><td> '. + '<input type="hidden" name="ctr'.($$ctr-1).'" value="'.$student.'" />'. + "\n".$$ctr.' </td><td> '. '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom. '\');" target="_self">'.$fullname.'</a> '. '<span class="LC_internal_info">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</span></td>'."\n"; @@ -3724,7 +3906,6 @@ sub viewstudentgrade { my ($aggtries,$totaltries); unless (exists($aggregates{$part})) { $totaltries = $record{'resource.'.$part.'.tries'}; - $aggtries = $totaltries; if ($$last_resets{$part}) { $aggtries = &get_num_tries(\%record,$$last_resets{$part}, @@ -4013,6 +4194,8 @@ sub split_part_type { sub csvupload_javascript_reverse_associate { my $error1=&mt('You need to specify the username or the student/employee ID'); my $error2=&mt('You need to specify at least one grading field'); + &js_escape(\$error1); + &js_escape(\$error2); return(<<ENDPICK); function verify(vf) { var foundsomething=0; @@ -4053,6 +4236,8 @@ ENDPICK sub csvupload_javascript_forward_associate { my $error1=&mt('You need to specify the username or the student/employee ID'); my $error2=&mt('You need to specify at least one grading field'); + &js_escape(\$error1); + &js_escape(\$error2); return(<<ENDPICK); function verify(vf) { var foundsomething=0; @@ -4130,6 +4315,7 @@ sub csvupload_fields { } my @fields=(['ID','Student/Employee ID'], + ['clicker','Clicker ID'], ['username','Student Username'], ['domain','Student Domain']); my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb); @@ -4161,6 +4347,7 @@ ENDPICK sub checkforfile_js { my $alertmsg = &mt('Please use the browse button to select a file from your local directory.'); + &js_escape(\$alertmsg); my $result = &Apache::lonhtmlcommon::scripttag(<<CSVFORMJS); function checkUpload(formname) { if (formname.upfile.value == "") { @@ -4324,13 +4511,45 @@ sub csvuploadassign { if (!$username) { my $id=$entries{$fields{'ID'}}; $id=~s/\s//g; - my %ids=&Apache::lonnet::idget($domain,$id); - $username=$ids{$id}; + if ($id ne '') { + my %ids=&Apache::lonnet::idget($domain,[$id]); + $username=$ids{$id}; + } else { + if ($entries{$fields{'clicker'}}) { + my $clicker = $entries{$fields{'clicker'}}; + $clicker=~s/\s//g; + if ($clicker ne '') { + my %clickers = &Apache::lonnet::idget($domain,[$clicker],'clickers'); + if ($clickers{$clicker} ne '') { + my $match = 0; + my @inclass; + foreach my $poss (split(/,/,$clickers{$clicker})) { + if (exists($$classlist{"$poss:$domain"})) { + $username = $poss; + push(@inclass,$poss); + $match ++; + + } + } + if ($match > 1) { + undef($username); + $request->print('<p class="LC_warning">'. + &mt('Score not saved for clicker: [_1] (matched multiple usernames: [_2])', + $clicker,join(', ',@inclass)).'</p>'); + } + } + } + } + } } if (!exists($$classlist{"$username:$domain"})) { my $id=$entries{$fields{'ID'}}; $id=~s/\s//g; - if ($id) { + my $clicker = $entries{$fields{'clicker'}}; + $clicker=~s/\s//g; + if ($clicker) { + push(@skipped,"$clicker:$domain"); + } elsif ($id) { push(@skipped,"$id:$domain"); } else { push(@skipped,"$username:$domain"); @@ -4430,6 +4649,7 @@ sub pickStudentPage { my ($request,$symb) = @_; my $alertmsg = &mt('Please select the student you wish to grade.'); + &js_escape(\$alertmsg); $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT)); function checkPickOne(formname) { @@ -4735,9 +4955,11 @@ sub displayPage { } } elsif ($env{'form.lastSub'} eq 'all') { my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : ''); + my $identifier = (&canmodify($usec)? $prob : ''); $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom, $env{'request.course.id'}, - '','.submission'); + '','.submission',undef, + $usec,$identifier); } if (&canmodify($usec)) { @@ -4789,7 +5011,7 @@ sub displaySubByDates { my $interaction; my $no_increment = 1; - my %lastrndseed; + my (%lastrndseed,%lasttype); for ($version=1;$version<=$$record{'version'};$version++) { my $timestamp = &Apache::lonlocal::locallocaltime($$record{$version.':timestamp'}); @@ -4847,12 +5069,14 @@ sub displaySubByDates { } else { $displaySub[0].=&mt('Trial: [_1]', $$record{"$where.$partid.tries"}); - if ($rndseed || $lastrndseed{$partid}) { - if ($rndseed ne $lastrndseed{$partid}) { + if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) { + if (($rndseed ne $lastrndseed{$partid}) && + (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) { $newvariation = ' ('.&mt('New variation this try').')'; } } $lastrndseed{$partid} = $rndseed; + $lasttype{$partid} = $type; } my $responseType=($isTask ? 'Task' : $responseType->{$partid}->{$responseId}); @@ -4950,7 +5174,7 @@ sub updateGradeByPage { $iterator->next(); # skip the first BEGIN_MAP my $curRes = $iterator->next(); # for "current resource" - my ($depth,$question,$prob,$changeflag)= (1,1,1,0); + my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0); while ($depth > 0) { if($curRes == $iterator->BEGIN_MAP) { $depth++; } if($curRes == $iterator->END_MAP) { $depth--; } @@ -4971,6 +5195,12 @@ sub updateGradeByPage { my @displayPts=(); my %aggregate = (); my $aggregateflag = 0; + if ($env{'form.HIDE'.$prob}) { + my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname); + my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2); + my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1); + $hideflag += $numchgs; + } foreach my $partid (@{$parts}) { my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid}; my $oldpts = $env{'form.oldpts'.$question.'_'.$partid}; @@ -5061,8 +5291,11 @@ sub updateGradeByPage { $studentTable.=&Apache::loncommon::end_data_table(); my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') : &mt('The scores were changed for [quant,_1,problem].', - $changeflag)); - $request->print($grademsg.$studentTable); + $changeflag).'<br />'); + my $hidemsg=($hideflag == 0 ? '' : + &mt('Submissions were marked "hidden" for [quant,_1,transaction].', + $hideflag).'<br />'); + $request->print($hidemsg.$grademsg.$studentTable); return ''; } @@ -5454,10 +5687,12 @@ sub scantron_selectphase { my $default_form_data=&defaultFormData($symb); my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'}; + my $alertmsg = &mt('Please use the browse button to select a file from your local directory.'); + &js_escape(\$alertmsg); $r->print(&Apache::lonhtmlcommon::scripttag(' function checkUpload(formname) { if (formname.upfile.value == "") { - alert("'.&mt('Please use the browse button to select a file from your local directory.').'"); + alert("'.$alertmsg.'"); return false; } formname.submit(); @@ -5688,7 +5923,9 @@ sub get_scantron_config { =item username_to_idmap creates a hash keyed by student/employee ID with values of the corresponding - student username:domain. + student username:domain. If a single ID occurs for more than one student, + the status of the student is checked, and if Active, the value in the hash + will be set to the Active student. Arguments: @@ -5706,8 +5943,17 @@ sub username_to_idmap { my ($classlist)= @_; my %idmap; foreach my $student (keys(%$classlist)) { - $idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}= - $student; + my $id = $classlist->{$student}->[&Apache::loncoursedata::CL_ID]; + unless ($id eq '') { + if (!exists($idmap{$id})) { + $idmap{$id} = $student; + } else { + my $status = $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS]; + if ($status eq 'Active') { + $idmap{$id} = $student; + } + } + } } return %idmap; } @@ -6577,7 +6823,7 @@ sub scantron_warning_screen { $scantron_config{'CODEstart'} && $scantron_config{'CODElength'}) { $CODElist=$env{'form.scantron_CODElist'}; - if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">None</span>'; } + if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">'.&mt('None').'</span>'; } $CODElist= '<tr><td><b>'.&mt('List of CODES to validate against:').'</b></td><td><tt>'. $env{'form.scantron_CODElist'}.'</tt></td></tr>'; @@ -7433,6 +7679,7 @@ sub verify_bubbles_checked { my (@ansnums) = @_; my $ansnumstr = join('","',@ansnums); my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines."); + &js_escape(\$warning); my $output = &Apache::lonhtmlcommon::scripttag((<<ENDSCRIPT)); function verify_bubble_radio(form) { var ansnumArray = new Array ("$ansnumstr"); @@ -8210,7 +8457,7 @@ sub hand_bubble_option { return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'. &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />'). '<label><input type="radio" name="scantron_lastbubblepoints" value="'.$bubbles_per_row.'" checked="checked" />'.&mt('[quant,_1,point]',$bubbles_per_row).'</label> '.&mt('or').' '. - '<label><input type="radio" name="scantron_lastbubblepoints" value="0"/>0 points</label></p>'; + '<label><input type="radio" name="scantron_lastbubblepoints" value="0" />'.&mt('0 points').'</label></p>'; } return; } @@ -8623,7 +8870,9 @@ sub scantron_upload_scantron_data { (' 'x2).&mt('(shows course personnel)'); my $default_form_data=&defaultFormData($symb); my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.'); + &js_escape(\$nofile_alert); my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded."); + &js_escape(\$nocourseid_alert); $r->print(&Apache::lonhtmlcommon::scripttag(' function checkUpload(formname) { if (formname.upfile.value == "") { @@ -8873,7 +9122,7 @@ sub scantron_download_scantron_data { &Apache::lonnet::allowuploaded('/adm/grades',$skipped); $r->print(' <p> - '.&mt('[_1]Original[_2] file as uploaded by the bubblesheet office.', + '.&mt('[_1]Original[_2] file as uploaded by the bubblesheet scanning office.', '<a href="'.$orig.'">','</a>').' </p> <p> @@ -9044,14 +9293,14 @@ sub checkscantron_results { '<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n". '</tr>'."\n". '<tr class="'.$css_class.'">'."\n". -'<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n"; +'<td>'.&mt('Submissions').'</td><td>'.$showrecord.'</td></tr>'."\n"; $passed ++; } else { my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row'; $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n". '</tr>'."\n". '<tr class="'.$css_class.'">'."\n". -'<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n". +'<td>'.&mt('Submissions').'</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n". '</tr>'."\n"; $failed ++; } @@ -9389,7 +9638,7 @@ sub submit_options_table { $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n". '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n"; - $result.=&selectfield(0). + $result.=&selectfield(1). '<input type="hidden" name="command" value="viewgrades" /> <div> <input type="submit" value="'.&mt('Next').' →" /> @@ -9445,13 +9694,8 @@ sub submit_options { sub selectfield { my ($full)=@_; my %options = - (&Apache::lonlocal::texthash( - 'yes' => 'with submissions', - 'queued' => 'in grading queue', - 'graded' => 'with ungraded submissions', - 'incorrect' => 'with incorrect submissions', - 'all' => 'with any status'), - 'select_form_order' => ['yes','queued','graded','incorrect','all']); + (&substatus_options, + 'select_form_order' => ['yes','queued','graded','incorrect','all']); my $result='<div class="LC_columnSection"> <fieldset> @@ -9487,6 +9731,15 @@ sub selectfield { return $result; } +sub substatus_options { + return &Apache::lonlocal::texthash( + 'yes' => 'with submissions', + 'queued' => 'in grading queue', + 'graded' => 'with ungraded submissions', + 'incorrect' => 'with incorrect submissions', + 'all' => 'with any status'); +} + sub reset_perm { undef(%perm); }