--- loncom/homework/grades.pm 2007/11/13 11:06:54 1.490 +++ loncom/homework/grades.pm 2009/04/16 16:36:32 1.562 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.490 2007/11/13 11:06:54 foxr Exp $ +# $Id: grades.pm,v 1.562 2009/04/16 16:36:32 bisitz Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,6 +26,8 @@ # http://www.lon-capa.org/ # + + package Apache::grades; use strict; use Apache::style; @@ -47,88 +49,49 @@ use LONCAPA; use POSIX qw(floor); -my %perm=(); -my %bubble_lines_per_response = (); # no. bubble lines for each response. - # index is "symb.part_id" - -my %first_bubble_line = (); # First bubble line no. for each bubble. - -# Save and restore the bubble lines array to the form env. - -sub save_bubble_lines { - foreach my $line (keys(%bubble_lines_per_response)) { - $env{"form.scantron.bubblelines.$line"} = $bubble_lines_per_response{$line}; - $env{"form.scantron.first_bubble_line.$line"} = - $first_bubble_line{$line}; - } -} - - -sub restore_bubble_lines { - my $line = 0; - %bubble_lines_per_response = (); - while ($env{"form.scantron.bubblelines.$line"}) { - my $value = $env{"form.scantron.bubblelines.$line"}; - $bubble_lines_per_response{$line} = $value; - $first_bubble_line{$line} = - $env{"form.scantron.first_bubble_line.$line"}; - $line++; - } - -} - -# Given the parsed scanline, get the response for -# 'answer' number n: +my %perm=(); -sub get_response_bubbles { - my ($parsed_line, $response) = @_; +# These variables are used to recover from ssi errors +my $ssi_retries = 5; +my $ssi_error; +my $ssi_error_resource; +my $ssi_error_message; - my $bubble_line = $first_bubble_line{$response-1} +1; - my $bubble_lines= $bubble_lines_per_response{$response-1}; - - my $selected = ""; - for (my $bline = 0; $bline < $bubble_lines; $bline++) { - $selected .= $$parsed_line{"scantron.$bubble_line.answer"}.":"; - $bubble_line++; +sub ssi_with_retries { + my ($resource, $retries, %form) = @_; + my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form); + if ($response->is_error) { + $ssi_error = 1; + $ssi_error_resource = $resource; + $ssi_error_message = $response->code . " " . $response->message; } - return $selected; -} - - -# ----- These first few routines are general use routines.---- - -# Return the number of occurences of a pattern in a string. - -sub occurence_count { - my ($string, $pattern) = @_; - my @matches = ($string =~ /$pattern/g); + return $content; - return scalar(@matches); } +# +# Prodcuces an ssi retry failure error message to the user: +# - -# Take a string known to have digits and convert all the -# digits into letters in the range J,A..I. - -sub digits_to_letters { - my ($input) = @_; - - my @alphabet = ('J', 'A'..'I'); - - my @input = split(//, $input); - my $output =''; - for (my $i = 0; $i < scalar(@input); $i++) { - if ($input[$i] =~ /\d/) { - $output .= $alphabet[$input[$i]]; - } else { - $output .= $input[$i]; - } - } - return $output; +sub ssi_print_error { + my ($r) = @_; + my $helpurl = &Apache::loncommon::top_nav_help('Helpdesk'); + $r->print(' +
+

'.&mt('An unrecoverable network error occurred:').'

+

+'.&mt('Unable to retrieve a resource from a server:').'
+'.&mt('Resource:').' '.$ssi_error_resource.'
+'.&mt('Error:').' '.$ssi_error_message.' +

+

'. +&mt('It is recommended that you try again later, as this error may mean the server was just temporarily unavailable, or is down for maintenance.').'
'. +&mt('If the error persists, please contact the [_1] for assistance.',$helpurl). +'

