--- loncom/homework/grades.pm	2009/01/16 10:59:52	1.548
+++ loncom/homework/grades.pm	2009/03/06 16:13:29	1.554
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.548 2009/01/16 10:59:52 bisitz Exp $
+# $Id: grades.pm,v 1.554 2009/03/06 16:13:29 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -274,6 +274,28 @@ sub reset_caches {
 	    }
 	}
     }
+
+    sub scantron_partids_tograde {
+        my ($resource,$cid,$uname,$udom) = @_;
+        my (%analysis,@parts);
+        if (ref($resource)) {
+            my $symb = $resource->symb();
+            my $analyze = &get_analyze($symb,$uname,$udom);
+            if (ref($analyze) eq 'HASH') {
+                %analysis = %{$analyze};
+            }
+            if (ref($analysis{'parts'}) eq 'ARRAY') {
+                foreach my $part (@{$analysis{'parts'}}) {
+                    my ($id,$respid) = split(/\./,$part);
+                    if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
+                        push(@parts,$part);
+                    }
+                }
+            }
+        }
+        return (\%analysis,\@parts);
+    }
+
 }
 
 #--- Clean response type for display
@@ -699,7 +721,7 @@ sub verifyreceipt {
 
     my $title.=
 	'<h3><span class="LC_info">'.
-	&mt('Verifying Submission Receipt [_1]',$receipt).
+	&mt('Verifying  Receipt No. [_1]',$receipt).
 	'</span></h3>'."\n".
 	'<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}).
 	'</h4>'."\n";
@@ -2234,7 +2256,7 @@ KEYWORDS
 	    '<option>7</option><option>10</option></select>'."\n";
 	my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
 	$ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
-	$endform.=&mt('[_1]student(s)',$ntstu);
+	$endform.=&mt('[quant,_1,student]',$ntstu);
 	$endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.
 	    'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
 	    '<input type="button" value="'.&mt('Next').'" '.
@@ -3426,7 +3448,7 @@ sub editgrades {
 	    if ($part !~ m/^\Q$partid\E/) { next;}
 	    if ($type eq 'awarded' || $type eq 'solved') { next; }
 	    my $display=&Apache::lonnet::metadata($url,$stores.'.display');
-	    $display =~ s/\[Part: (\w)+\]//;
+	    $display =~ s/\[Part: \Q$part\E\]//;
             my $narrowtext = &mt('Tries');
 	    $display =~ s/Number of Attempts/$narrowtext/;
 	    $header .= '<th align="center">'.&mt('Old').' '.$display.'</th>'.
@@ -4728,7 +4750,7 @@ sub getSequenceDropDown {
 }
 
 my %bubble_lines_per_response;     # no. bubble lines for each response.
-                                   # index is "symb.part_id"
+                                   # key is zero-based index - 0, 1, 2 ...
 
 my %first_bubble_line;             # First bubble line no. for each bubble.
 
@@ -4769,7 +4791,6 @@ sub restore_bubble_lines {
             $env{"form.scantron.responsetype.$line"};
 	$line++;
     }
-
 }
 
 #  Given the parsed scanline, get the response for 
@@ -4778,7 +4799,6 @@ sub restore_bubble_lines {
 sub get_response_bubbles {
     my ($parsed_line, $response)  = @_;
 
-
     my $bubble_line = $first_bubble_line{$response-1} +1;
     my $bubble_lines= $bubble_lines_per_response{$response-1};
     
@@ -5487,7 +5507,8 @@ sub scantron_parse_scanline {
     my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;
 
     my %record;
-    my $questions=substr($line,$$scantron_config{'Qstart'}-1);  # Answers
+    my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};
+    my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff
     if (!($$scantron_config{'CODElocation'} eq 0 ||
 	  $$scantron_config{'CODElocation'} eq 'none')) {
@@ -7242,7 +7263,7 @@ sub scantron_get_maxbubble {
     %first_bubble_line         = ();
     %subdivided_bubble_lines   = ();
     %responsetype_per_response = ();
-  
+
     my $response_number = 0;
     my $bubble_line     = 0;
     foreach my $resource (@resources) {
@@ -7301,7 +7322,7 @@ sub scantron_get_maxbubble {
 	    }
         }
     }
-    &Apache::lonnet::delenv('scantron\.');
+    &Apache::lonnet::delenv('scantron.');
 
     &save_bubble_lines();
     $env{'form.scantron_maxbubble'} =
@@ -7309,33 +7330,6 @@ sub scantron_get_maxbubble {
     return $env{'form.scantron_maxbubble'};
 }
 
-sub scantron_partids_tograde {
-    my ($resource,$cid,$uname,$udom) = @_;
-    my (%analysis,@parts); 
-
-    if (ref($resource)) {
-        my $symb = $resource->symb();
-        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);
-        %analysis = &Apache::lonnet::str2hash($an);
-
-        if (ref($analysis{'parts'}) eq 'ARRAY') {
-            foreach my $part (@{$analysis{'parts'}}) {
-                my ($id,$respid) = split(/\./,$part);
-                if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
-                    push(@parts,$part);
-                }
-            }
-        }
-    }
-    return (\%analysis,\@parts);
-}
-
 sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;
     #get student info
@@ -7407,15 +7401,8 @@ sub scantron_process_students {
     my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
-
-    my ($uname,$udom,%partids_by_symb);
-    foreach my $resource (@resources) {
-        my $ressymb = $resource->symb(); 
-        my ($analysis,$parts) = 
-            &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
-        $partids_by_symb{$ressymb} = $parts;
-    }
 #    $r->print("geto ".scalar(@resources)."<br />");
+    my ($uname,$udom);
     my $result= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
   <input type="hidden" name="command" value="scantron_configphase" />
@@ -7439,7 +7426,6 @@ SCANTRONFORM
     my $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.
@@ -7480,6 +7466,13 @@ SCANTRONFORM
  	}
   	($uname,$udom)=split(/:/,$uname);
 
+        my %partids_by_symb;
+        foreach my $resource (@resources) {
+            my $ressymb = $resource->symb();
+            my ($analysis,$parts) =
+                &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);            $partids_by_symb{$ressymb} = $parts;
+        }
+
 	&Apache::lonxml::clear_problem_counter();
   	&Apache::lonnet::appenv($scan_record);
 
@@ -7496,7 +7489,7 @@ SCANTRONFORM
         }
 
         if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
