+'.&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;
+}
+
#
# --- Retrieve the parts from the metadata file.---
sub getpartlist {
@@ -157,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');
@@ -195,27 +240,28 @@ 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"};
}
@@ -947,7 +993,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";
}
@@ -1759,9 +1805,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
@@ -2142,8 +2188,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('');
@@ -2470,7 +2516,7 @@ sub processHandGrade {
my (@parsedlist,@nextlist);
my ($nextflg) = 0;
- foreach (sort
+ foreach my $item (sort
{
if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
@@ -2478,12 +2524,12 @@ sub processHandGrade {
return $a cmp $b;
} (keys(%$fullname))) {
if ($nextflg == 1 && $button =~ /Next$/) {
- push @parsedlist,$_;
+ push(@parsedlist,$item);
}
- $nextflg = 1 if ($_ eq $laststu);
+ $nextflg = 1 if ($item eq $laststu);
if ($button eq 'Previous') {
- last if ($_ eq $firststu);
- push @parsedlist,$_;
+ last if ($item eq $firststu);
+ push(@parsedlist,$item);
}
}
$ctr = 0;
@@ -2506,11 +2552,11 @@ sub processHandGrade {
my $submitted = 0;
my $ungraded = 0;
my $incorrect = 0;
- foreach (keys(%status)) {
- $submitted = 1 if ($status{$_} ne 'nothing');
- $ungraded = 1 if ($status{$_} =~ /^ungraded/);
- $incorrect = 1 if ($status{$_} =~ /^incorrect/);
- my ($foo,$partid,$foo1) = split(/\./,$_);
+ foreach my $item (keys(%status)) {
+ $submitted = 1 if ($status{$item} ne 'nothing');
+ $ungraded = 1 if ($status{$item} =~ /^ungraded/);
+ $incorrect = 1 if ($status{$item} =~ /^incorrect/);
+ my ($foo,$partid,$foo1) = split(/\./,$item);
if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
$submitted = 0;
}
@@ -2521,7 +2567,7 @@ sub processHandGrade {
next if (!$ungraded && ($submitonly eq 'graded'));
next if (!$incorrect && $submitonly eq 'incorrect');
}
- push @nextlist,$student if ($ctr < $ntstu);
+ push(@nextlist,$student) if ($ctr < $ntstu);
last if ($ctr == $ntstu);
$ctr++;
}
@@ -2529,7 +2575,7 @@ sub processHandGrade {
$ctr = 0;
my $total = scalar(@nextlist)-1;
- foreach (sort @nextlist) {
+ foreach (sort(@nextlist)) {
my ($uname,$udom,$submitter) = split(/:/);
$env{'form.student'} = $uname;
$env{'form.userdom'} = $udom;
@@ -2575,7 +2621,7 @@ sub saveHandGrade {
}
} elsif ($dropMenu eq 'reset status'
&& exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
- foreach my $key (keys (%record)) {
+ foreach my $key (keys(%record)) {
if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; }
}
$newrecord{'resource.'.$new_part.'.regrader'}=
@@ -2610,7 +2656,7 @@ sub saveHandGrade {
&handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
next;
} else {
- push @parts_graded, $new_part;
+ push(@parts_graded,$new_part);
}
if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
$newrecord{'resource.'.$new_part.'.awarded'} = $partial;
@@ -2637,7 +2683,7 @@ sub saveHandGrade {
$record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' ||
$dropMenu eq 'reset status')
{
- push (@version_parts,$new_part);
+ push(@version_parts,$new_part);
}
}
my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
@@ -2685,7 +2731,7 @@ sub check_and_remove_from_queue {
sub handback_files {
my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;
- my $portfolio_root = &propath($domain,$stuname).'/userfiles/portfolio';
+ my $portfolio_root = '/userfiles/portfolio';
my ($partlist,$handgrade,$responseType) = &response_type($symb);
my @part_response_id = &flatten_responseType($responseType);
@@ -2703,7 +2749,8 @@ sub handback_files {
my ($answer_name,$answer_ver,$answer_ext) =
&file_name_version_ext($answer_file);
my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
- my @dir_list = &Apache::lonnet::dirlist($portfolio_path,$domain,$stuname,$portfolio_root);
+ my $getpropath = 1;
+ my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,$domain,$stuname,$getpropath);
my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
# fix file name
my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
@@ -2809,7 +2856,7 @@ sub decrement_aggs {
if ($aggtries == $totaltries) {
$decrement{'users'} = 1;
}
- foreach my $type (keys (%decrement)) {
+ foreach my $type (keys(%decrement)) {
$$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type};
}
return;
@@ -2839,8 +2886,7 @@ sub version_portfiles {
my $version_parts = join('|',@$v_flag);
my @returned_keys;
my $parts = join('|', @$parts_graded);
- my $portfolio_root = &propath($domain,$stu_name).
- '/userfiles/portfolio';
+ my $portfolio_root = '/userfiles/portfolio';
foreach my $key (keys(%$record)) {
my $new_portfiles;
if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
@@ -2851,7 +2897,8 @@ sub version_portfiles {
my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
my ($answer_name,$answer_ver,$answer_ext) =
&file_name_version_ext($answer_file);
- my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stu_name,$portfolio_root);
+ my $getpropath = 1;
+ my @dir_list = &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') {
@@ -3210,7 +3257,7 @@ sub viewgrades {
$display =~ s|^Number of Attempts|Tries |; # makes the column narrower
if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
my ($partid) = &split_part_type($part);
- push(@partids, $partid);
+ push(@partids,$partid);
my $display_part=&get_display_part($partid,$symb);
if ($display =~ /^Partial Credit Factor/) {
$result.='
'.
@@ -3364,7 +3411,7 @@ sub editgrades {
my $header;
while ($ctr < $env{'form.totalparts'}) {
my $partid = $env{'form.partid_'.$ctr};
- push @partid,$partid;
+ push(@partid,$partid);
$weight{$partid} = $env{'form.weight_'.$partid};
$ctr++;
}
@@ -3960,31 +4007,31 @@ sub csvuploadassign {
$grades{$store_key}=$entries{$fields{$dest}};
}
}
- if (! %grades) { push(@skipped,"$username:$domain no data to save"); }
- $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
- my $result=&Apache::lonnet::cstore(\%grades,$symb,
+ if (! %grades) {
+ push(@skipped,&mt("[_1]: no data to save","$username:$domain"));
+ } else {
+ $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
+ my $result=&Apache::lonnet::cstore(\%grades,$symb,
$env{'request.course.id'},
$domain,$username);
- if ($result eq 'ok') {
- $request->print('.');
- } else {
- $request->print("
-
- Failed to save student $username:$domain.
- Message when trying to save was ($result)
-
-
';
my %newrecord=();
@@ -4513,10 +4562,10 @@ sub updateGradeByPage {
}
my $display_part=&get_display_part($partid,$curRes->symb());
my $oldstatus = $env{'form.solved'.$question.'_'.$partid};
- $displayPts[0].=' Part: '.$display_part.' = '.
+ $displayPts[0].=' '.&mt('Part').': '.$display_part.' = '.
(($oldstatus eq 'excused') ? 'excused' : $oldpts).
' ';
- $displayPts[1].=' Part: '.$display_part.' = '.
+ $displayPts[1].=' '.&mt('Part').': '.$display_part.' = '.
(($score eq 'excused') ? 'excused' : $newpts).
' ';
$question++;
@@ -4565,9 +4614,9 @@ sub updateGradeByPage {
$studentTable.=&Apache::loncommon::end_data_table();
$studentTable.=&show_grading_menu_form($env{'form.symb'});
- my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :
- 'The scores were changed for '.
- $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
+ my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') :
+ &mt('The scores were changed for [quant,_1,problem].',
+ $changeflag));
$request->print($grademsg.$studentTable);
return '';
@@ -4681,9 +4730,10 @@ my %bubble_lines_per_response; # no.
my %first_bubble_line; # First bubble line no. for each bubble.
-my %subdivided_bubble_lines; # no. bubble lines for optionresponse
- # or matchresponse where an individual
- # response can have multiple lines
+my %subdivided_bubble_lines; # no. bubble lines for optionresponse,
+ # matchresponse or rankresponse, where
+ # an individual response can have multiple
+ # lines
my %responsetype_per_response; # responsetype for each response
@@ -4750,8 +4800,9 @@ sub get_response_bubbles {
sub scantron_filenames {
my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
+ my $getpropath = 1;
my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
- &propath($cdom,$cname));
+ $getpropath);
my @possiblenames;
foreach my $filename (sort(@files)) {
($filename)=split(/&/,$filename);
@@ -4794,19 +4845,76 @@ sub scantron_uploads {
=cut
sub scantron_scantab {
- my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
my $result=''."\n";
-
return $result;
}
+=pod
+
+=item get_scantronformat_file
+
+ Returns an array containing lines from the scantron format file for
+ the domain of the course.
+
+ If a url for a custom.tab file is listed in domain's configuration.db,
+ lines are from this file.
+
+ Otherwise, if a default.tab has been published in RES space by the
+ domainconfig user, lines are from this file.
+
+ Otherwise, fall back to getting lines from the legacy file on the
+ local server: /home/httpd/lonTabs/default_scantronformat.tab
+
+=cut
+
+sub get_scantronformat_file {
+ my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$cdom);
+ my $gottab = 0;
+ my @lines;
+ if (ref($domconfig{'scantron'}) eq 'HASH') {
+ if ($domconfig{'scantron'}{'scantronformat'} ne '') {
+ my $formatfile = &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.$domconfig{'scantron'}{'scantronformat'});
+ if ($formatfile ne '-1') {
+ @lines = split("\n",$formatfile,-1);
+ $gottab = 1;
+ }
+ }
+ }
+ if (!$gottab) {
+ my $confname = $cdom.'-domainconfig';
+ my $default = $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cdom.'/'.$confname.'/default.tab';
+ my $formatfile = &Apache::lonnet::getfile($default);
+ if ($formatfile ne '-1') {
+ @lines = split("\n",$formatfile,-1);
+ $gottab = 1;
+ }
+ }
+ if (!$gottab) {
+ my @domains = &Apache::lonnet::current_machine_domains();
+ if (grep(/^\Q$cdom\E$/,@domains)) {
+ my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
+ @lines = <$fh>;
+ close($fh);
+ } else {
+ my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab');
+ @lines = <$fh>;
+ close($fh);
+ }
+ }
+ return @lines;
+}
+
=pod
=item scantron_CODElist
@@ -4880,6 +4988,8 @@ sub scantron_selectphase {
my $CODE_unique=&scantron_CODEunique();
my $result;
+ $ssi_error = 0;
+
# Chunk of form to prompt for a file to grade and how:
$result.= '
@@ -5000,8 +5110,37 @@ sub scantron_selectphase {
');
&Apache::lonpickcode::code_list($r,2);
+
+ $r->print('
');
$r->print($grading_menu_button);
- return
+ return;
}
=pod
@@ -5063,10 +5202,10 @@ sub scantron_selectphase {
sub get_scantron_config {
my ($which) = @_;
- my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
+ my @lines = &get_scantronformat_file();
my %config;
#FIXME probably should move to XML it has already gotten a bit much now
- foreach my $line (<$fh>) {
+ foreach my $line (@lines) {
my ($name,$descrip)=split(/:/,$line);
if ($name ne $which ) { next; }
chomp($line);
@@ -5457,7 +5596,10 @@ sub scantron_validator_lettnum {
my $occurrences = 0;
if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||
($responsetype_per_response{$questnum-1} eq 'formularesponse') ||
- ($responsetype_per_response{$questnum-1} eq 'stringresponse')) {
+ ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||
+ ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||
+ ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||
+ ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {
my @singlelines = split('',$currquest);
foreach my $entry (@singlelines) {
$occurrences = &occurence_count($entry,$matchon);
@@ -5524,6 +5666,10 @@ sub scantron_validator_positional {
# If the split only gives us one element.. the full length of the
# answer string, no bubbles are filled in:
+ if ($answers_needed eq '') {
+ return;
+ }
+
if (length($array[0]) eq $$scantron_config{'Qlength'}*$answers_needed) {
for (my $ans=0; $ans<$answers_needed; $ans++ ) {
$record->{"scantron.$ansnum.answer"}='';
@@ -5552,7 +5698,10 @@ sub scantron_validator_positional {
#
if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||
($responsetype_per_response{$questnum-1} eq 'formularesponse') ||
- ($responsetype_per_response{$questnum-1} eq 'stringresponse')) {
+ ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||
+ ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||
+ ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||
+ ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {
my $doubleerror = 0;
while (($currquest >= $$scantron_config{'Qlength'}) &&
(!$doubleerror)) {
@@ -6026,8 +6175,7 @@ sub scantron_validate_file {
}
if (!$stop) {
my $warning=&scantron_warning_screen('Start Grading');
- $r->print('
-'.&mt('Validation process complete.').'
+ $r->print(&mt('Validation process complete.').'
'.$warning.'
@@ -6425,14 +6573,7 @@ sub scantron_validate_sequence {
return (0,$currentphase+1);
}
-=pod
-
-=item scantron_validate_ID
- Validates all scanlines in the selected file to not have any
- invalid or underspecified student IDs
-
-=cut
sub scantron_validate_ID {
my ($r,$currentphase) = @_;
@@ -6498,35 +6639,6 @@ sub scantron_validate_ID {
return (0,$currentphase+1);
}
-=pod
-
-=item scantron_get_correction
-
- Builds the interface screen to interact with the operator to fix a
- specific error condition in a specific scanline
-
- Arguments:
- $r - Apache request object
- $i - number of the current scanline
- $scan_record - hash ref as returned from &scantron_parse_scanline()
- $scan_config - hash ref as returned from &get_scantron_config()
- $line - full contents of the current scanline
- $error - error condition, valid values are
- 'incorrectCODE', 'duplicateCODE',
- 'doublebubble', 'missingbubble',
- 'duplicateID', 'incorrectID'
- $arg - extra information needed
- For errors:
- - duplicateID - paper number that this studentID was seen before on
- - duplicateCODE - array ref of the paper numbers this CODE was
- seen on before
- - incorrectCODE - current incorrect CODE
- - doublebubble - array ref of the bubble lines that have double
- bubble errors
- - missingbubble - array ref of the bubble lines that have missing
- bubble errors
-
-=cut
sub scantron_get_correction {
my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;
@@ -6664,7 +6776,7 @@ ENDSCRIPT
foreach my $question (@{$arg}) {
my @linenums = &prompt_for_corrections($r,$question,$scan_config,
$scan_record, $error);
- push (@lines_to_correct,@linenums);
+ push(@lines_to_correct,@linenums);
}
$r->print(&verify_bubbles_checked(@lines_to_correct));
} elsif ($error eq 'missingbubble') {
@@ -6684,7 +6796,7 @@ ENDSCRIPT
foreach my $question (@{$arg}) {
my @linenums = &prompt_for_corrections($r,$question,$scan_config,
$scan_record, $error);
- push (@lines_to_correct,@linenums);
+ push(@lines_to_correct,@linenums);
}
$r->print(&verify_bubbles_checked(@lines_to_correct));
} else {
@@ -6788,13 +6900,14 @@ for multi and missing bubble cases).
Numbered from 0 (but question numbers are from
1.
%first_bubble_line - Starting bubble line for each question.
- %subdivided_bubble_lines - optionresponse and matchresponse type
- problems render as separate sub-questions,
+ %subdivided_bubble_lines - optionresponse, matchresponse and rankresponse
+ type problems render as separate sub-questions,
in exam mode. This hash contains a
comma-separated list of the lines per
sub-question.
- %responsetype_per_response - essayresponse, forumalaresponse, and
- stringresponse type problem parts can have
+ %responsetype_per_response - essayresponse, formularesponse,
+ stringresponse, imageresponse, reactionresponse,
+ and organicresponse type problem parts can have
multiple lines per response if the weight
assigned exceeds 10. In this case, only
one bubble per line is permitted, but more
@@ -6828,7 +6941,10 @@ sub prompt_for_corrections {
$r->print(&mt('The group of bubble lines below responds to a single question.').' ');
if (($responsetype_per_response{$question-1} eq 'essayresponse') ||
($responsetype_per_response{$question-1} eq 'formularesponse') ||
- ($responsetype_per_response{$question-1} eq 'stringresponse')) {
+ ($responsetype_per_response{$question-1} eq 'stringresponse') ||
+ ($responsetype_per_response{$question-1} eq 'imageresponse') ||
+ ($responsetype_per_response{$question-1} eq 'reactionresponse') ||
+ ($responsetype_per_response{$question-1} eq 'organicresponse')) {
$r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their scantron sheets.",$lines).'
'.&mt('A non-zero score can be assigned to the student during scantron grading by selecting a bubble in at least one line.').' '.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').' '.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'
');
} else {
$r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")." ");
@@ -6838,7 +6954,7 @@ sub prompt_for_corrections {
my $selected = $$scan_record{"scantron.$current_line.answer"};
&scantron_bubble_selector($r,$scan_config,$current_line,
$questionnum,$error,split('', $selected));
- push (@linenums,$current_line);
+ push(@linenums,$current_line);
$current_line++;
}
if ($lines > 1) {
@@ -7054,7 +7170,7 @@ sub scantron_validate_CODE {
$line,'duplicateCODE',$usedCODEs{$CODE});
return(1,$currentphase);
}
- push (@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'});
+ push(@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'});
}
return (0,$currentphase+1);
}
@@ -7093,25 +7209,6 @@ sub scantron_validate_doublebubble {
return (0,$currentphase+1);
}
-=pod
-
-=item scantron_get_maxbubble
-
- Returns the maximum number of bubble lines that are expected to
- occur. Does this by walking the selected sequence rendering the
- resource and then checking &Apache::lonxml::get_problem_counter()
- for what the current value of the problem counter is.
-
- Caches the results to $env{'form.scantron_maxbubble'},
- $env{'form.scantron.bubble_lines.n'},
- $env{'form.scantron.first_bubble_line.n'} and
- $env{"form.scantron.sub_bubblelines.n"}
- which are the total number of bubble, lines, the number of bubble
- lines for response n and number of the first bubble line for response n,
- and a comma separated list of numbers of bubble lines for sub-questions
- (for optionresponse items only), for response n.
-
-=cut
sub scantron_get_maxbubble {
if (defined($env{'form.scantron_maxbubble'}) &&
@@ -7141,51 +7238,68 @@ sub scantron_get_maxbubble {
my $response_number = 0;
my $bubble_line = 0;
foreach my $resource (@resources) {
- # Need to retrieve part IDs and response IDs because essayresponse
- # items are not included in $analysis{'parts'} from lonnet::ssi.
- my %possible_part_ids;
- if (ref($resource->parts()) eq 'ARRAY') {
+ my $symb = $resource->symb();
+
+ my (@parts,@allparts,@possible_parts);
+
+ # Need to retrieve part IDs and response IDs because essayresponse,
+ # reactionresponse and organicresponse items are not included in
+ # $analysis{'parts'} from lonnet::ssi.
+ if (ref($resource->parts()) eq 'ARRAY') {
foreach my $part (@{$resource->parts()}) {
- my @resp_ids = $resource->responseIds($part);
- foreach my $id (@resp_ids) {
- $possible_part_ids{$part.'.'.$id} = 1;
+ if (!&Apache::loncommon::check_if_partid_hidden($part,$symb,$udom,$uname)) {
+ my @resp_ids = $resource->responseIds($part);
+ foreach my $id (@resp_ids) {
+ my $part_id = $part.'.'.$id;
+ push(@possible_parts,$part_id);
+ }
}
}
}
- my $result=&Apache::lonnet::ssi($resource->src(),
- ('symb' => $resource->symb()),
- ('grade_target' => 'analyze'),
- ('grade_courseid' => $cid),
- ('grade_domain' => $udom),
- ('grade_username' => $uname));
- my (undef, $an) =
- split(/_HASH_REF__/,$result, 2);
- my @parts;
+ my $result=&ssi_with_retries($resource->src(), $ssi_retries,
+ ('symb' => $symb,
+ 'grade_target' => 'analyze',
+ 'grade_courseid' => $cid,
+ 'grade_domain' => $udom,
+ 'grade_username' => $uname));
+ my (undef, $an) =
+ split(/_HASH_REF__/,$result, 2);
my %analysis = &Apache::lonnet::str2hash($an);
if (ref($analysis{'parts'}) eq 'ARRAY') {
- @parts = @{$analysis{'parts'}};
+ foreach my $part (@{$analysis{'parts'}}) {
+ my ($id,$respid) = split(/\./,$part);
+ if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
+ push(@parts,$part);
+ }
+ }
}
- # Add part_ids for any essayresponse items.
- foreach my $part_id (keys(%possible_part_ids)) {
- if ($analysis{$part_id.'.type'} eq 'essayresponse') {
- if (!grep(/^\Q$part_id\E$/,@parts)) {
- push (@parts,$part_id);
+ # Add part_ids for any essayresponse, reactionresponse or
+ # organicresponse items.
+ foreach my $part_id (@possible_parts) {
+ if (grep(/^\Q$part_id\E$/,@parts)) {
+ push(@allparts,$part_id);
+ } else {
+ if (($analysis{$part_id.'.type'} eq 'essayresponse') ||
+ ($analysis{$part_id.'.type'} eq 'reactionresponse') ||
+ ($analysis{$part_id.'.type'} eq 'organicresponse')) {
+ push(@allparts,$part_id);
}
}
}
- foreach my $part_id (@parts) {
- my $lines = $analysis{"$part_id.bubble_lines"};
+ foreach my $part_id (@allparts) {
+ my $lines;
# TODO - make this a persistent hash not an array.
- # optionresponse and matchresponse type items render as
- # separate sub-questions in exam mode.
+ # optionresponse, matchresponse and rankresponse type items
+ # render as separate sub-questions in exam mode.
if (($analysis{$part_id.'.type'} eq 'optionresponse') ||
- ($analysis{$part_id.'.type'} eq 'matchresponse')) {
+ ($analysis{$part_id.'.type'} eq 'matchresponse') ||
+ ($analysis{$part_id.'.type'} eq 'rankresponse')) {
my ($numbub,$numshown);
if ($analysis{$part_id.'.type'} eq 'optionresponse') {
if (ref($analysis{$part_id.'.options'}) eq 'ARRAY') {
@@ -7195,13 +7309,17 @@ sub scantron_get_maxbubble {
if (ref($analysis{$part_id.'.items'}) eq 'ARRAY') {
$numbub = scalar(@{$analysis{$part_id.'.items'}});
}
+ } elsif ($analysis{$part_id.'.type'} eq 'rankresponse') {
+ if (ref($analysis{$part_id.'.foils'}) eq 'ARRAY') {
+ $numbub = scalar(@{$analysis{$part_id.'.foils'}});
+ }
}
if (ref($analysis{$part_id.'.shown'}) eq 'ARRAY') {
$numshown = scalar(@{$analysis{$part_id.'.shown'}});
}
my $bubbles_per_line = 10;
- my $inner_bubble_lines = int($numshown/$bubbles_per_line);
- if (($numshown % $bubbles_per_line) != 0) {
+ my $inner_bubble_lines = int($numbub/$bubbles_per_line);
+ if (($numbub % $bubbles_per_line) != 0) {
$inner_bubble_lines++;
}
for (my $i=0; $i<$numshown; $i++) {
@@ -7209,6 +7327,9 @@ sub scantron_get_maxbubble {
$inner_bubble_lines.',';
}
$subdivided_bubble_lines{$response_number} =~ s/,$//;
+ $lines = $numshown * $inner_bubble_lines;
+ } else {
+ $lines = $analysis{"$part_id.bubble_lines"};
}
$first_bubble_line{$response_number} = $bubble_line;
@@ -7230,15 +7351,6 @@ sub scantron_get_maxbubble {
return $env{'form.scantron_maxbubble'};
}
-=pod
-
-=item scantron_validate_missingbubbles
-
- Validates all scanlines in the selected file to not have any
- answers that don't have bubbles that have not been verified
- to be bubble free.
-
-=cut
sub scantron_validate_missingbubbles {
my ($r,$currentphase) = @_;
@@ -7293,35 +7405,15 @@ sub scantron_validate_missingbubbles {
return (0,$currentphase+1);
}
-=pod
-
-=item scantron_process_students
-
- Routine that does the actual grading of the bubble sheet information.
-
- The parsed scanline hash is added to %env
-
- Then foreach unskipped scanline it does an &Apache::lonnet::ssi()
- foreach resource , with the form data of
-
- 'submitted' =>'scantron'
- 'grade_target' =>'grade',
- 'grade_username'=> username of student
- 'grade_domain' => domain of student
- 'grade_courseid'=> of course
- 'grade_symb' => symb of resource to grade
-
- This triggers a grading pass. The problem grading code takes care
- of converting the bubbled letter information (now in %env) into a
- valid submission.
-
-=cut
sub scantron_process_students {
my ($r) = @_;
+
my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
my ($symb)=&get_symb($r);
- if (!$symb) {return '';}
+ if (!$symb) {
+ return '';
+ }
my $default_form_data=&defaultFormData($symb);
my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
@@ -7342,6 +7434,7 @@ SCANTRONFORM
my @delayqueue;
my %completedstudents;
+ my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
my $count=&get_todo_count($scanlines,$scan_data);
my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
'Scantron Progress',$count,
@@ -7353,6 +7446,18 @@ SCANTRONFORM
my ($uname,$udom,$started);
&scantron_get_maxbubble(); # Need the bubble lines array to parse.
+
+
+ # If an ssi failed in scantron_get_maxbubble, put an error message out to
+ # the user and return.
+
+ if ($ssi_error) {
+ $r->print("");
+ &ssi_print_error($r);
+ $r->print(&show_grading_menu_form($symb));
+ &Apache::lonnet::remove_lock($lock);
+ return ''; # Dunno why the other returns return '' rather than just returning.
+ }
while ($i<$scanlines->{'count'}) {
($uname,$udom)=('','');
@@ -7380,7 +7485,7 @@ SCANTRONFORM
($uname,$udom)=split(/:/,$uname);
&Apache::lonxml::clear_problem_counter();
- &Apache::lonnet::appenv(%$scan_record);
+ &Apache::lonnet::appenv($scan_record);
if (&scantron_clear_skip($scanlines,$scan_data,$i)) {
&scantron_putfile($scanlines,$scan_data);
@@ -7401,10 +7506,17 @@ SCANTRONFORM
$form{'CODE'}=$scan_record->{'scantron.CODE'};
} else {
$form{'CODE'}='';
+ }
+ my $result=&ssi_with_retries($resource->src(), $ssi_retries, %form);
+ if ($ssi_error) {
+ $ssi_error = 0; # So end of handler error message does not trigger.
+ $r->print("");
+ &ssi_print_error($r);
+ $r->print(&show_grading_menu_form($symb));
+ &Apache::lonnet::remove_lock($lock);
+ return ''; # Why return ''? Beats me.
}
- my $result=&Apache::lonnet::ssi($resource->src(),%form);
- if ($result ne '') {
- }
+
if (&Apache::loncommon::connection_aborted($r)) { last; }
}
$completedstudents{$uname}={'line'=>$line};
@@ -7414,6 +7526,7 @@ SCANTRONFORM
&Apache::lonnet::delenv('scantron\.');
}
&Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
+ &Apache::lonnet::remove_lock($lock);
# my $lasttime = &Time::HiRes::time()-$start;
# $r->print("
took $lasttime
");
@@ -7422,14 +7535,6 @@ SCANTRONFORM
return '';
}
-=pod
-
-=item scantron_upload_scantron_data
-
- Creates the screen for adding a new bubble sheet data file to a course.
-
-=cut
-
sub scantron_upload_scantron_data {
my ($r)=@_;
$r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));
@@ -7470,14 +7575,6 @@ sub scantron_upload_scantron_data {
return '';
}
-=pod
-
-=item scantron_upload_scantron_data_save
-
- Adds a provided bubble information data file to the course if user
- has the correct privileges to do so.
-
-=cut
sub scantron_upload_scantron_data_save {
my($r)=@_;
@@ -7539,14 +7636,6 @@ sub scantron_upload_scantron_data_save {
return '';
}
-=pod
-
-=item valid_file
-
- Validates that the requested bubble data file exists in the course.
-
-=cut
-
sub valid_file {
my ($requested_file)=@_;
foreach my $filename (sort(&scantron_filenames())) {
@@ -7555,16 +7644,6 @@ sub valid_file {
return 0;
}
-=pod
-
-=item scantron_download_scantron_data
-
- Shows a list of the three internal files (original, corrected,
- skipped) for a specific bubble sheet data file that exists in the
- course.
-
-=cut
-
sub scantron_download_scantron_data {
my ($r)=@_;
my $default_form_data=&defaultFormData(&get_symb($r,1));
@@ -7604,11 +7683,271 @@ sub scantron_download_scantron_data {
return '';
}
-=pod
+sub checkscantron_results {
+ my ($r) = @_;
+ my ($symb)=&get_symb($r);
+ if (!$symb) {return '';}
+ my $grading_menu_button=&show_grading_menu_form($symb);
+ my $cid = $env{'request.course.id'};
+ my %lettdig = (
+ A => 1,
+ B => 2,
+ C => 3,
+ D => 4,
+ E => 5,
+ F => 6,
+ G => 7,
+ H => 8,
+ I => 9,
+ J => 0,
+ );
+ my $numletts = scalar(keys(%lettdig));
+ my $cnum = $env{'course.'.$cid.'.num'};
+ my $cdom = $env{'course.'.$cid.'.domain'};
+ my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});
+ my %record;
+ my %scantron_config =
+ &Apache::grades::get_scantron_config($env{'form.scantron_format'});
+ my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();
+ my $classlist=&Apache::loncoursedata::get_classlist();
+ my %idmap=&Apache::grades::username_to_idmap($classlist);
+ my $navmap=Apache::lonnavmaps::navmap->new();
+ my $map=$navmap->getResourceByUrl($sequence);
+ my @resources=$navmap->retrieveResources($map,undef,1,0);
+ my (%scandata,%lastname,%bylast);
+ $r->print('
+ '.$grading_menu_button);
+ return;
+}
-=cut
#-------- end of section for handling grading scantron forms -------
#
@@ -7663,25 +8002,25 @@ sub grading_menu {
});
$fields{'command'} = 'csvform';
$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
- push (@menu, { url => $url,
+ push(@menu, { url => $url,
name => &mt('Upload Scores'),
short_description =>
&mt('Specify a file containing the class scores for current resource.')});
$fields{'command'} = 'processclicker';
$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
- push (@menu, { url => $url,
+ push(@menu, { url => $url,
name => &mt('Process Clicker'),
short_description =>
&mt('Specify a file containing the clicker information for this resource.')});
$fields{'command'} = 'scantron_selectphase';
$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
- push (@menu, { url => $url,
- name => &mt('Grade/Manage Scantron Forms'),
+ push(@menu, { url => $url,
+ name => &mt('Grade/Manage/Review Scantron Forms'),
short_description =>
- &mt('')});
+ &mt('Grade scantron exams, upload/download scantron data files, and review previously graded scantron exams.')});
$fields{'command'} = 'verify';
$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
- push (@menu, { url => "",
+ push(@menu, { url => "",
name => &mt('Verify Receipt'),
short_description =>
&mt('')});
@@ -7706,14 +8045,12 @@ sub grading_menu {
$menudata->{'url'}.'" >'.
$menudata->{'name'}."\n";
} else {
- $Str .='