[LON-CAPA-cvs] cvs: loncom /homework grades.pm matchresponse.pm optionresponse.pm radiobuttonresponse.pm rankresponse.pm response.pm /interface lonprintout.pm

raeburn raeburn at source.lon-capa.org
Tue Sep 13 17:43:03 EDT 2011


raeburn		Tue Sep 13 21:43:03 2011 EDT

  Modified files:              
    /loncom/homework	grades.pm radiobuttonresponse.pm rankresponse.pm 
                    	optionresponse.pm matchresponse.pm response.pm 
    /loncom/interface	lonprintout.pm 
  Log:
  - Additional (eighteenth field) BubblesPerRow in Bubblesheet Format file.
    - specify bubbles available for each question row (default is 10).
  - Support case where question (or subquestion for optionresponse, 
      matchresponse, or rankresponse) requires more bubbles than in one line.
    - Students prompted to bubble once in multiple lines.
    - Numbering recognizes multiple lines per question.
    - Also supports: imageresponse, stringresponse, formularesponse where
      students leave link(s) blank if weight assigned is greater than
      bubbles per line -- multiple lines also supported in this case.
  
  For example:
  - 15-bubble MSU Art History exams now supported on standard 10-bubble 
      bubblesheets used at MSU. 
  
  
-------------- next part --------------
Index: loncom/homework/grades.pm
diff -u loncom/homework/grades.pm:1.648 loncom/homework/grades.pm:1.649
--- loncom/homework/grades.pm:1.648	Fri May 27 14:39:50 2011
+++ loncom/homework/grades.pm	Tue Sep 13 21:42:58 2011
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.648 2011/05/27 14:39:50 bisitz Exp $
+# $Id: grades.pm,v 1.649 2011/09/13 21:42:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -213,7 +213,7 @@
     }
 
     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 '') {
@@ -247,6 +247,9 @@
                     '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 '') {
@@ -287,7 +290,7 @@
     }
 
     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, at parts);
         if (ref($resource)) {
             my $symb = $resource->symb();
@@ -295,7 +298,9 @@
             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};
             }
@@ -5350,7 +5355,8 @@
  
       LastName    - column that the last name starts in
       LastNameLength - number of columns that the last name spans
-
+      BubblesPerRow - number of bubbles available in each row used to 
+                      bubble an answer. (If not specified, 10 assumed).
 =cut
 
 sub get_scantron_config {
@@ -5380,6 +5386,7 @@
 	$config{'FirstNamelength'}=$config[14];
 	$config{'LastName'}=$config[15];
 	$config{'LastNamelength'}=$config[16];
+        $config{'BubblesPerRow'}=$config[17];
 	last;
     }
     return %config;
@@ -6301,7 +6308,8 @@
     #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $nav_error;
-    my $max_bubble=&scantron_get_maxbubble(\$nav_error);
+    my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+    my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {
         $r->print(&navmap_errormsg());
         return '';
@@ -6754,7 +6762,7 @@
     my ($scanlines,$scan_data)=&scantron_getfile();
 
     my $nav_error;
-    &scantron_get_maxbubble(\$nav_error); # parse needs the bubble_lines.. array.
+    &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble_lines.. array.
     if ($nav_error) {
         $r->print(&navmap_errormsg());
         return(1,$currentphase);
@@ -7153,7 +7161,19 @@
     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().
@@ -7308,7 +7328,7 @@
     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);
@@ -7367,7 +7387,7 @@
     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);
@@ -7389,7 +7409,7 @@
 
 
 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();
@@ -7408,6 +7428,7 @@
     }
     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();
 
@@ -7423,7 +7444,7 @@
     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,undef,$bubbles_per_row);
         if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
 	    foreach my $part_id (@{$parts}) {
                 my $lines;
@@ -7452,9 +7473,10 @@
                     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++) {
@@ -7465,7 +7487,7 @@
                     $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;
@@ -7486,6 +7508,18 @@
     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
@@ -7496,7 +7530,7 @@
     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);
     }
@@ -7554,6 +7588,8 @@
     my $default_form_data=&defaultFormData($symb);
 
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
+    my $bubbles_per_row =
+        &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);
@@ -7566,7 +7602,7 @@
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     my (%grader_partids_by_symb,%grader_randomlists_by_symb);
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,
-                            \%grader_randomlists_by_symb);
+                            \%grader_randomlists_by_symb,$bubbles_per_row);
     my $resource_error;
     foreach my $resource (@resources) {
         my $ressymb;
@@ -7578,7 +7614,7 @@
         }
         my ($analysis,$parts) =
             &scantron_partids_tograde($resource,$env{'request.course.id'},
-                                      $env{'user.name'},$env{'user.domain'},1);
+                                      $env{'user.name'},$env{'user.domain'},1,$bubbles_per_row);
         $grader_partids_by_symb{$ressymb} = $parts;
         if (ref($analysis) eq 'HASH') {
             if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
@@ -7616,7 +7652,7 @@
     my $started;
 
     my $nav_error;
-    &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.
+    &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     if ($nav_error) {
         $r->print(&navmap_errormsg());
         return '';
@@ -7672,7 +7708,7 @@
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                 my ($analysis,$parts) =
-                    &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
+                    &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom,undef,$bubbles_per_row);
                 $partids_by_symb{$ressymb} = $parts;
             } else {
                 $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
@@ -7701,7 +7737,8 @@
         }
 
         if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
