--- loncom/homework/grades.pm 2014/11/24 02:36:26 1.728 +++ loncom/homework/grades.pm 2016/01/31 21:25:42 1.737 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.728 2014/11/24 02:36:26 raeburn Exp $ +# $Id: grades.pm,v 1.737 2016/01/31 21:25:42 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -406,7 +406,7 @@ 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><tt>'.&keywords_highlight(&HTML::Entities::encode($answer, '"<>&')).'</tt></blockquote>'; + return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>'; } elsif ( $response eq 'organic') { my $result=&mt('Smile representation: [_1]', @@ -857,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; @@ -871,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); @@ -1177,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]; @@ -1422,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', @@ -1438,21 +1451,15 @@ INNERJS txtc => 'Text Color', font => 'Font Size', fnst => 'Font Style', - col1 => 'red', - col2 => 'green', - col3 => 'blue', - siz1 => 'normal', - siz2 => '+1', - siz3 => '+2', - sty1 => 'normal', - sty2 => 'italic', - sty3 => 'bold', ); + &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; @@ -1479,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 != "") { @@ -1562,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>"); } @@ -1587,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>"); } @@ -1595,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(); @@ -1610,15 +1617,15 @@ INNERJS var redsel = ""; var grnsel = ""; var blusel = ""; - var txtcol1 = "$lt{'col1'}"; - var txtcol2 = "$lt{'col2'}"; - var txtcol3 = "$lt{'col3'}"; - var txtsiz1 = "$lt{'siz1'}"; - var txtsiz2 = "$lt{'siz2'}"; - var txtsiz3 = "$lt{'siz3'}"; - var txtsty1 = "$lt{'sty1'}"; - var txtsty2 = "$lt{'sty2'}"; - var txtsty3 = "$lt{'sty3'}"; + 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'"}; @@ -1655,10 +1662,10 @@ INNERJS hDoc.$docopen; hDoc.write('$start_page_highlight_central'); hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">"); - hDoc.write("<h1>$lt{'kehi'}<\\/h1>"); + hDoc.write("<h1>$html_js_lt{'kehi'}<\\/h1>"); hDoc.write('<table border="0" width="100%"><tr style="background-color:#A1D676">'); - hDoc.write("<th>$lt{'txtc'}<\\/th><th>$lt{'font'}<\\/th><th>$lt{'fnst'}<\\/th><\\/tr>"); + 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) { @@ -1676,8 +1683,8 @@ INNERJS function highlightend() { var hDoc = hwdWin.document; hDoc.write("<\\/table><br \\/>"); - 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("<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(); @@ -1811,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.', @@ -2543,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; @@ -3199,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, @@ -3354,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); @@ -3385,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 @@ -3452,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]; @@ -4101,6 +4036,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; @@ -4141,6 +4078,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; @@ -4218,6 +4157,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); @@ -4249,6 +4189,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 == "") { @@ -4412,13 +4353,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"); @@ -4518,6 +4491,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) { @@ -4879,7 +4853,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'}); @@ -4937,12 +4911,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}); @@ -5553,10 +5529,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(); @@ -5787,7 +5765,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: @@ -5805,8 +5785,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; } @@ -7532,6 +7521,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"); @@ -8722,7 +8712,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 == "") {