--- loncom/homework/grades.pm 2002/07/19 20:42:18 1.40 +++ loncom/homework/grades.pm 2008/03/17 20:39:50 1.515 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.40 2002/07/19 20:42:18 ng Exp $ +# $Id: grades.pm,v 1.515 2008/03/17 20:39:50 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,13 +25,6 @@ # # http://www.lon-capa.org/ # -# 2/9,2/13 Guy Albertelli -# 6/8 Gerd Kortemeyer -# 7/26 H.K. Ng -# 8/20 Gerd Kortemeyer -# Year 2002 -# June, July 2002 H.K. Ng -# package Apache::grades; use strict; @@ -39,1133 +32,1396 @@ use Apache::style; use Apache::lonxml; use Apache::lonnet; use Apache::loncommon; +use Apache::lonhtmlcommon; +use Apache::lonnavmaps; use Apache::lonhomework; -use Apache::lonmsg qw(:user_normal_msg); +use Apache::lonpickcode; +use Apache::loncoursedata; +use Apache::lonmsg(); use Apache::Constants qw(:common); -#use Time::HiRes qw( gettimeofday tv_interval ); +use Apache::lonlocal; +use Apache::lonenc; +use String::Similarity; +use LONCAPA; + +use POSIX qw(floor); + + + +my %perm=(); + +# These variables are used to recover from ssi errors + +my $ssi_retries = 5; +my $ssi_error; +my $ssi_error_resource; +my $ssi_error_message; + + +# 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. + + +# +# 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 occured. +# 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 occured this becomes true. +# It is up to the caller to initialize this to false +# if desired. +# ssi_last_error_resource - If an unrecoverable error occured, this is the value +# of the resource that could not be rendered by the ssi +# call. +# ssi_last_error - 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 $content; -sub moreinfo { - my ($request,$reason) = @_; - $request->print("Unable to process request: $reason"); - if ( $Apache::grades::viewgrades eq 'F' ) { - $request->print('
'); - } - return ''; } +# +# Prodcuces an ssi retry failure error message to the user: +# -sub verifyreceipt { - my $request=shift; - my $courseid=$ENV{'request.course.id'}; -# my $cdom=$ENV{"course.$courseid.domain"}; -# my $cnum=$ENV{"course.$courseid.num"}; - my $receipt=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'. - $ENV{'form.receipt'}; - $receipt=~s/[^\-\d]//g; - my $symb=$ENV{'form.symb'}; - unless ($symb) { - $symb=&Apache::lonnet::symbread($ENV{'form.url'}); - } - if ((&Apache::lonnet::allowed('mgr',$courseid)) && ($symb)) { - $request->print(''.$matches." match%s
",$matches <= 1 ? '' : 'es'); -# needs to print who is matched +sub ssi_print_error { + my ($r) = @_; + $r->print('Unable to perform a resource fetch from a server:
');
+ $r->print("Resource: $ssi_error_resource
");
+ $r->print("Error: $ssi_error_message
Try again later.");
+ $r->print('If errors persist, contact LonCAPA support for assistance
Resource: '.$ENV{'form.url'}.' | ||
Part id: '.$_.' | '. - 'Type: '.$responsetype.' | '. - 'Handgrade: '.$handgrade.' |
"; + } else { + $result.=" | "; + } + $partsseen{$partID}=1; + } + my $display_part=&get_display_part($partID,$symb); + $result.=' | '.&mt('Part: [_1]',$display_part).' '. + $resID.' | '. + ''.&mt('Type: [_1]',$responsetype).' | '.&mt('Handgrade: [_1]',$handgrade).' | '; + } + } + $result.='
'.
- '
|
'; + } elsif ($response eq 'match') { + my %answer=&Apache::lonnet::str2hash($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); + foreach my $foil (@$order) { + my $item=shift(@items); + if ($grading{$foil} == 1) { + $toprow.=''. + '
'. + ' '.&mt('Answer').' '.$toprow.''.' '.$grayFont.&mt('Option ID').' '. + $grayFont.$bottomrow.'
'; + } elsif ($response eq 'radiobutton') { + my %answer=&Apache::lonnet::str2hash($answer); + my ($toprow,$bottomrow); + my $correct = + &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom); + foreach my $foil (@$order) { + if (exists($answer{$foil})) { + if ($foil eq $correct) { + $toprow.=''. + '
'. + ' '.&mt('Answer').' '.$toprow.''. + ' '.$grayFont.&mt('Item ID').' '. + $middlerow.''.' '.$grayFont.&mt('Option ID').' '. + $bottomrow.'
'; + } elsif ($response eq 'essay') { + if (! exists ($env{'form.'.$symb})) { + my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade', + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); + + my $loginuser = $env{'user.name'}.':'.$env{'user.domain'}; + $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : ''; + $env{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red'; + $env{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0'; + $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-'. + '
'. + ' '.&mt('Answer').' '.$toprow.''.' '.$grayFont.&mt('Option ID').' '. + $grayFont.$bottomrow.'
'.&keywords_highlight($answer).''; + } elsif ( $response eq 'organic') { + my $result='Smile representation: "'.$answer.'"'; + my $jme=$record->{$version."resource.$partid.$respid.molecule"}; + $result.=&Apache::chemresponse::jme_img($jme,$answer,400); + return $result; + } elsif ( $response eq 'Task') { + if ( $answer eq 'SUBMITTED') { + my $files = $record->{$version."resource.$respid.$partid.bridgetask.portfiles"}; + my $result = &Apache::bridgetask::file_list($files,$uname,$udom); + return $result; + } elsif ( grep(/^\Q$version\E.*?\.instance$/, keys(%{$record})) ) { + my @matches = grep(/^\Q$version\E.*?\.instance$/, + keys(%{$record})); + return join('
' + .&mt('Overall result: [_1]', + $record->{$version."resource.$respid.$partid.status"}) + .'
'; + + $result .= '';
- $result.='
|
'. + &mt('The above receipt matches the following [numerate,_1,student].',$matches). + '
'. + $header. + $contents. + &Apache::loncommon::end_data_table()."\n"; + } + return $string.&show_grading_menu_form($symb); } -sub processHandGrade { - my ($request) = shift; - my $url = $ENV{'form.url'}; - my $symb = $ENV{'form.symb'}; - my $button = $ENV{'form.gradeOpt'}; - my $ngrade = $ENV{'form.NCT'}; - my $ntstu = $ENV{'form.NTSTU'}; - - my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'}; - my %keyhash = (); - $ENV{'form.keywords'} =~ s/,\s{0,}|\s+/ /g; - $ENV{'form.keywords'} =~ s/^\s+|\s+$//; - $keyhash{$symb.'_keywords'} = $ENV{'form.keywords'}; - $keyhash{$symb.'_subject'} = $ENV{'form.msgsub'}; - $keyhash{$loginuser.'_kwclr'} = $ENV{'form.kwclr'}; - $keyhash{$loginuser.'_kwsize'} = $ENV{'form.kwsize'}; - $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'}; - - my ($ctr,$idx) = (1,1); - while ($ctr <= $ENV{'form.savemsgN'}) { - if ($ENV{'form.savemsg'.$ctr} ne '') { - $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr}; - $idx++; - } - $ctr++; - } - $ctr = 0; - while ($ctr < $ngrade) { - if ($ENV{'form.newmsg'.$ctr} ne '') { - $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr}; - $ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr}; - $idx++; - } - $ctr++; - } - $ENV{'form.savemsgN'} = --$idx; - $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'}; - my $putresult = &Apache::lonnet::put - ('nohist_handgrade',\%keyhash, - $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, - $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); - - if ($ENV{'form.refresh'} eq 'on') { - my $ctr = 0; - $ENV{'form.NTSTU'}=$ngrade; - while ($ctr < $ngrade) { - ($ENV{'form.student'},my $udom) = split(/:/,$ENV{'form.unamedom'.$ctr}); - &submission($request,$ctr,$ngrade-1); - $ctr++; - } - return ''; - } +#--- This is called by a number of programs. +#--- Called from the Grading Menu - View/Grade an individual student +#--- Also called directly when one clicks on the subm button +# on the problem page. +sub listStudents { + my ($request) = shift; - if ($button eq 'Save & Next') { - my $ctr = 0; - while ($ctr < $ngrade) { - my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr}); - my ($errorflg) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr); - return '' if ($errorflg eq 'error'); - - my $includemsg = $ENV{'form.includemsg'.$ctr}; - my ($subject,$message,$msgstatus) = ('','',''); - if ($includemsg =~ /savemsg|new$ctr/) { - $subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/); - my (@msgnum) = split(/,/,$includemsg); - foreach (@msgnum) { - $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne ''); - } - $message =~ s/\s+/ /g; - $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,$ENV{'form.msgsub'},$message); - } - if ($ENV{'form.collaborator'.$ctr}) { - my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr}); - foreach (@collaborators) { - &saveHandGrade($request,$url,$symb,$_,$udom,$ctr); - if ($message ne '') { - $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,$ENV{'form.msgsub'},$message); - } - } - } - $ctr++; - } - } - my $firststu = $ENV{'form.unamedom0'}; - my $laststu = $ENV{'form.unamedom'.($ngrade-1)}; - $ctr = 2; - while ($laststu eq '') { - $laststu = $ENV{'form.unamedom'.($ngrade-$ctr)}; - $ctr++; - $laststu = $firststu if ($ctr > $ngrade); - } - my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0'); + my ($symb) = &get_symb($request); + my $cdom = $env{"course.$env{'request.course.id'}.domain"}; + my $cnum = $env{"course.$env{'request.course.id'}.num"}; + my $getsec = $env{'form.section'} eq '' ? 'all' : $env{'form.section'}; + my $getgroup = $env{'form.group'} eq '' ? 'all' : $env{'form.group'}; + my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'}; + my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View'; + $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? + &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'}; + + my $result='Resource: '.$url.' | ||
Part id: '.$_.' | '. - 'Type: '.$responsetype.' | '. - 'Handgrade: '.$handgrade.' |
'."\n";
- $result.='
|
'.$_.' | '.$classlist{$_}.' |
'."\n";
- $result.='
|
'."\n";
- $result.='
|
'."\n";
- $result.='
|
'."\n";
- $result.='
|
\n"; + $radio.=(($ctr+1)%10 == 0 ? ' |
'.
+ &mt('Number of records updated = [_1] for [quant,_2,student].',
+ $rec_update,$count).'
'.
+ ''.&mt('Total number of students = [_1]',$env{'form.total'}).
+ '
'."\n";
+ $result.='
|
+ +
+ENDPICK + my %fields=&get_fields(); + if (!defined($fields{'domain'})) { + my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain'); + $request->print("\nUsers are in domain: ".$domform."
\n"); + } + foreach my $key (sort(keys(%env))) { + if ($key !~ /^form\.(.*)$/) { next; } + my $cleankey=$1; + if ($cleankey eq 'command') { next; } + $request->print(''."\n"); + } + # FIXME do a check for any duplicated user ids... + # FIXME do a check for any invalid user ids?... + $request->print('". + &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]", + "$username:$domain",$result)."
"); + } + $request->rflush(); + $countdone++; + } + } + $request->print(''.&mt('Skipped Students').'
'); + foreach my $student (@skipped) { $request->print("$student'.&mt('Students Not Allowed to Modify').'
'); + foreach my $student (@notallowed) { $request->print("$student