-                                   \@resources,\%partids_by_symb) eq 'ssi_error') {
+                                   \@resources,\%partids_by_symb,
+                                   $bubbles_per_row) eq 'ssi_error') {
             $ssi_error = 0; # So end of handler error message does not trigger.
             $r->print("</form>");
             &ssi_print_error($r);
@@ -7728,7 +7765,8 @@
             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') {
+                                           \@resources,\%partids_by_symb,
+                                           $bubbles_per_row) eq 'ssi_error') {
                     $ssi_error = 0; # So end of handler error message does not trigger.
                     $r->print("</form>");
                     &ssi_print_error($r);
@@ -7791,14 +7829,15 @@
 }
 
 sub graders_resources_pass {
-    my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb) = @_;
+    my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb,
+        $bubbles_per_row) = @_;
     if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) && 
         (ref($grader_randomlists_by_symb) eq 'HASH')) {
         foreach my $resource (@{$resources}) {
             my $ressymb = $resource->symb();
             my ($analysis,$parts) =
                 &scantron_partids_tograde($resource,$env{'request.course.id'},
-                                          $env{'user.name'},$env{'user.domain'},1);
+                                          $env{'user.name'},$env{'user.domain'},1,$bubbles_per_row);
             $grader_partids_by_symb->{$ressymb} = $parts;
             if (ref($analysis) eq 'HASH') {
                 if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
@@ -7812,7 +7851,8 @@
 }
 
 sub grade_student_bubbles {
-    my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts) = @_;
+    my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts,$bubbles_per_row) = @_;
+# Walk folder as student here to get resources in order student sees.
     if (ref($resources) eq 'ARRAY') {
         my $count = 0;
         foreach my $resource (@{$resources}) {
@@ -7825,6 +7865,9 @@
                         'grade_symb'     => $ressymb,
                         'CODE'           => $scancode
                        );
+            if ($bubbles_per_row ne '') {
+                $form{'bubbles_per_row'} = $bubbles_per_row;
+            }
             if (ref($parts) eq 'HASH') {
                 if (ref($parts->{$ressymb}) eq 'ARRAY') {
                     foreach my $part (@{$parts->{$ressymb}}) {
@@ -8100,6 +8143,7 @@
     my %record;
     my %scantron_config =
         &Apache::grades::get_scantron_config($env{'form.scantron_format'});
+    my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);
@@ -8127,7 +8171,7 @@
                                     'inline',undef,'checkscantron');
     my ($username,$domain,$started);
     my $nav_error;
-    &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.
+    &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     if ($nav_error) {
         $r->print(&navmap_errormsg());
         return '';
@@ -8177,7 +8221,7 @@
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                 (my $analysis,$parts) =
-                    &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain);
+                    &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain,undef,$bubbles_per_row);
             } else {
                 $parts = $grader_partids_by_symb{$ressymb};
             }
@@ -9517,6 +9561,8 @@
        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()
Index: loncom/homework/radiobuttonresponse.pm
diff -u loncom/homework/radiobuttonresponse.pm:1.150 loncom/homework/radiobuttonresponse.pm:1.151
--- loncom/homework/radiobuttonresponse.pm:1.150	Fri Aug 26 22:40:17 2011
+++ loncom/homework/radiobuttonresponse.pm	Tue Sep 13 21:42:58 2011
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # mutliple choice style responses
 #
