--- loncom/homework/grades.pm 2011/10/10 18:27:34 1.596.2.5 +++ loncom/homework/grades.pm 2012/05/02 15:36:10 1.596.2.12.2.5 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.596.2.5 2011/10/10 18:27:34 raeburn Exp $ +# $Id: grades.pm,v 1.596.2.12.2.5 2012/05/02 15:36:10 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -272,7 +272,7 @@ sub reset_caches { } sub get_analyze { - my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed)=@_; + my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed,$bubbles_per_row)=@_; my $key = "$symb\0$uname\0$udom"; if ($type eq 'randomizetry') { if ($trial ne '') { @@ -306,6 +306,9 @@ sub reset_caches { 'grade_courseid' => $env{'request.course.id'}, 'grade_username' => $uname, 'grade_noincrement' => $no_increment); + if ($bubbles_per_row ne '') { + $form{'bubbles_per_row'} = $bubbles_per_row; + } if ($type eq 'randomizetry') { $form{'grade_questiontype'} = $type; if ($rndseed ne '') { @@ -346,7 +349,7 @@ sub reset_caches { } sub scantron_partids_tograde { - my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_; + my ($resource,$cid,$uname,$udom,$check_for_randomlist,$bubbles_per_row) = @_; my (%analysis,@parts); if (ref($resource)) { my $symb = $resource->symb(); @@ -354,7 +357,9 @@ sub reset_caches { if ($check_for_randomlist) { $add_to_form = { 'check_parts_withrandomlist' => 1,}; } - my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form); + my $analyze = + &get_analyze($symb,$uname,$udom,undef,$add_to_form, + undef,undef,undef,$bubbles_per_row); if (ref($analyze) eq 'HASH') { %analysis = %{$analyze}; } @@ -416,7 +421,7 @@ sub cleanRecord { '
@@ -6384,7 +6404,7 @@ sub scantron_warning_screen {
'.&mt('If this information is correct, please click on \'[_1]\'.',&mt($button_text)).' ".&mt("An error was detected ($error)".
- " for PaperID [_1]",
- $$scan_record{'scantron.PaperID'})." '
+ .&mt('An error was detected ([_1]) for PaperID [_2]',
+ "$error",
+ ''.$$scan_record{'scantron.PaperID'}.'')
+ ." ".&mt("An error was detected ($error)".
- " in scanline [_1]
'.&mt('Sequence to be Graded:').' '.$title.'
-'.$CODElist.'
+'.$CODElist.$lastbubblepoints.'
'.&mt('Data File that will be used:').' '.$env{'form.scantron_selectfile'}.'
[_2]
",
- $i,$line)."
".&mt("The ID on the form is [_1]
".
- "The name on the paper is [_2],[_3]",
- $$scan_record{'scantron.ID'},
- $$scan_record{'scantron.LastName'},
- $$scan_record{'scantron.FirstName'})."
' + .&mt('An error was detected ([_1]) in scanline [_2] [_3]', + "$error", $i, "
$line") + ." \n"); + } + my $message = + '
'
+ .&mt('The ID on the form is [_1]',
+ "$$scan_record{'scantron.ID'}")
+ .'
'
+ .&mt('The name on the paper is [_1], [_2]',
+ $$scan_record{'scantron.LastName'},
+ $$scan_record{'scantron.FirstName'})
+ .'
".&mt("The encoded ID is not in the classlist"). + $r->print('
'.&mt("The encoded ID is not in the classlist"). "
\n"); } elsif ($error eq 'duplicateID') { - $r->print("".&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."
\n"); + $r->print(''.&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."
\n"); } $r->print($message); $r->print("".&mt("How should I handle this?")."
\n");
@@ -7074,14 +7108,15 @@ sub scantron_get_correction {
$r->print('');
} elsif ($error =~ /CODE$/) {
if ($error eq 'incorrectCODE') {
- $r->print("
".&mt("The encoded CODE is not in the list of possible CODEs.")."
\n"); + $r->print(''.&mt("The encoded CODE is not in the list of possible CODEs.")."
\n"); } elsif ($error eq 'duplicateCODE') { - $r->print("".&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."
\n"); + $r->print(''.&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."
\n"); } - $r->print("".&mt("The CODE on the form is '[_1]'",
- $$scan_record{'scantron.CODE'})."
\n");
+ $r->print("
".&mt('The CODE on the form is [_1]', + "'$$scan_record{'scantron.CODE'}'") + ."
\n"); $r->print($message); - $r->print("".&mt("How should I handle this?")."
\n");
+ $r->print("
".&mt("How should I handle this?")."
\n"); $r->print("\n".&mt("There have been multiple bubbles scanned for some question(s)")."
\n"); + $r->print(''.&mt("There have been multiple bubbles scanned for some question(s)")."
\n"); # The form field scantron_questions is acutally a list of line numbers. # represented by this form so: @@ -7166,7 +7201,7 @@ ENDSCRIPT } $r->print(&verify_bubbles_checked(@lines_to_correct)); } elsif ($error eq 'missingbubble') { - $r->print("".&mt("There have been no bubbles scanned for some question(s)")."
\n"); + $r->print(''.&mt("There have been [_1]no[_2] bubbles scanned for some question(s)",'','')."
\n"); $r->print($message); $r->print("".&mt("Please indicate which bubble should be used for grading.")."
"); $r->print(&mt("Some questions have no scanned bubbles.")."\n"); @@ -7371,7 +7406,19 @@ sub scantron_bubble_selector { my $max=$$scan_config{'Qlength'}; my $scmode=$$scan_config{'Qon'}; - if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; } + if ($scmode eq 'number' || $scmode eq 'letter') { + if (($$scan_config{'BubblesPerRow'} =~ /^\d+$/) && + ($$scan_config{'BubblesPerRow'} > 0)) { + $max=$$scan_config{'BubblesPerRow'}; + if (($scmode eq 'number') && ($max > 10)) { + $max = 10; + } elsif (($scmode eq 'letter') && $max > 26) { + $max = 26; + } + } else { + $max = 10; + } + } my @alphabet=('A'..'Z'); $r->print(&Apache::loncommon::start_data_table(). @@ -7526,7 +7573,7 @@ sub scantron_validate_CODE { my %allcodes=&get_codes(); my $nav_error; - &scantron_get_maxbubble(\$nav_error); # parse needs the lines per response array. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the lines per response array. if ($nav_error) { $r->print(&navmap_errormsg()); return(1,$currentphase); @@ -7585,7 +7632,7 @@ sub scantron_validate_doublebubble { my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); my $nav_error; - &scantron_get_maxbubble(\$nav_error); # parse needs the bubble line array. + &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble line array. if ($nav_error) { $r->print(&navmap_errormsg()); return(1,$currentphase); @@ -7607,7 +7654,7 @@ sub scantron_validate_doublebubble { sub scantron_get_maxbubble { - my ($nav_error) = @_; + my ($nav_error,$scantron_config) = @_; if (defined($env{'form.scantron_maxbubble'}) && $env{'form.scantron_maxbubble'}) { &restore_bubble_lines(); @@ -7626,6 +7673,7 @@ sub scantron_get_maxbubble { } my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); + my $bubbles_per_row = &bubblesheet_bubbles_per_row($scantron_config); &Apache::lonxml::clear_problem_counter(); @@ -7641,7 +7689,8 @@ sub scantron_get_maxbubble { my $response_number = 0; my $bubble_line = 0; foreach my $resource (@resources) { - my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,$udom); + my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname, + $udom,$bubbles_per_row); if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) { foreach my $part_id (@{$parts}) { my $lines; @@ -7670,9 +7719,10 @@ sub scantron_get_maxbubble { if (ref($analysis->{$part_id.'.shown'}) eq 'ARRAY') { $numshown = scalar(@{$analysis->{$part_id.'.shown'}}); } - my $bubbles_per_line = 10; - my $inner_bubble_lines = int($numbub/$bubbles_per_line); - if (($numbub % $bubbles_per_line) != 0) { + my $bubbles_per_row = + &bubblesheet_bubbles_per_row($scantron_config); + my $inner_bubble_lines = int($numbub/$bubbles_per_row); + if (($numbub % $bubbles_per_row) != 0) { $inner_bubble_lines++; } for (my $i=0; $i<$numshown; $i++) { @@ -7683,7 +7733,7 @@ sub scantron_get_maxbubble { $lines = $numshown * $inner_bubble_lines; } else { $lines = $analysis->{"$part_id.bubble_lines"}; - } + } $first_bubble_line{$response_number} = $bubble_line; $bubble_lines_per_response{$response_number} = $lines; @@ -7704,6 +7754,18 @@ sub scantron_get_maxbubble { return $env{'form.scantron_maxbubble'}; } +sub bubblesheet_bubbles_per_row { + my ($scantron_config) = @_; + my $bubbles_per_row; + if (ref($scantron_config) eq 'HASH') { + $bubbles_per_row = $scantron_config->{'BubblesPerRow'}; + } + if ((!$bubbles_per_row) || ($bubbles_per_row < 1)) { + $bubbles_per_row = 10; + } + return $bubbles_per_row; +} + sub scantron_validate_missingbubbles { my ($r,$currentphase) = @_; #get student info @@ -7714,7 +7776,7 @@ sub scantron_validate_missingbubbles { my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); my ($scanlines,$scan_data)=&scantron_getfile(); my $nav_error; - my $max_bubble=&scantron_get_maxbubble(\$nav_error); + my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config); if ($nav_error) { return(1,$currentphase); } @@ -7761,6 +7823,41 @@ sub scantron_validate_missingbubbles { return (0,$currentphase+1); } +sub hand_bubble_option { + my (undef, undef, $sequence) = + &Apache::lonnet::decode_symb($env{'form.selectpage'}); + return if ($sequence eq ''); + my $navmap = Apache::lonnavmaps::navmap->new(); + unless (ref($navmap)) { + return; + } + my $needs_hand_bubbles; + my $map=$navmap->getResourceByUrl($sequence); + my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); + foreach my $res (@resources) { + if (ref($res)) { + if ($res->is_problem()) { + my $partlist = $res->parts(); + foreach my $part (@{ $partlist }) { + my @types = $res->responseType($part); + if (grep(/^(chem|essay|image|formula|math|string|functionplot)$/,@types)) { + $needs_hand_bubbles = 1; + last; + } + } + } + } + } + if ($needs_hand_bubbles) { + my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); + my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); + return &mt('The sequence to be graded contains response types which are handgraded.').''.
+ &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','
').
+ ' '.&mt('or').' '.
+ '
');
+ $r->print(' ');
if ($scancode eq '') {
- $r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2].',
+ $r->print(&mt('Mismatch grading bubblesheet for user: [_1] with ID: [_2].',
$uname.':'.$udom,$scan_record->{'scantron.ID'}));
} else {
- $r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2] and CODE: [_3].',
+ $r->print(&mt('Mismatch grading bubblesheet for user: [_1] with ID: [_2] and CODE: [_3].',
$uname.':'.$udom,$scan_record->{'scantron.ID'},$scancode));
}
$r->print('
'.&Apache::loncommon::start_data_table()."\n".
@@ -7982,11 +8083,11 @@ SCANTRONFORM
'
'.
- &mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([_4] bubblesheet lines/student).',
+ &mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([quant,_4,bubblesheet line] per student).',
'',
$numstudents,
'',
@@ -9055,7 +9165,8 @@ sub process_clicker {
my $pincorrect=&mt("Percentage points for incorrect solution");
my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',
{'iclicker' => 'i>clicker',
- 'interwrite' => 'interwrite PRS'});
+ 'interwrite' => 'interwrite PRS',
+ 'turning' => 'Turning Technologies'});
$symb = &Apache::lonenc::check_encrypt($symb);
$result.=<
'.&mt('Found [_1] question(s)',$number).'
'.
''.
&mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',
@@ -9359,6 +9473,31 @@ sub interwrite_eval {
return ($errormsg,$number);
}
+sub turning_eval {
+ my ($questiontitles,$responses)=@_;
+ my $number=0;
+ my $errormsg='';
+ foreach my $line (split(/[\n\r]/,$env{'form.upfile'})) {
+ my %components=&Apache::loncommon::record_sep($line);
+ my @entries=map {$components{$_}} (sort(keys(%components)));
+ if ($#entries>$number) { $number=$#entries; }
+ my $id=$entries[0];
+ my @idresponses;
+ $id=~s/^[\#0]+//;
+ unless ($id) { next; }
+ for (my $idx=1;$idx<=$#entries;$idx++) {
+ $entries[$idx]=~s/\,/\;/g;
+ $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+\;]+//g;
+ push(@idresponses,$entries[$idx]);
+ }
+ $$responses{$id}=join(',',@idresponses);
+ }
+ for (my $i=1; $i<=$number; $i++) {
+ $$questiontitles[$i]=&mt('Question [_1]',$i);
+ }
+ return ($errormsg,$number);
+}
+
sub assign_clicker_grades {
my ($r)=@_;
my ($symb)=&get_symb($r);
@@ -9372,9 +9511,10 @@ sub assign_clicker_grades {
# FIXME: This should probably look for the first handgradeable part
my $part=$$partlist[0];
# Start screen output
- my ($result) = &showResourceInfo($symb,$env{'form.probTitle'}).'
';
+ my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
- $result .= &Apache::loncommon::start_data_table().
+ $result .= '
'.
+ &Apache::loncommon::start_data_table().
&Apache::loncommon::start_data_table_header_row().
''.&mt('Assigning grades based on clicker file').' '.
&Apache::loncommon::end_data_table_header_row().
@@ -9562,9 +9702,12 @@ sub handler {
} else {
&init_perm();
if (!$env{'request.course.id'}) {
- # Not in a course.
- $env{'user.error.msg'}="/adm/grades::vgr:0:0:Cannot display grades page outside course context";
- return HTTP_NOT_ACCEPTABLE;
+ unless ((&Apache::lonnet::allowed('usc',$env{'request.role.domain'})) &&
+ ($command =~ /^scantronupload/)) {
+ # Not in a course.
+ $env{'user.error.msg'}="/adm/grades::vgr:0:0:Cannot display grades page outside course context";
+ return HTTP_NOT_ACCEPTABLE;
+ }
} elsif (!%perm) {
$request->internal_redirect('/adm/quickgrades');
}
@@ -9752,6 +9895,8 @@ ssi_with_retries()
calling routine should trap the error condition and display the warning
found in &navmap_errormsg().
+ $scantron_config - Reference to bubblesheet format configuration hash.
+
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()
@@ -9775,7 +9920,7 @@ ssi_with_retries()
=item scantron_process_students() :
- Routine that does the actual grading of the bubble sheet information.
+ Routine that does the actual grading of the bubblesheet information.
The parsed scanline hash is added to %env
@@ -9795,7 +9940,7 @@ ssi_with_retries()
=item scantron_upload_scantron_data() :
- Creates the screen for adding a new bubble sheet data file to a course.
+ Creates the screen for adding a new bubblesheet data file to a course.
=item scantron_upload_scantron_data_save() :
@@ -9809,7 +9954,7 @@ ssi_with_retries()
=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
+ skipped) for a specific bubblesheet data file that exists in the
course.
=item scantron_validate_ID() :