-                                   @resources) eq 'ssi_error') {
+                                   \@resources,\%partids_by_symb) eq 'ssi_error') {
             $ssi_error = 0; # So end of handler error message does not trigger.
             $r->print("</form>");
             &ssi_print_error($r);
@@ -7514,19 +7507,32 @@ SCANTRONFORM
             my $studentrecord = '';
             my $counter = -1;
             foreach my $resource (@resources) {
+                my $ressymb = $resource->symb();
                 ($counter,my $recording) =
                     &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
-                                             $counter,$studentdata,\%partids_by_symb,
+                                             $counter,$studentdata,$partids_by_symb{$ressymb},
                                              \%scantron_config,\%lettdig,$numletts);
                 $studentrecord .= $recording;
             }
             if ($studentrecord ne $studentdata) {
+                &Apache::lonxml::clear_problem_counter();
+                if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
+                                           \@resources,\%partids_by_symb) eq 'ssi_error') {
+                    $ssi_error = 0; # So end of handler error message does not trigger.
+                    $r->print("</form>");
+                    &ssi_print_error($r);
+                    $r->print(&show_grading_menu_form($symb));
+                    &Apache::lonnet::remove_lock($lock);
+                    delete($completedstudents{$uname});
+                    return '';
+                }
                 $counter = -1;
                 $studentrecord = '';
                 foreach my $resource (@resources) {
+                    my $ressymb = $resource->symb();
                     ($counter,my $recording) =
                         &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
-                                                 $counter,$studentdata,\%partids_by_symb,
+                                                 $counter,$studentdata,$partids_by_symb{$ressymb},
                                                  \%scantron_config,\%lettdig,$numletts);
                     $studentrecord .= $recording;
                 }
@@ -7563,7 +7569,7 @@ SCANTRONFORM
         if (&Apache::loncommon::connection_aborted($r)) { last; }
     } continue {
 	&Apache::lonxml::clear_problem_counter();
-	&Apache::lonnet::delenv('scantron\.');
+	&Apache::lonnet::delenv('scantron.');
     }
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
     &Apache::lonnet::remove_lock($lock);