-# $Id: radiobuttonresponse.pm,v 1.150 2011/08/26 22:40:17 raeburn Exp $
+# $Id: radiobuttonresponse.pm,v 1.151 2011/09/13 21:42:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -138,14 +138,10 @@
 %Apache::response::foilgroup=();
 sub start_foilgroup {
     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
-    my $result;
     %Apache::response::foilgroup=();
     $Apache::radiobuttonresponse::conceptgroup=0;
     &Apache::response::pushrandomnumber(undef,$target);
-    if ($target eq 'tex' && $Apache::lonhomework::type eq 'exam') {
-	$result.='\item[\textbf{'.$Apache::lonxml::counter.'}.]';
-    }
-    return $result;
+    return;
 }
 
 sub storesurvey {
@@ -222,15 +218,10 @@
 
     my $result;
     my $bubble_lines;
-    my $bubbles_per_line;
     my $answer_count;
     my $id   = $Apache::inputtags::response['-1'];
     my $part = $Apache::inputtags::part;
-    $bubbles_per_line = 
-	&Apache::response::get_response_param($Apache::inputtags::part."_$id",
-					      'numbubbles',
-					      $default_bubbles_per_line);
-
+    my $bubbles_per_line = &getbubblesnum($part,$id);
 
     if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' ||
 	$target eq 'tex' || $target eq 'analyze') {
@@ -260,7 +251,7 @@
 	    $answer_count = scalar(@shown);
 
 	    if ($target eq 'web' || $target eq 'tex') {
-		$result=&displayfoils($target,
+                $result=&displayfoils($target,
 				      $answer, \@shown,
 				      $direction,
 				      $bubbles_per_line);
@@ -295,6 +286,20 @@
     return $result;
 }
 
+sub getbubblesnum {
+    my ($part,$id) = @_;
+    my $bubbles_per_line;
+    my $default_numbubbles = $default_bubbles_per_line;
+    if (($env{'form.bubbles_per_row'} =~ /^\d+$/) &&
+        ($env{'form.bubbles_per_row'} > 0)) {
+        $default_numbubbles = $env{'form.bubbles_per_row'};
+    }
+    $bubbles_per_line =
+        &Apache::response::get_response_param($part."_$id",'numbubbles',
+                                              $default_numbubbles);
+    return $bubbles_per_line;
+}
+
 sub getfoilcounts {
     my @names;
     my $truecnt=0;
@@ -648,6 +653,30 @@
 	if ($target ne 'tex' && $direction eq 'horizontal') {
 	    $result.="<table><tr>";
 	}
+        my $numlines;
+        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+            my $numitems = scalar(@{ $whichfoils });
+            $numlines = int($numitems/$bubbles_per_line);
+            if (($numitems % $bubbles_per_line) != 0) {
+                $numlines ++;
+            }
+            if ($numlines < 1) {
+                $numlines = 1;
+            }
+            if ($numlines > 1) {
+                my $linetext;
+                for (my $i=0; $i<$numlines; $i++) {
+                    $linetext .= $Apache::lonxml::counter+$i.', ';
+                }
+                $linetext =~ s/,\s$//;
+                $result .= '\item[\small {\textbf{'.$linetext.'}}]'.
+                           ' {\footnotesize '.
+                           &mt('(Bubble once in [_1] lines)',$numlines).
+                           '} \hspace*{\fill} \\\\';
+            } else {
+                $result .= '\item[\textbf{'.$Apache::lonxml::counter.'}.]';
+            }
+        }
 	foreach my $name (@{ $whichfoils }) {
 	    if ($target ne 'tex') {
 		if ($direction eq 'horizontal') {
@@ -671,9 +700,12 @@
 			$line++;
 			$i = 0;
 			$bubble_number = 0;
-			$result.='\item[\textbf{'.($Apache::lonxml::counter+$line).'}.]';
 		    }
-		    $result .= '{\small \textbf{'.$alphabet[$i].'}}$\bigcirc$'.$Apache::response::foilgroup{$name.'.text'}.'\\\\';  #' stupid emacs
+                    my $identifier;
+                    if ($numlines > 1) {
+                        $identifier = $Apache::lonxml::counter+$line;
+                    }
+                    $result .= '{\small \textbf{'.$identifier.$alphabet[$i].'}}$\bigcirc$'.$Apache::response::foilgroup{$name.'.text'}.'\\\\';  #' stupid emacs
 		    $i++;
 		    $bubble_number++;
 		} else {
@@ -852,6 +884,7 @@
 <endouttext />
 </foil>';
 }
+
 1;
 __END__
 
Index: loncom/homework/rankresponse.pm
diff -u loncom/homework/rankresponse.pm:1.65 loncom/homework/rankresponse.pm:1.66
--- loncom/homework/rankresponse.pm:1.65	Tue Jun  7 17:27:37 2011
+++ loncom/homework/rankresponse.pm	Tue Sep 13 21:42:58 2011
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # rank style response
 #
-# $Id: rankresponse.pm,v 1.65 2011/06/07 17:27:37 www Exp $
+# $Id: rankresponse.pm,v 1.66 2011/09/13 21:42:58 raeburn Exp $
 # Copyright Michigan State University Board of Trustees
 #
 # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
@@ -127,8 +127,17 @@
 	}
 	my $part = $Apache::inputtags::part;
 	my $id   = $Apache::inputtags::response[-1];
-	&Apache::lonxml::increment_counter(&getfoilcounts($max),
-					   "$part.$id");
+        my ($numrows,$bubbles_per_row);
+        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+            my (@whichfoils)=&whichfoils($max,$randomize);
+            ($numrows,$bubbles_per_row) =
+                &Apache::optionresponse::getnumrows(scalar(@whichfoils));
+        }
+        if ($numrows < 1) {
+            $numrows = 1;
+        }
+        my $increment = &getfoilcounts($max) * $numrows;
+	&Apache::lonxml::increment_counter($increment,"$part.$id");
 	if ($target eq 'analyze') {
 	    &Apache::lonhomework::set_bubble_lines();
 	}
@@ -227,8 +236,34 @@
     my %grade;
     my ($temp,$right,$wrong,$ignored)=(1,0,0,0);
     my @correctorder=&get_correct_order($tol, at whichfoils);
+    my ($numrows,$bubbles_per_row);
+    if ($Apache::lonhomework::scantronmode) {
+        my $numitems = scalar(@whichfoils);
+        ($numrows,$bubbles_per_row) =
+            &Apache::optionresponse::getnumrows($numitems);
+    }
+    if ($numrows < 1) {
+        $numrows = 1;
+    }
+
     foreach my $name (@whichfoils) {
-	my $response = &Apache::response::getresponse($temp,'A is 1');
+        my $response;
+        if ($numrows > 1) {
+            my $num = $temp;
+            my $totalnum;
+            for (my $i=0; $i<$numrows; $i++) {
+                my $item = &Apache::response::getresponse($num,'A is 1');
+                if ($item =~ /^\d+$/) {
+                    $totalnum = $i*$bubbles_per_row + $item;
+                }
+                $num ++;
+            }
+            $response = $totalnum;
+            $temp += $numrows;
+        } else {
+            $response = &Apache::response::getresponse($temp,'A is 1');
+            $temp ++;
+        }
 	my $value=shift(@correctorder);
 	if ( $response =~ /[^\s]/) {
 	    $responsehash{$name}=$response;
@@ -241,7 +276,6 @@
 	} else {
 	    $ignored++;
 	}
-	$temp++;
     }
     my $malformed=&check_response_order(%responsehash);
     my $part=$Apache::inputtags::part;
@@ -346,6 +380,13 @@
 	my %lastresponse=&Apache::lonnet::str2hash($lastresponse); 
 	my @alp = splice @alphabet, 0, $#whichopt + 1;
 	my $internal_counter=$Apache::lonxml::counter;
+        my ($numrows,$bubbles_per_row);
+        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+            ($numrows,$bubbles_per_row) = 
+                &Apache::optionresponse::getnumrows(scalar(@alp));
+        } else {
+            $numrows = 1;
+        }
         if($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes') {
             $result .= '\strut \\\\ \strut \\\\' ;
         }
