[LON-CAPA-cvs] cvs: loncom /homework grades.pm response.pm

raeburn raeburn@source.lon-capa.org
Fri, 06 Mar 2009 16:13:29 -0000


This is a MIME encoded message

--raeburn1236356009
Content-Type: text/plain

raeburn		Fri Mar  6 16:13:29 2009 EDT

  Modified files:              
    /loncom/homework	grades.pm response.pm 
  Log:
  - Verification of Scantron grading for problems containing randomlists
    - &scantron_partids_tograde() 
      - changed to use &get_analyze() to retrieve partIDs/responseIDs 
      - subroutine moved to be within scope of %analyze_cache.
      - needs to be called for each user (randomlist -> possibly different responseIDs for different users).
  
  - Robustness against changes to lonxml::counter being made by multitasking instructors.
    - scantron_questnum_start.$part.$id form element passed in ssi call for scantron grading.
      - where available, this value is used within response::getresponse() instead of lonxml::counter to determine starting column for scantron line.
  
  - Second pass (if optional inbuilt verification enabled during scantron grading).
    - Added missing call to repeat &grade_student_bubbles() when anomaly is detected during first pass.
  
  ****
  Work-in-progress - possible performance hit - perhaps check if resource includes <randomlist></randomlist> before calling &scantron_partids_tograde() for each user?
  
  
--raeburn1236356009
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20090306161329.txt"

Index: loncom/homework/grades.pm
diff -u loncom/homework/grades.pm:1.553 loncom/homework/grades.pm:1.554
--- loncom/homework/grades.pm:1.553	Tue Feb 24 17:12:19 2009
+++ loncom/homework/grades.pm	Fri Mar  6 16:13:29 2009
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.553 2009/02/24 17:12:19 biermanm 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 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
@@ -4728,7 +4750,7 @@
 }
 
 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 @@
             $env{"form.scantron.responsetype.$line"};
 	$line++;
     }
-
 }
 
 #  Given the parsed scanline, get the response for 
@@ -4778,7 +4799,6 @@
 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};
     
@@ -7243,7 +7263,7 @@
     %first_bubble_line         = ();
     %subdivided_bubble_lines   = ();
     %responsetype_per_response = ();
-  
+
     my $response_number = 0;
     my $bubble_line     = 0;
     foreach my $resource (@resources) {
@@ -7310,33 +7330,6 @@
     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
@@ -7408,15 +7401,8 @@
     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" />
@@ -7440,7 +7426,6 @@
     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.
@@ -7481,6 +7466,13 @@
  	}
   	($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);
 
@@ -7497,7 +7489,7 @@
         }
 
         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);
@@ -7515,19 +7507,32 @@
             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;
                 }
@@ -7577,18 +7582,32 @@
 }
 
 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;
 }
@@ -7761,13 +7780,7 @@
     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");
@@ -7822,9 +7835,12 @@
         ($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;
         }
@@ -7889,15 +7905,14 @@
 }
 
 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"}) {
@@ -7990,7 +8005,7 @@
                 }
             }
         }
-        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++) {
Index: loncom/homework/response.pm
diff -u loncom/homework/response.pm:1.210 loncom/homework/response.pm:1.211
--- loncom/homework/response.pm:1.210	Tue Dec 23 18:09:36 2008
+++ loncom/homework/response.pm	Fri Mar  6 16:13:29 2009
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # various response type definitons response definition
 #
-# $Id: response.pm,v 1.210 2008/12/23 18:09:36 raeburn Exp $
+# $Id: response.pm,v 1.211 2009/03/06 16:13:29 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -948,13 +948,17 @@
 	my $id    = $Apache::inputtags::response[-1];
 	
 	my $line;
+	my $startline = $env{'form.scantron_questnum_start.'.$part.'.'.$id};
+        if (!$startline) {
+            $startline = $Apache::lonxml::counter;
+        }
 	for ($line = 0; $line < $lines; $line++) {
-	    my $theline = $Apache::lonxml::counter+$offset-1+$line;
+            my $theline = $startline+$offset-1+$line;
 	    $response = $env{"scantron.$theline.answer"};
 	    if ((defined($response)) && ($response ne "") && ($response ne " ")) {
 		last;
 	    }
-	    
+ 
 	}
 
 	# save bubbled letter for later

--raeburn1236356009--