@@ -7576,18 +7582,32 @@ SCANTRONFORM
 }
 
 sub grade_student_bubbles {
-    my ($r,$uname,$udom,$scan_record,$scancode,@resources) = @_;
-    foreach my $resource (@resources) {
-        my %form = ('submitted'     => 'scantron',
-                    'grade_target'  => 'grade',
-                    'grade_username'=> $uname,
-                    'grade_domain'  => $udom,
-                    'grade_courseid'=> $env{'request.course.id'},
-                    'grade_symb'    => $resource->symb(),
-                    'CODE'          => $scancode);
-        my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
-        return 'ssi_error' if ($ssi_error);
-        last if (&Apache::loncommon::connection_aborted($r));
+    my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts) = @_;
+    if (ref($resources) eq 'ARRAY') {
+        my $count = 0;
+        foreach my $resource (@{$resources}) {
+            my $ressymb = $resource->symb();
+            my %form = ('submitted'      => 'scantron',
+                        'grade_target'   => 'grade',
+                        'grade_username' => $uname,
+                        'grade_domain'   => $udom,
+                        'grade_courseid' => $env{'request.course.id'},
+                        'grade_symb'     => $ressymb,
+                        'CODE'           => $scancode
+                       );
+            if (ref($parts) eq 'HASH') {
+                if (ref($parts->{$ressymb}) eq 'ARRAY') {
+                    foreach my $part (@{$parts->{$ressymb}}) {
+                        $form{'scantron_questnum_start.'.$part} =
+                            1+$env{'form.scantron.first_bubble_line.'.$count};
+                        $count++;
+                    }
+                }
+            }
+            my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
+            return 'ssi_error' if ($ssi_error);
+            last if (&Apache::loncommon::connection_aborted($r));
+        }
     }
     return;
 }
@@ -7760,13 +7780,7 @@ sub checkscantron_results {
     my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,undef,1,0);
-    my ($uname,$udom,%partids_by_symb);
-    foreach my $resource (@resources) {
-        my $ressymb = $resource->symb();
-        my ($analysis,$parts) =
-            &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
-        $partids_by_symb{$ressymb} = $parts;
-    }
+    my ($uname,$udom);
     my (%scandata,%lastname,%bylast);
     $r->print('
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n");
@@ -7821,9 +7835,12 @@ sub checkscantron_results {
         ($username,$domain)=split(/:/,$uname);
         my $counter = -1;
         foreach my $resource (@resources) {
+            my $ressymb = $resource->symb();
+            my ($analysis,$parts) =
+                &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain);
             ($counter,my $recording) =
                 &verify_scantron_grading($resource,$domain,$username,$cid,$counter,
-                                         $scandata{$pid},\%partids_by_symb,
+                                         $scandata{$pid},$parts,
                                          \%scantron_config,\%lettdig,$numletts);
             $record{$pid} .= $recording;
         }
@@ -7888,15 +7905,14 @@ sub checkscantron_results {
 }
 
 sub verify_scantron_grading {
-    my ($resource,$domain,$username,$cid,$counter,$scandata,$partids_by_symb,
+    my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,
         $scantron_config,$lettdig,$numletts) = @_;
     my ($record,%expected,%startpos);
     return ($counter,$record) if (!ref($resource));
     return ($counter,$record) if (!$resource->is_problem());
     my $symb = $resource->symb();
-    return ($counter,$record) if (ref($partids_by_symb) ne 'HASH');
-    return ($counter,$record) if (ref($partids_by_symb->{$symb}) ne 'ARRAY');
-    foreach my $part_id (@{$partids_by_symb->{$symb}}) {
+    return ($counter,$record) if (ref($partids) ne 'ARRAY');
+    foreach my $part_id (@{$partids}) {
         $counter ++;
         $expected{$part_id} = 0;
         if ($env{"form.scantron.sub_bubblelines.$counter"}) {
@@ -7989,7 +8005,7 @@ sub verify_scantron_grading {
                 }
             }
         }
-        foreach my $part_id (@{$partids_by_symb->{$symb}}) {
+        foreach my $part_id (@{$partids}) {
             if ($recorded{$part_id} eq '') {
                 for (my $i=0; $i<$expected{$part_id}; $i++) {
                     for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
@@ -8864,7 +8880,7 @@ ENDHEADER
        }
     }
 # We are done
-    $result.='<br />'.&mt('Successfully stored grades for [_1] student(s).',$storecount).
+    $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).
              '</td></tr></table>'."\n".
              '</td></tr></table><br /><br />'."\n";
     return $result.&show_grading_menu_form($symb);