@@ -398,12 +439,26 @@
 		}
 	    } else {
 		if ($Apache::lonhomework::type eq 'exam') {
+                    my $itemlabel;
+                    if ($numrows == 1) {
+                        $itemlabel = '\item[\textbf{'.$internal_counter.'}]';
+                    } else {
+                        my $linetext;
+                        for (my $i=0; $i<$numrows; $i++) {
+                            $linetext .= $internal_counter+$i.', ';
+                        }
+                        $linetext =~ s/,\s$//;
+                        $itemlabel = '\item[\small {\textbf{'.$linetext.'}}]'.
+                                     ' {\footnotesize '.
+                                     &mt('(Bubble once in [_1] lines)',$numrows).
+                                     '} \hspace*{\fill} \\\\';
+                    }
 		    $result .= '\vskip 0 mm '.$text.' \vskip 0 mm '."\n";
 		    $result .= '\vskip -1 mm';
-		    $result .= '\begin{enumerate}\item[\textbf{'.$internal_counter.'}.]';
-		    $result .= &Apache::optionresponse::bubbles(\@alp,\@whichopt,'rankresponse');
-		    $result .= '\end{enumerate} \vskip -8 mm \strut ';
-		    $internal_counter++;
+                    $result .= '\begin{enumerate}'.$itemlabel;
+		    $result .= &Apache::optionresponse::bubbles(\@alp,\@whichopt,'rankresponse',undef,$numrows,$bubbles_per_row,$internal_counter);
+                    $result .= '\end{enumerate} \vskip -8 mm \strut ';
+		    $internal_counter += $numrows;
 		} else {
                     if($env{'form.pdfFormFields'} ne 'yes') {
 		        $result.=' \vskip 0mm \framebox[5 mm][s]{\tiny\strut} '.$text."\n";
Index: loncom/homework/optionresponse.pm
diff -u loncom/homework/optionresponse.pm:1.176 loncom/homework/optionresponse.pm:1.177
--- loncom/homework/optionresponse.pm:1.176	Tue Jun  7 17:27:37 2011
+++ loncom/homework/optionresponse.pm	Tue Sep 13 21:42:58 2011
@@ -1,7 +1,7 @@
 # LearningOnline Network with CAPA
 # option list style responses
 #
-# $Id: optionresponse.pm,v 1.176 2011/06/07 17:27:37 www Exp $
+# $Id: optionresponse.pm,v 1.177 2011/09/13 21:42:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -252,11 +252,41 @@
 	my $right=0;
 	my $wrong=0;
 	my $ignored=0;
+        my ($numrows,$bubbles_per_row);
+        if ($Apache::lonhomework::scantronmode) {
+            my $numitems = scalar(@opt);
+            ($numrows,$bubbles_per_row) =
+                &Apache::optionresponse::getnumrows($numitems);
+        }
+        if ($numrows < 1) {
+            $numrows = 1;
+        }
 	foreach $name (@whichopt) {
-	  my $response=&Apache::response::getresponse($temp);
-	  if ($env{'form.submitted'} eq 'scantron' && $response=~/\S/) {
-	      $response = $opt[$response];
-	  }
+	  my $response;
+          if ($env{'form.submitted'} eq 'scantron') {
+              if ($numrows > 1) {
+                  my $num = $temp;
+                  my $totalnum;
+                  for (my $i=0; $i<$numrows; $i++) {
+                      my $item = &Apache::response::getresponse($num);
+                      if ($item =~ /^\d+$/) {
+                          $totalnum = $i*$bubbles_per_row + $item;
+                      }
+                      $num ++;
+                  }
+                  if ($totalnum =~ /^\d+$/) {
+                      $response = $opt[$totalnum];
+                  }
+                  $temp += $numrows;
+              } else {
+                  if ($response=~/\S/) {
+                      $response = $opt[$response];
+                  }
+                  $temp ++;
+              }
+	  } else {
+              $temp ++;
+          }
 	  if ( $response =~ /[^\s]/) {
 	    $responsehash{$name}=$response;
 	    my $value=$Apache::response::foilgroup{$name.'.value'};
@@ -269,7 +299,6 @@
 	  } else {
 	    $ignored++;
 	  }
-	  $temp++;
 	}
 	my $part=$Apache::inputtags::part;
 	my $id = $Apache::inputtags::response['-1'];
@@ -347,8 +376,16 @@
     }
     my $part_id     = $Apache::inputtags::part;
     my $response_id = $Apache::inputtags::response[-1];
-    &Apache::lonxml::increment_counter(&getfoilcounts($max),
-				       "$part_id.$response_id");
+    my ($numrows,$bubbles_per_row);
+    if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+        ($numrows,$bubbles_per_row) =
+            &Apache::optionresponse::getnumrows(scalar(@opt));
+    }
+    if ($numrows < 1) {
+        $numrows = 1;
+    }
+    my $increment = &getfoilcounts($max) * $numrows;
+    &Apache::lonxml::increment_counter($increment,"$part_id.$response_id");
     if ($target eq 'analyze') {
 	&Apache::lonhomework::set_bubble_lines();
     }
@@ -631,9 +668,24 @@
 	  }
 	  if ($Apache::lonhomework::type eq 'exam') {
 	      $result.='\vskip -1 mm\noindent';
-              $result.= '\textbf{'. $internal_counter.'}. \vskip -3mm'.&bubbles(\@alphabet,\@opt).
-		                 ' \strut ';
-	      $internal_counter++;
+              my ($numrows,$bubbles_per_row) = &getnumrows(scalar(@opt)); 
+              if ($numrows == 1) {  
+                  $result .= '\textbf{'.$internal_counter.'}. \vskip -3mm';
+              } else {
+                  my $linetext;
+                  for (my $i=0; $i<$numrows; $i++) {
+                      $linetext .= $internal_counter+$i.', ';
+                  }
+                  $linetext =~ s/,\s$//;
+                  $result .= '\small {\textbf{'.$linetext.'}} '.
+                             '\hskip 2 mm {\footnotesize '.
+                             &mt('(Bubble once in [_1] lines)',$numrows).
+                             '} \vskip 1 mm';
+              }
+              $result.= &bubbles(\@alphabet,\@opt,undef,undef,$numrows,
+                                 $bubbles_per_row,$internal_counter).
+                        ' \strut ';
+	      $internal_counter += $numrows;
 	  }
           if ($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes'
               && $Apache::inputtags::status[-1] eq 'CAN_ANSWER'
@@ -714,7 +766,8 @@
 
 
 sub bubbles {
-    my ($ralphabet,$ropt,$response, $max_width) = @_;
+    my ($ralphabet,$ropt,$response,$max_width,$numrows,$bubbles_per_row,
+        $internal_counter) = @_;
     my @alphabet = @$ralphabet;
     my @opt = @$ropt;
     my ($result,$head,$line) =('','','');
@@ -735,28 +788,37 @@
     }
     &Apache::lonxml::debug("Final maxwidth: $textwidth");
     for (my $ind=0;$ind<=$number_of_bubbles;$ind++) {
+        my $item;
+        if ($numrows > 1) {
+            my $num = $internal_counter+int($ind/$bubbles_per_row);
+            my $idx = int($ind % $bubbles_per_row);
+            $item = $num.$alphabet[$idx];
+        } else {
+            $item = $alphabet[$ind];
+        }
 	my $leftmargin;
 	$opt[$ind]=&Apache::lonxml::latex_special_symbols($opt[$ind]);
 	if ($response eq 'rankresponse') {$opt[$ind]='Rank '.$opt[$ind];}
 	if ($ind==0) {$leftmargin=6;} else {$leftmargin=10;}
 
-	$current_length += (length($opt[$ind])+length($alphabet[$ind])+4)*2;
+	$current_length += (length($opt[$ind])+length($item)+4)*2;
+        
 	if ($current_length<($textwidth-$leftmargin) and $ind!=$number_of_bubbles) {
-	    $line.='\hskip 4 mm {\small \textbf{'.$alphabet[$ind].'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';
+            
+	    $line.='\hskip 4 mm {\small \textbf{'.$item.'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';
 	    $head.='lr';
 	} else {
 	    $line=~s/\&\s*$//;
 	    $result.='\vskip -1 mm\noindent\setlength{\tabcolsep}{2 mm}\renewcommand{\arraystretch}{1.25}\begin{tabular}{'.$head.'}'.$line.'\\\\\end{tabular}\vskip 0 mm';
-	    $line = '\hskip 4 mm {\small \textbf{'.$alphabet[$ind].'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';;
+	    $line = '\hskip 4 mm {\small \textbf{'.$item.'}}$\bigcirc$\hskip -1 mm & \hskip -3 mm {\small '.$opt[$ind].'} & ';
 	    $head ='lr';
-	    $current_length = (length($opt[$ind])+length($alphabet[$ind]))*2;
+	    $current_length = (length($opt[$ind])+length($item))*2;
 	}
 
     }
     return $result;
 }
 