'); + return; } # @@ -239,8 +202,8 @@ sub showResourceInfo { my %resptype = (); my $hdgrade='no'; my %partsseen; - foreach my $partID (sort keys(%$responseType)) { - foreach my $resID (sort keys(%{ $responseType->{$partID} })) { + foreach my $partID (sort(keys(%$responseType))) { + foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) { my $handgrade=$$handgrade{$partID.'_'.$resID}; my $responsetype = $responseType->{$partID}->{$resID}; $hdgrade = $handgrade if ($handgrade eq 'yes'); @@ -254,9 +217,9 @@ sub showResourceInfo { $partsseen{$partID}=1; } my $display_part=&get_display_part($partID,$symb); - $result.=''.&mt('Part: [_1]',$display_part).' '. - $resID.''. - ''.&mt('Type: [_1]',$responsetype).''; + $result.=''.&mt('Part').': '.$display_part. + ' '.$resID.''. + ''.&mt('Type').': '.$responsetype.''; # ''.&mt('Handgrade: [_1]',$handgrade).''; } } @@ -271,45 +234,101 @@ sub reset_caches { { my %analyze_cache; + my %analyze_cache_formkeys; sub reset_analyze_cache { undef(%analyze_cache); + undef(%analyze_cache_formkeys); } sub get_analyze { - my ($symb,$uname,$udom)=@_; + my ($symb,$uname,$udom,$no_increment,$add_to_hash)=@_; my $key = "$symb\0$uname\0$udom"; - return $analyze_cache{$key} if (exists($analyze_cache{$key})); + if (exists($analyze_cache{$key})) { + my $getupdate = 0; + if (ref($add_to_hash) eq 'HASH') { + foreach my $item (keys(%{$add_to_hash})) { + if (ref($analyze_cache_formkeys{$key}) eq 'HASH') { + if (!exists($analyze_cache_formkeys{$key}{$item})) { + $getupdate = 1; + last; + } + } else { + $getupdate = 1; + } + } + } + if (!$getupdate) { + return $analyze_cache{$key}; + } + } my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); $url=&Apache::lonnet::clutter($url); - my $subresult=&Apache::lonnet::ssi($url, - ('grade_target' => 'analyze'), - ('grade_domain' => $udom), - ('grade_symb' => $symb), - ('grade_courseid' => - $env{'request.course.id'}), - ('grade_username' => $uname)); + my %form = ('grade_target' => 'analyze', + 'grade_domain' => $udom, + 'grade_symb' => $symb, + 'grade_courseid' => $env{'request.course.id'}, + 'grade_username' => $uname, + 'grade_noincrement' => $no_increment); + if (ref($add_to_hash)) { + %form = (%form,%{$add_to_hash}); + } + my $subresult=&ssi_with_retries($url, $ssi_retries,%form); (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); my %analyze=&Apache::lonnet::str2hash($subresult); + if (ref($add_to_hash) eq 'HASH') { + $analyze_cache_formkeys{$key} = $add_to_hash; + } else { + $analyze_cache_formkeys{$key} = {}; + } return $analyze_cache{$key} = \%analyze; } sub get_order { - my ($partid,$respid,$symb,$uname,$udom)=@_; - my $analyze = &get_analyze($symb,$uname,$udom); + my ($partid,$respid,$symb,$uname,$udom,$no_increment)=@_; + my $analyze = &get_analyze($symb,$uname,$udom,$no_increment); return $analyze->{"$partid.$respid.shown"}; } sub get_radiobutton_correct_foil { my ($partid,$respid,$symb,$uname,$udom)=@_; my $analyze = &get_analyze($symb,$uname,$udom); - foreach my $foil (@{&get_order($partid,$respid,$symb,$uname,$udom)}) { - if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') { - return $foil; + my $foils = &get_order($partid,$respid,$symb,$uname,$udom); + if (ref($foils) eq 'ARRAY') { + foreach my $foil (@{$foils}) { + if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') { + return $foil; + } } } } + + sub scantron_partids_tograde { + my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_; + my (%analysis,@parts); + if (ref($resource)) { + my $symb = $resource->symb(); + my $add_to_form; + if ($check_for_randomlist) { + $add_to_form = { 'check_parts_withrandomlist' => 1,}; + } + my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form); + if (ref($analyze) eq 'HASH') { + %analysis = %{$analyze}; + } + if (ref($analysis{'parts'}) eq 'ARRAY') { + foreach my $part (@{$analysis{'parts'}}) { + my ($id,$respid) = split(/\./,$part); + if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) { + push(@parts,$part); + } + } + } + } + return (\%analysis,\@parts); + } + } #--- Clean response type for display @@ -735,7 +754,7 @@ sub verifyreceipt { my $title.= '

'. - &mt('Verifying Submission Receipt [_1]',$receipt). + &mt('Verifying Receipt No. [_1]',$receipt). '

'."\n". '

'.&mt('Resource: [_1]',$env{'form.probTitle'}). '

'."\n"; @@ -819,18 +838,16 @@ sub listStudents { $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'}; - my $result='

 '. - &mt($viewgrade.' Submissions for a Student or a Group of Students') + my $result='

 ' + .&mt("$viewgrade Submissions for a Student or a Group of Students") .'

'; my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes')); - my %lt = ( '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.", - ); - %lt = &Apache::lonlocal::texthash(%lt); + my %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.', + ); $request->print(< function checkSelect(checkBox) { @@ -872,18 +889,17 @@ LISTJAVASCRIPT my $gradeTable='
'. "\n".$table; - $gradeTable .= - ' '. - &mt('View Problem Text: [_1]', - ''."\n". - ''."\n". - '').'
'."\n"; - $gradeTable .= - ' '. - &mt('View Answer: [_1]', - ''."\n". - ''."\n". - '').'
'."\n"; + $gradeTable .= &Apache::lonhtmlcommon::start_pick_box(); + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text')) + .''."\n" + .''."\n" + .'
'."\n" + .&Apache::lonhtmlcommon::row_closure(); + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer')) + .''."\n" + .''."\n" + .'
'."\n" + .&Apache::lonhtmlcommon::row_closure(); my $submission_options; if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { @@ -898,21 +914,20 @@ LISTJAVASCRIPT ''."\n". ''."\n". ''; - $gradeTable .= - ' '. - &mt('Submissions: [_1]',$submission_options).'
'."\n"; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions')) + .$submission_options + .&Apache::lonhtmlcommon::row_closure(); + + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments')) + .'' + .&Apache::lonhtmlcommon::row_closure(); $gradeTable .= - ' '. - &mt('Grading Increments: [_1]', - ''); - - $gradeTable .= &build_section_inputs(). ''."\n". '
'."\n". @@ -923,23 +938,30 @@ LISTJAVASCRIPT ''."\n"; if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) { - $gradeTable.=''."\n"; + $gradeTable .= ''."\n"; } else { - $gradeTable.=&mt('Student Status: [_1]', - &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'
'; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status')) + .&Apache::lonhtmlcommon::StatusOptions( + $saveStatus,undef,1,'javascript:reLoadList(this.form);') + .&Apache::lonhtmlcommon::row_closure(); } - $gradeTable.=&mt('To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '. - 'next to the student\'s name(s). Then click on the Next button.').'
'."\n". - ''."\n"; + $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism')) + .'' + .&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box(); + + $gradeTable .= '

