--- loncom/homework/grades.pm 2007/11/13 01:14:32 1.488 +++ loncom/homework/grades.pm 2009/06/17 18:41:31 1.528.2.14 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.488 2007/11/13 01:14:32 albertel Exp $ +# $Id: grades.pm,v 1.528.2.14 2009/06/17 18:41:31 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -47,88 +47,89 @@ 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. +my %perm=(); -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++; - } - -} +# These variables are used to recover from ssi errors -# Given the parsed scanline, get the response for -# 'answer' number n: +my $ssi_retries = 5; +my $ssi_error; +my $ssi_error_resource; +my $ssi_error_message; -sub get_response_bubbles { - my ($parsed_line, $response) = @_; +# Do an ssi with retries: +# While I'd love to factor out this with the vesrion in lonprintout, +# that would either require a data coupling between modules, which I refuse to perpetuate +# (there's quite enough of that already), or would require the invention of another infrastructure +# I'm not quite ready to invent (e.g. an ssi_with_retry object). +# +# At least the logic that drives this has been pulled out into loncommon. - 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++; +# +# ssi_with_retries - Does the server side include of a resource. +# if the ssi call returns an error we'll retry it up to +# the number of times requested by the caller. +# If we still have a proble, no text is appended to the +# output and we set some global variables. +# to indicate to the caller an SSI error occurred. +# All of this is supposed to deal with the issues described +# in LonCAPA BZ 5631 see: +# http://bugs.lon-capa.org/show_bug.cgi?id=5631 +# by informing the user that this happened. +# +# Parameters: +# resource - The resource to include. This is passed directly, without +# interpretation to lonnet::ssi. +# form - The form hash parameters that guide the interpretation of the resource +# +# retries - Number of retries allowed before giving up completely. +# Returns: +# On success, returns the rendered resource identified by the resource parameter. +# Side Effects: +# The following global variables can be set: +# ssi_error - If an unrecoverable error occurred this becomes true. +# It is up to the caller to initialize this to false +# if desired. +# ssi_error_resource - If an unrecoverable error occurred, this is the value +# of the resource that could not be rendered by the ssi +# call. +# ssi_error_message - The error string fetched from the ssi response +# in the event of an error. +# +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. + return $content; -sub occurence_count { - my ($string, $pattern) = @_; - - my @matches = ($string =~ /$pattern/g); - - 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 +240,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 +255,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).''; } } @@ -277,38 +278,42 @@ sub reset_caches { } sub get_analyze { - my ($symb,$uname,$udom)=@_; + my ($symb,$uname,$udom,$no_increment)=@_; my $key = "$symb\0$uname\0$udom"; return $analyze_cache{$key} if (exists($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 $subresult=&ssi_with_retries($url, $ssi_retries, + ('grade_target' => 'analyze', + 'grade_domain' => $udom, + 'grade_symb' => $symb, + 'grade_courseid' => + $env{'request.course.id'}, + 'grade_username' => $uname, + 'grade_noincrement' => $no_increment)); (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); my %analyze=&Apache::lonnet::str2hash($subresult); 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; + } + } + } } } @@ -735,7 +740,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"; @@ -820,17 +825,15 @@ sub listStudents { &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'}; my $result='

 '. - &mt($viewgrade.' Submissions for a Student or a Group of Students') + &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) { @@ -873,17 +876,15 @@ LISTJAVASCRIPT "\n".$table; $gradeTable .= - ' '. - &mt('View Problem Text: [_1]', + ' '.&mt('View Problem Text').': '. ''."\n". ''."\n". - '').'
'."\n"; + '
'."\n"; $gradeTable .= - ' '. - &mt('View Answer: [_1]', + ' '.&mt('View Answer').': '. ''."\n". ''."\n". - '').'
'."\n"; + '
'."\n"; my $submission_options; if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { @@ -899,18 +900,16 @@ LISTJAVASCRIPT ''."\n". ''; $gradeTable .= - ' '. - &mt('Submissions: [_1]',$submission_options).'
'."\n"; + ' '.&mt('Submissions').': '.$submission_options.'
'."\n"; $gradeTable .= - ' '. - &mt('Grading Increments: [_1]', + ' '.&mt('Grading Increments').': '. ''); + ''; $gradeTable .= &build_section_inputs(). @@ -929,15 +928,14 @@ LISTJAVASCRIPT &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'
'; } - $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". + $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"; # 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); @@ -1029,7 +1027,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 +1055,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 +1151,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 +1162,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 +1440,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 +1679,7 @@ sub gradeBox { my $radio.=''."\n"; # display radio buttons in a nice table 10 across while ($thisweight<=$wgt) { - $radio.= '\n"; @@ -1695,7 +1695,7 @@ sub gradeBox { $wgt.')" />'."\n"; $line.='',$display_part,$radio,$line); - + ''; $result.='
/'.$wgt.' '.$wgtmsg. ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). - ' '."\n"; + ' '.&mt('Grade Status').':'."\n"; $line.='Part:[_1]Points:[_2]or[_3]'.&mt('Part').':'.$display_part.''.&mt('Points').':'.$radio.''.&mt('or').''.$line.'
'."\n"; $result.=''."\n". @@ -1841,9 +1840,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 +2091,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 +2132,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 +2143,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 +2223,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 +2272,7 @@ KEYWORDS ''."\n"; my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1'); $ntstu =~ s/