-
 sub start_conceptgroup {
   my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
   $Apache::optionresponse::conceptgroup=1;
@@ -904,6 +966,24 @@
 sub insert_drawoptionlist {
     return '<drawoptionlist />';
 }
+
+sub getnumrows {
+    my ($numitems) = @_;
+    my $bubbles_per_row;
+    my $default_numbubbles = 10;
+    if (($env{'form.bubbles_per_row'} =~ /^\d+$/) &&
+        ($env{'form.bubbles_per_row'} > 0)) {
+        $bubbles_per_row = $env{'form.bubbles_per_row'};
+    } else {
+        $bubbles_per_row = $default_numbubbles;
+    }
+    my $numrows = int ($numitems/$bubbles_per_row);
+    if (($numitems % $bubbles_per_row) != 0) {
+        $numrows ++;
+    }
+    return ($numrows,$bubbles_per_row);
+}
+
 1;
 __END__
  
Index: loncom/homework/matchresponse.pm
diff -u loncom/homework/matchresponse.pm:1.81 loncom/homework/matchresponse.pm:1.82
--- loncom/homework/matchresponse.pm:1.81	Sun Dec 19 00:54:20 2010
+++ loncom/homework/matchresponse.pm	Tue Sep 13 21:42:58 2011
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Full matching style response
 #
-# $Id: matchresponse.pm,v 1.81 2010/12/19 00:54:20 raeburn Exp $
+# $Id: matchresponse.pm,v 1.82 2011/09/13 21:42:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -173,9 +173,31 @@
     } elsif ($target eq 'tex') {
 	my $table=' \begin{description}\setlength{\leftmargin}{2em}\setlength{\labelwidth}{1em}\setlength{\itemsep}{0.5pt plus1pt minus2pt}\setlength{\listparindent}{0em} ';
 	my $i=0;
+        my ($numrows,$bubbles_per_row);
+        if ($Apache::lonhomework::type eq 'exam') {
+            ($numrows,$bubbles_per_row) = 
+                &Apache::optionresponse::getnumrows(scalar(@names)); 
+        } else {
+            $numrows = 1;
+        }
 	foreach my $name (@names) {
 	    # $Apache::response::itemgroup{$name.'.text'}=~s/\$\$/\$/g;
-	    $table.='\item['.$alphabet[$i].'] '.
+            my $item;
+            if (($numrows > 1) && ($bubbles_per_row > 0)) {
+                my $num = 1+int($i/$bubbles_per_row);
+                my $idx = int($i % $bubbles_per_row);
+                if ($idx == 0) {
+                    if ($num == 1) {
+                        $table .= '\item[\footnotesize {'.&mt('(first line)').'}]';
+                    } else {
+                        $table .= '\item[\footnotesize {'.&mt('(next line)').'}]';
+                    }
+                }
+                $item = $alphabet[$idx];
+            } else {
+                $item = $alphabet[$i];
+            }
+	    $table.='\item['.$item.'] '.
 		$Apache::response::itemgroup{$name.'.text'};
 	    $i++;
 	}
@@ -288,8 +310,20 @@
 						 ['text','value','location']);
 	    #FIXME need to store options in some way
 	}