' + .&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n" + .'' + .'

'; # checkall buttons $gradeTable.=&check_script('gradesub', 'stuinfo'); $gradeTable.='
'."\n"; + 'value="'.&mt('Next').' →" />
'."\n"; $gradeTable.=&check_buttons(); - $gradeTable.=''; my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup); $gradeTable.= &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(); @@ -1029,7 +1051,7 @@ LISTJAVASCRIPT ' '.$section.($group ne '' ?'/'.$group:'').''."\n"; if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { - foreach (sort keys(%status)) { + foreach (sort(keys(%status))) { next if ($_ =~ /^resource.*?submitted_by$/); $gradeTable.=' '.&mt($status{$_}).' '."\n"; } @@ -1057,7 +1079,7 @@ LISTJAVASCRIPT $gradeTable.=&Apache::loncommon::end_data_table()."\n". '
'."\n"; + 'value="'.&mt('Next').' →" />'."\n"; if ($ctr == 0) { my $num_students=(scalar(keys(%$fullname))); if ($num_students eq 0) { @@ -1153,6 +1175,7 @@ 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 = '); $request->print(< function updateRadio(formname,id,weight) { @@ -1163,7 +1186,7 @@ sub sub_page_js { gradeBox.value = pts; var resetbox = false; if (isNaN(pts) || pts < 0) { - alert("A number equal or greater than 0 is expected. Entered value = "+pts); + alert("$alertmsg"+pts); for (var i=0; iprint(< @@ -1440,7 +1464,7 @@ INNERJS else return; var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," "); if (cleantxt=="") { - alert("Please select a word or group of words from document and then click this link."); + alert("$alertmsg"); return; } var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt); @@ -1679,7 +1703,7 @@ sub gradeBox { my $radio.=''."\n"; # display radio buttons in a nice table 10 across while ($thisweight<=$wgt) { - $radio.= '\n"; @@ -1695,7 +1719,7 @@ sub gradeBox { $wgt.')" />'."\n"; $line.='',$display_part,$radio,$line); $result .= - &mt('',$display_part,$radio,$line); - + ''. $result.='
/'.$wgt.' '.$wgtmsg. ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). - ' '."\n"; + ' '.&mt('Grade Status').':'."\n"; $line.=''."\n"; + #&mt('Part:[_1]Points:[_2]or[_3]Part:[_1]Points:[_2]or[_3]'.&mt('Part').':'.$display_part.''.&mt('Points').':'.$radio.''.&mt('or').''.$line.'
'."\n"; $result.=''."\n". @@ -1841,9 +1865,9 @@ sub download_all_link { join("\n",&Apache::loncommon::get_env_multiple('form.vPart')); my $identifier = &Apache::loncommon::get_cgi_id(); - &Apache::lonnet::appenv('cgi.'.$identifier.'.students' => $all_students, - 'cgi.'.$identifier.'.symb' => $symb, - 'cgi.'.$identifier.'.parts' => $parts,); + &Apache::lonnet::appenv({'cgi.'.$identifier.'.students' => $all_students, + 'cgi.'.$identifier.'.symb' => $symb, + 'cgi.'.$identifier.'.parts' => $parts,}); $r->print(''. &mt('Download All Submitted Documents').''); return @@ -2092,7 +2116,7 @@ KEYWORDS $lastsubonly.="\n".'
Part: '. $display_part.' ( ID '.$respid. ' )   '. - ''.&mt('Nothing submitted - no attempts').'

'; + ''.&mt('Nothing submitted - no attempts.').'

'; next; } foreach my $submission (@$string) { @@ -2133,7 +2157,7 @@ KEYWORDS ' )
   '; my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record); if (@$files) { - $lastsubonly.='
'.&mt('Like all files provided by users, this file may contain virusses').'
'; + $lastsubonly.='
'.&mt('Like all files provided by users, this file may contain viruses').'
'; my $file_counter = 0; foreach my $file (@$files) { $file_counter++; @@ -2144,7 +2168,7 @@ KEYWORDS } $lastsubonly.=''.&mt('Submitted Answer:').' '. &cleanRecord($subval,$responsetype,$symb,$partid, - $respid,\%record,$order); + $respid,\%record,$order,undef,$uname,$udom); if ($similar) {$lastsubonly.="

$similar\n";} $lastsubonly.=''; } @@ -2224,8 +2248,8 @@ KEYWORDS $seen{$partid}++; next if ($$handgrade{$part_resp} ne 'yes' && $env{'form.lastSub'} eq 'hdgrade'); - push @partlist,$partid; - push @gradePartRespid,$partid.'.'.$respid; + push(@partlist,$partid); + push(@gradePartRespid,$partid.'.'.$respid); $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record)); } $request->print(''); @@ -2273,7 +2297,7 @@ KEYWORDS ''."\n"; my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1'); $ntstu =~ s/