# The LearningOnline Network with CAPA # The LON-CAPA Grading handler # # $Id: grades.pm,v 1.513.2.1 2008/03/24 19:08:09 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # package Apache::grades; use strict; use Apache::style; use Apache::lonxml; use Apache::lonnet; use Apache::loncommon; use Apache::lonhtmlcommon; use Apache::lonnavmaps; use Apache::lonhomework; use Apache::lonpickcode; use Apache::loncoursedata; use Apache::lonmsg(); use Apache::Constants qw(:common); 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; } # # Prodcuces an ssi retry failure error message to the user: # 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
"; } 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 .= ''. &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); } #--- 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; 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='