-	&Apache::lonxml::increment_counter(&getfoilcounts($max), 
-					   "$part.$response_id");
+        my ($numrows,$bubbles_per_row);
+        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+            my $numitems;
+            if (ref($Apache::response::itemgroup{'names'}) eq 'ARRAY') {
+                $numitems = scalar(@{ $Apache::response::itemgroup{'names'} });
+                ($numrows,$bubbles_per_row) =
+                    &Apache::optionresponse::getnumrows($numitems);
+            }
+        }
+        if ($numrows < 1) {
+            $numrows = 1;
+        }
+        my $increment = &getfoilcounts($max) * $numrows;
+	&Apache::lonxml::increment_counter($increment,"$part.$response_id");
 	if ($target eq 'analyze') {
 	    &Apache::lonhomework::set_bubble_lines();
 	}
@@ -362,8 +396,41 @@
 	    %{ $Apache::response::itemgroup{'letter_name_map'} };
     }
     my @items;
+    my $numitems = scalar(@{ $Apache::response::itemgroup{'names'} });
+    my ($numrows,$bubbles_per_row);
+    if ($Apache::lonhomework::scantronmode) {
+        my $numitems = scalar(@{ $Apache::response::itemgroup{'names'} });
+        ($numrows,$bubbles_per_row) =
+            &Apache::optionresponse::getnumrows($numitems);
+    }
+    if ($numrows < 1) {
+        $numrows = 1;
+    }
+    my @alphabet=('A'..'Z');
+    my %nums_from_letters;
+    for (my $i=0; $i<@alphabet; $i++) {
+        $nums_from_letters{$alphabet[$i]} = $i;
+    }
     foreach my $name (@whichfoils) {
-	my $response = &Apache::response::getresponse($temp,'letter');
+        my $response;
+        if ($numrows > 1) {
+            my $num = $temp;
+            my $totalnum;
+            for (my $i=0; $i<$numrows; $i++) {
+                my $item = &Apache::response::getresponse($num,'letter');
+                if ($item =~ /^\w$/) {
+                    $totalnum = $i*$bubbles_per_row + $nums_from_letters{$item};
+                }
+                $num ++;
+            }
+            if ($totalnum =~ /^\d+$/) {
+                $response = $alphabet[$totalnum];
+            }
+            $temp += $numrows;
+        } else {
+	    $response = &Apache::response::getresponse($temp,'letter');
+            $temp ++;
+        }
 	push(@items,$response);
 	my $responsename = $letter_name_map{$response};
 	my $value=$Apache::response::foilgroup{$name.'.value'};
@@ -381,7 +448,6 @@
 	} else {
 	    $ignored++;
 	}
-	$temp++;
     }
     my $part=$Apache::inputtags::part;
     my $id = $Apache::inputtags::response['-1'];
@@ -535,6 +601,13 @@
 	my @alphabet=('A'..'Z');
 	my @used_letters=sort(keys(%letter_name_map));
 	my $internal_counter=$Apache::lonxml::counter;
+        my ($numrows,$bubbles_per_row);
+        if (($target eq 'tex') && ($Apache::lonhomework::type eq 'exam')) {
+            ($numrows,$bubbles_per_row) = 
+                &Apache::optionresponse::getnumrows(scalar(@used_letters));
+        } else {
+            $numrows = 1;
+        }
 	foreach my $name (@whichfoils) {
 	    my $lastopt=$lastresponse{$name};
 	    my $last_letter=$name_letter_map{$lastopt};
@@ -618,15 +691,27 @@
 		    my @emptyItems = ();
 		    for (my $i=0;$i<=$#used_letters;$i++) {push @emptyItems, ' ';}
 		    $question.='\vskip -1 mm\noindent\begin{list}{}{\setlength{\listparindent}{0mm}\setlength{\leftmargin}{2mm}}'
-			.'\item \hskip -3mm \textbf{'.$internal_counter.'}';
+			.'\item \hskip -3mm ';
+                    if ($numrows == 1) {
+                        $question .= '\textbf{'.$internal_counter.'}';
+                    } else {
+                        my $linetext;
+                        for (my $i=0; $i<$numrows; $i++) {
+                            $linetext .= $internal_counter+$i.', ';
+                        }
+                        $linetext =~ s/,\s$//;
+                        $question .= '\small {\textbf{'.$linetext.'}} '.
+                                     '\hskip 2 mm {\footnotesize '.
+                                     &mt('(Bubble once in [_1] lines)',$numrows).
+                                     '} \vskip 3 mm';
+                    }
+                    my $max_width;
 		    if (&itemdisplay('left') || &itemdisplay('right')) {
-			$question .= '\vskip -4 mm' . &Apache::optionresponse::bubbles(\@used_letters,\@emptyItems, "", $righttabsize);
-		    }
-		    else {
-			$question .= '\vskip -4 mm' . &Apache::optionresponse::bubbles(\@used_letters,\@emptyItems);
-		    }
+                        $max_width = $righttabsize;
+                    }
+		    $question .= '\vskip -4 mm' . &Apache::optionresponse::bubbles(\@used_letters,\@emptyItems,'',$max_width,$numrows,$bubbles_per_row,$internal_counter);
 		    $question .= '\end{list} \vskip -8 mm \strut ';
-		    $internal_counter++;
+		    $internal_counter += $numrows;
 	        } else {
                     if($env{'form.pdfFormFields'} eq 'yes' 
                             && $Apache::inputtags::status['-1'] eq 'CAN_ANSWER') {
@@ -823,5 +908,6 @@
 <endouttext />
 </foil>';
 }
+
 1;
 __END__
Index: loncom/homework/response.pm
diff -u loncom/homework/response.pm:1.225 loncom/homework/response.pm:1.226
--- loncom/homework/response.pm:1.225	Thu Sep  8 01:18:15 2011
+++ loncom/homework/response.pm	Tue Sep 13 21:42:58 2011
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # various response type definitons response definition
 #
-# $Id: response.pm,v 1.225 2011/09/08 01:18:15 raeburn Exp $
+# $Id: response.pm,v 1.226 2011/09/13 21:42:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1003,16 +1003,18 @@
 	$Apache::lonhomework::results{"resource.$part.$id.scantron"}.=
 	    $response;
 	if ($resulttype ne 'letter') {
-	    if ($resulttype eq 'A is 1') {
-		$response = $let_to_num{$response}+1;
-	    } else {
-		$response = $let_to_num{$response};
+            $response = $let_to_num{$response};
+            if ($resulttype eq 'A is 1') {
+                if ($response ne "") {
+                    $response = $response+1;
+                }
 	    }
 	    if ($response ne "") {
 		$response += $line * $bubbles_per_line;
 	    }
 	} else {
 	    if ($response ne "") {
+                my $raw = $response;
 		$response = chr(ord($response) + $line * $bubbles_per_line);
 	    }
 	}
@@ -1033,7 +1035,10 @@
 =item &repetition();
 
 Returns the number of lines that are required to encode the weight.
-(Currently expects that there are 10 bubbles per line)
+(Default is for 10 bubbles per bubblesheet item; other (integer) 
+values can be specified by using a custom Bubblesheet format file 
+with an eighteenth entry (BubblesPerRow) set to the integer 
+appropriate for the bubblesheets which will be used to assign weights.
 
 =cut
 
@@ -1041,8 +1046,15 @@
     my $id = $Apache::inputtags::part;
     my $weight = &Apache::lonnet::EXT("resource.$id.weight");
     if (!defined($weight) || ($weight eq '')) { $weight=1; }
-    my $repetition = int($weight/10);
-    if ($weight % 10 != 0) { $repetition++; } 
+    my $bubbles_per_row;
+    if (($env{'form.bubbles_per_row'} =~ /^\d+$/) && 
+        ($env{'form.bubbles_per_row'} > 0)) {
+        $bubbles_per_row = $env{'form.bubbles_per_row'};
+    } else {
+        $bubbles_per_row = 10;
+    }
+    my $repetition = int($weight/$bubbles_per_row);
+    if ($weight % $bubbles_per_row != 0) { $repetition++; } 
     return $repetition;
 }
 
Index: loncom/interface/lonprintout.pm
diff -u loncom/interface/lonprintout.pm:1.595 loncom/interface/lonprintout.pm:1.596
--- loncom/interface/lonprintout.pm:1.595	Wed Jun 22 11:00:47 2011
+++ loncom/interface/lonprintout.pm	Tue Sep 13 21:43:03 2011
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # Printout
 #
-# $Id: lonprintout.pm,v 1.595 2011/06/22 11:00:47 foxr Exp $
+# $Id: lonprintout.pm,v 1.596 2011/09/13 21:43:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2928,17 +2928,24 @@
 
 	 my $code_option=$helper->{'VARS'}->{'CODE_OPTION'};
          my @lines = &Apache::grades::get_scantronformat_file();
-	 my ($code_type,$code_length)=('letter',6);
+	 my ($code_type,$code_length,$bubbles_per_row)=('letter',6,10);
 	 foreach my $line (@lines) {
-	     my ($name,$type,$length) = (split(/:/,$line))[0,2,4];
+             chomp($line);
+	     my ($name,$type,$length,$bubbles_per_item) = 
+                 (split(/:/,$line))[0,2,4,17];
 	     if ($name eq $code_option) {
 		 $code_length=$length;
 		 if ($type eq 'number') { $code_type = 'number'; }
+                 chomp($bubbles_per_item); 
+                 if (($bubbles_per_item ne '') && ($bubbles_per_item > 0)) {
+                     $bubbles_per_row = $bubbles_per_item; 
+                 }
 	     }
 	 }
 	 my %moreenv = ('textwidth' => &get_textwidth($helper,$LaTeXwidth));
 	 $moreenv{'problem_split'}    = $parmhash{'problem_stream_switch'};
          $moreenv{'instructor_comments'}='hide';
+         $moreenv{'bubbles_per_row'} = $bubbles_per_row;
 	 my $seed=time+($$<<16)+($$);
 	 my @allcodes;
 	 if ($old_name) {
@@ -3265,7 +3272,7 @@
 	($print_type eq 'incomplete_problems_selpeople_course')) {
 	$print_incomplete = 1;
     }
-    if ($person =~ 'anon') {
+    if ($person eq 'anonymous') {
 	$namepostfix .="Name: ";
 	$fullname = "CODE - ".$moreenv->{'CODE'};
     }


More information about the LON-CAPA-cvs mailing list