Diff for /loncom/homework/grades.pm between versions 1.690 and 1.702

version 1.690, 2013/06/16 16:46:48 version 1.702, 2013/08/15 14:10:09
Line 736  sub most_similar { Line 736  sub most_similar {
 sub initialverifyreceipt {  sub initialverifyreceipt {
    my ($request,$symb) = @_;     my ($request,$symb) = @_;
    &commonJSfunctions($request);     &commonJSfunctions($request);
    return '<form name="gradingMenu"><input type="submit" value="'.&mt('Verify Receipt Number.').'" />'.     return '<form name="gradingMenu" action=""><input type="submit" value="'.&mt('Verify Receipt Number.').'" />'.
         &Apache::lonnet::recprefix($env{'request.course.id'}).          &Apache::lonnet::recprefix($env{'request.course.id'}).
         '-<input type="text" name="receipt" size="4" />'.          '-<input type="text" name="receipt" size="4" />'.
         '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".          '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
Line 908  LISTJAVASCRIPT Line 908  LISTJAVASCRIPT
     $submission_options.=      $submission_options.=
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="lastonly" /> '.          '<label><input type="radio" name="lastSub" value="lastonly" /> '.
         &mt('last submission only').' </label></span>'."\n".          &mt('last submission').' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="last" /> '.          '<label><input type="radio" name="lastSub" value="last" /> '.
         &mt('last submission &amp; parts info').' </label></span>'."\n".          &mt('last submission with details').' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.          '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.
         &mt('by dates and submissions').'</label></span>'."\n".          &mt('all submissions').'</label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="all" /> '.          '<label><input type="radio" name="lastSub" value="all" /> '.
         &mt('all details').'</label></span>';          &mt('all submissions with details').'</label></span>';
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Submissions'))
                   .$submission_options                    .$submission_options
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure();
   
Line 1702  sub gradeBox { Line 1702  sub gradeBox {
     $wgt       = ($wgt > 0 ? $wgt : '1');      $wgt       = ($wgt > 0 ? $wgt : '1');
     my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?      my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
   '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));    '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));
     my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";      my $data_WGT='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
     my $display_part= &get_display_part($partid,$symb);      my $display_part= &get_display_part($partid,$symb);
     my %last_resets = &get_last_resets($symb,$env{'request.course.id'},      my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
        [$partid]);         [$partid]);
Line 1710  sub gradeBox { Line 1710  sub gradeBox {
     if ($last_resets{$partid}) {      if ($last_resets{$partid}) {
         $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);          $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);
     }      }
     $result.=&Apache::loncommon::start_data_table_row();      my $result=&Apache::loncommon::start_data_table_row();
     my $ctr = 0;      my $ctr = 0;
     my $thisweight = 0;      my $thisweight = 0;
     my $increment = &get_increment();      my $increment = &get_increment();
Line 1747  sub gradeBox { Line 1747  sub gradeBox {
   
   
     $result .=       $result .= 
     '<td>'.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';      '<td>'.$data_WGT.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';
     $result.=&Apache::loncommon::end_data_table_row();      $result.=&Apache::loncommon::end_data_table_row();
       $result.=&Apache::loncommon::start_data_table_row().'<td colspan="6">';
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".      $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
  '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".   '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
  '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.   '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
Line 1759  sub gradeBox { Line 1760  sub gradeBox {
         $aggtries.'" />'."\n";          $aggtries.'" />'."\n";
     my $res_error;      my $res_error;
     $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error);      $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error);
       $result.='</td>'.&Apache::loncommon::end_data_table_row();
     if ($res_error) {      if ($res_error) {
         return &navmap_errormsg();          return &navmap_errormsg();
     }      }
Line 2051  sub submission { Line 2053  sub submission {
 <br /><b>$lt{'keyw'}:</b>&nbsp;  <br /><b>$lt{'keyw'}:</b>&nbsp;
 <a href="javascript:keywords(document.SCORE);" target="_self">$lt{'list'}</a>&nbsp; &nbsp;  <a href="javascript:keywords(document.SCORE);" target="_self">$lt{'list'}</a>&nbsp; &nbsp;
 <a href="#" onmousedown="javascript:getSel(); return false"  <a href="#" onmousedown="javascript:getSel(); return false"
  CLASS="page">$lt{'past'}</a>&nbsp; &nbsp;   class="page">$lt{'past'}</a>&nbsp; &nbsp;
 <a href="javascript:kwhighlight();" target="_self">$lt{'high'}</a><br /><br />  <a href="javascript:kwhighlight();" target="_self">$lt{'high'}</a><br /><br />
 KEYWORDS  KEYWORDS
 #  #
Line 2137  KEYWORDS Line 2139  KEYWORDS
  $result.=$sub_result;   $result.=$sub_result;
     }      }
     $request->print($result."\n");      $request->print($result."\n");
       
     # print student answer/submission      # print student answer/submission
     # Options are (1) Handgraded submission only      # Options are (1) Handgraded submission only
     #             (2) Last submission, includes submission that is not handgraded       #             (2) Last submission, includes submission that is not handgraded 
     #                  (for multi-response type part)      #                  (for multi-response type part)
     #             (3) Last submission plus the parts info      #             (3) Last submission plus the parts info
     #             (4) The whole record for this student      #             (4) The whole record for this student
     if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {      
  my ($string,$timestamp)= &get_last_submission(\%record);      my ($string,$timestamp)= &get_last_submission(\%record);
   
  my $lastsubonly;      my $lastsubonly;
   
         if ($$timestamp eq '') {      if ($$timestamp eq '') {
             $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>';           $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>'; 
         } else {      } else {
             $lastsubonly =          $lastsubonly =
                 '<div class="LC_grade_submissions_body">'              '<div class="LC_grade_submissions_body">'
                .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";             .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";
   
     my %seenparts;   my %seenparts;
     my @part_response_id = &flatten_responseType($responseType);   my @part_response_id = &flatten_responseType($responseType);
     foreach my $part (@part_response_id) {   foreach my $part (@part_response_id) {
  next if ($env{'form.lastSub'} eq 'hdgrade'       next if ($env{'form.lastSub'} eq 'hdgrade' 
  && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');   && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');
   
  my ($partid,$respid) = @{ $part };      my ($partid,$respid) = @{ $part };
  my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
  if ($env{"form.$uname:$udom:$partid:submitted_by"}) {      if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
     if (exists($seenparts{$partid})) { next; }   if (exists($seenparts{$partid})) { next; }
     $seenparts{$partid}=1;   $seenparts{$partid}=1;
     my $submitby='<b>Part:</b> '.$display_part.                  $request->print(
  ' <b>Collaborative submission by:</b> '.                      '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
  '<a href="javascript:viewSubmitter(\''.                      ' <b>'.&mt('Collaborative submission by: [_1]',
  $env{"form.$uname:$udom:$partid:submitted_by"}.                                 '<a href="javascript:viewSubmitter(\''.
  '\');" target="_self">'.                                 $env{"form.$uname:$udom:$partid:submitted_by"}.
  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';                                 '\');" target="_self">'.
     $request->print($submitby);                                 $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
     next;                      '<br />');
  }   next;
  my $responsetype = $responseType->{$partid}->{$respid};   }
  if (!exists($record{"resource.$partid.$respid.submission"})) {      my $responsetype = $responseType->{$partid}->{$respid};
                     $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.      if (!exists($record{"resource.$partid.$respid.submission"})) {
                         '<b>'.&mt('Part: [_1]',$display_part).'</b>'.                  $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
                         ' <span class="LC_internal_info">'.                      '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                         '('.&mt('Response ID: [_1]',$respid).')'.                      ' <span class="LC_internal_info">'.
                         '</span>&nbsp; &nbsp;'.                      '('.&mt('Response ID: [_1]',$respid).')'.
  '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';                      '</span>&nbsp; &nbsp;'.
     next;            '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
  }   next;
  foreach my $submission (@$string) {      }
     my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);      foreach my $submission (@$string) {
     if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }   my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
     my ($ressub,$hide,$subval) = split(/:/,$submission,3);   if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
     # Similarity check   my ($ressub,$hide,$subval) = split(/:/,$submission,3);
     my $similar='';   # Similarity check
                     my ($type,$trial,$rndseed);                  my $similar='';
                     if ($hide eq 'rand') {                  my ($type,$trial,$rndseed);
                         $type = 'randomizetry';                  if ($hide eq 'rand') {
                         $trial = $record{"resource.$partid.tries"};                      $type = 'randomizetry';
                         $rndseed = $record{"resource.$partid.rndseed"};                      $trial = $record{"resource.$partid.tries"};
                     }                      $rndseed = $record{"resource.$partid.rndseed"};
     if($env{'form.checkPlag'}){                  }
  my ($oname,$odom,$ocrsid,$oessay,$osim)=          if ($env{'form.checkPlag'}) {
     &most_similar($uname,$udom,$symb,$subval);         my ($oname,$odom,$ocrsid,$oessay,$osim)=
  if ($osim) {          &most_similar($uname,$udom,$symb,$subval);
     $osim=int($osim*100.0);      if ($osim) {
     my %old_course_desc =    $osim=int($osim*100.0);
  &Apache::lonnet::coursedescription($ocrsid,   my %old_course_desc = 
    {'one_time' => 1});      &Apache::lonnet::coursedescription($ocrsid,
    {'one_time' => 1});
                             if ($hide eq 'anon') {  
                                 $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.                          if ($hide eq 'anon') {
                                          &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';                              $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
                             } else {                                       &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
         $similar="<hr /><h3><span class=\"LC_warning\">".                          } else {
     &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',      $similar="<hr /><h3><span class=\"LC_warning\">".
         $osim,   &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
         &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',      $osim,
       &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
         $old_course_desc{'description'},          $old_course_desc{'description'},
         $old_course_desc{'num'},          $old_course_desc{'num'},
         $old_course_desc{'domain'}).          $old_course_desc{'domain'}).
     '</span></h3><blockquote><i>'.      '</span></h3><blockquote><i>'.
     &keywords_highlight($oessay).      &keywords_highlight($oessay).
     '</i></blockquote><hr />';      '</i></blockquote><hr />';
                             }                          }
  }              }
     }   }
     my $order=&get_order($partid,$respid,$symb,$uname,$udom,   my $order=&get_order($partid,$respid,$symb,$uname,$udom,
                                          undef,$type,$trial,$rndseed);                                       undef,$type,$trial,$rndseed);
     if ($env{'form.lastSub'} eq 'lastonly' ||                   if ($env{'form.lastSub'} eq 'lastonly' || $env{'form.lastSub'} eq 'datesub' || $env{'form.lastSub'} =~ /^(last|all)$/ || ($env{'form.lastSub'} eq 'hdgrade' && 
  ($env{'form.lastSub'} eq 'hdgrade' &&   
  $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {   $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {
  my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
                         $lastsubonly.='<div class="LC_grade_submission_part">'.                      $lastsubonly.='<div class="LC_grade_submission_part">'.
                             '<b>'.&mt('Part: [_1]',$display_part).'</b>'.                          '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                             ' <span class="LC_internal_info">'.                          ' <span class="LC_internal_info">'.
                             '('.&mt('Response ID: [_1]',$respid).')'.                          '('.&mt('Response ID: [_1]',$respid).')'.
                             '</span>&nbsp; &nbsp;';                          '</span>&nbsp; &nbsp;';
  my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);      my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
  if (@$files) {                          
                             if ($hide eq 'anon') {      if (@$files) {
                                 $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));  
                             } else {  
                                 $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />';  
                                 foreach my $file (@$files) {  
                                     &Apache::lonnet::allowuploaded('/adm/grades',$file);  
                                     $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" /> '.$file.'</a>';  
                                 }  
                             }  
     $lastsubonly.='<br />';  
  }  
                         if ($hide eq 'anon') {                          if ($hide eq 'anon') {
                             $lastsubonly.='<b>'.&mt('Anonymous Survey').'</b>';                               $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
                         } else {                          } else {
     $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.                              $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
         &cleanRecord($subval,$responsetype,$symb,$partid,                                          .'<br /><span class="LC_warning">';
      $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);                              if(@$files == 1) {
                                   $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
                               } else {
                                   $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
                               }
                               $lastsubonly .= '</span>';                         
                               foreach my $file (@$files) {
                                   &Apache::lonnet::allowuploaded('/adm/grades',$file);
                                   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
                               }
                         }                          }
  if ($similar) {$lastsubonly.="<br /><br />$similar\n";}   $lastsubonly.='<br />';
  $lastsubonly.='</div>';                      }
     }                      if ($hide eq 'anon') {
                           $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; 
                       } else {
                        $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>'.
       &cleanRecord($subval,$responsetype,$symb,$partid,
    $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
                       }
               if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
       $lastsubonly.='</div>';
  }   }
     }              }
     $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body  
  }   }
  $request->print($lastsubonly);   $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
    } elsif ($env{'form.lastSub'} eq 'datesub') {      }
       $request->print($lastsubonly);
       if ($env{'form.lastSub'} eq 'datesub') {
         my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);          my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);
  $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));   $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
     } elsif ($env{'form.lastSub'} =~ /^(last|all)$/) {      } 
  $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,      if ($env{'form.lastSub'} =~ /^(last|all)$/) {
           $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
  $env{'request.course.id'},   $env{'request.course.id'},
  $last,'.submission',   $last,'.submission',
  'Apache::grades::keywords_highlight'));   'Apache::grades::keywords_highlight'));
Line 2300  KEYWORDS Line 2311  KEYWORDS
     '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";      '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
  $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.   $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
     ',\''.$msgfor.'\');" target="_self">'.      ',\''.$msgfor.'\');" target="_self">'.
     &mt('Compose message to student').(scalar(@$col_fullnames) >= 1 ? 's' : '').'</a><label> ('.      &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.
     &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.      &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
     '<img src="'.$request->dir_config('lonIconsURL').      ' <img src="'.$request->dir_config('lonIconsURL').
     '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".      '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".
     '<br />&nbsp;('.      '<br />&nbsp;('.
     &mt('Message will be sent when you click on Save &amp; Next below.').")\n";      &mt('Message will be sent when you click on Save &amp; Next below.').")\n";
  $result.='</div></div>';   $result.='</div></div>';
Line 3584  sub viewgrades { Line 3595  sub viewgrades {
     $partid.'" size="4" '.'onchange="javascript:writePoint(\''.      $partid.'" size="4" '.'onchange="javascript:writePoint(\''.
  $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.   $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
     $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";      $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";
  $line.= '<td><b>'.&mt('Grade Status').':</b><select name="SELVAL_'.$partid.'"'.          $line.= '<td><b>'.&mt('Grade Status').':</b>'.
     'onchange="javascript:writeRadText(\''.$partid.'\','.              '<select name="SELVAL_'.$partid.'" '.
  $weight{$partid}.')"> '.              'onchange="javascript:writeRadText(\''.$partid.'\','.
                   $weight{$partid}.')"> '.
     '<option selected="selected"> </option>'.      '<option selected="selected"> </option>'.
     '<option value="excused">'.&mt('excused').'</option>'.      '<option value="excused">'.&mt('excused').'</option>'.
     '<option value="reset status">'.&mt('reset status').'</option>'.      '<option value="reset status">'.&mt('reset status').'</option>'.
Line 3635  sub viewgrades { Line 3647  sub viewgrades {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  if ($display =~ /^Partial Credit Factor/) {   if ($display =~ /^Partial Credit Factor/) {
     $result.='<th>'.      $result.='<th>'.
  &mt('Score Part: [_1]<br /> (weight = [_2])',   &mt('Score Part: [_1][_2](weight = [_3])',
     $display_part,$weight{$partid}).'</th>'."\n";      $display_part,'<br />',$weight{$partid}).'</th>'."\n";
     next;      next;
           
  } else {   } else {
Line 4444  LISTJAVASCRIPT Line 4456  LISTJAVASCRIPT
     my ($curpage) =&Apache::lonnet::decode_symb($symb);       my ($curpage) =&Apache::lonnet::decode_symb($symb); 
 #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb);   #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
 #    my $type=($curpage =~ /\.(page|sequence)/);  #    my $type=($curpage =~ /\.(page|sequence)/);
     my $select = '<select name="selectpage">'."\n";  
       # Collection of hidden fields
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);          my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $select.='<option value="'.$ctr.'" '.          $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
     ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').          $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
     '>'.$showtitle.'</option>'."\n";          $ctr++;
  $ctr++;  
     }      }
     $select.= '</select>';      $result.='<input type="hidden" name="page" />'."\n".
     $result.='&nbsp;<b>'.&mt('Problems from').':</b> '.$select."<br />\n";          '<input type="hidden" name="title" />'."\n";
   
       $result.=&build_section_inputs();
       my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
       $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".
    '<input type="hidden" name="command" value="displayPage" />'."\n".
    '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
   
       # Show grading options
       $result.=&Apache::lonhtmlcommon::start_pick_box();
       my $select = '<select name="selectpage">'."\n";
     $ctr=0;      $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";   $select.='<option value="'.$ctr.'"'.
  $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";      ($$symbx{$_} =~ /$curpage$/ ? ' selected="selected"' : '').
       '>'.$showtitle.'</option>'."\n";
  $ctr++;   $ctr++;
     }      }
     $result.='<input type="hidden" name="page" />'."\n".      $select.= '</select>';
  '<input type="hidden" name="title" />'."\n";  
   
     my $options =      $result.=
  '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".          &Apache::lonhtmlcommon::row_title(&mt('Problems from'))
  '<label><input type="radio" name="vProb" value="yes" /> '.&mt('yes').' </label>'."<br />\n";         .$select
     $result.='&nbsp;<b>'.&mt('View Problem Text').': </b>'.$options;         .&Apache::lonhtmlcommon::row_closure();
   
     $options =      $result.=
  '<label><input type="radio" name="lastSub" value="none" /> '.&mt('none').' </label>'."\n".          &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
  '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.&mt('by dates and submissions').'</label>'."\n".         .'<label><input type="radio" name="vProb" value="no"'
  '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').' </label>'."\n";             .' checked="checked" /> '.&mt('no').' </label>'."\n"
     $result.='&nbsp;<b>'.&mt('Submissions').': </b>'.$options;         .'<label><input type="radio" name="vProb" value="yes" />'
              .&mt('yes').'</label>'."\n"
          .&Apache::lonhtmlcommon::row_closure();
   
       $result.=
           &Apache::lonhtmlcommon::row_title(&mt('View Submissions'))
          .'<label><input type="radio" name="lastSub" value="none" /> '
              .&mt('none').' </label>'."\n"
          .'<label><input type="radio" name="lastSub" value="datesub"'
              .' checked="checked" /> '.&mt('all submissions').'</label>'."\n"
          .'<label><input type="radio" name="lastSub" value="all" /> '
              .&mt('all submissions with details').' </label>'
          .&Apache::lonhtmlcommon::row_closure();
           
     $result.=&build_section_inputs();      $result.=
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));          &Apache::lonhtmlcommon::row_title(&mt('Use CODE'))
     $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".         .'<input type="text" name="CODE" value="" />'
  '<input type="hidden" name="command" value="displayPage" />'."\n".         .&Apache::lonhtmlcommon::row_closure(1)
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."<br />\n";         .&Apache::lonhtmlcommon::end_pick_box();
   
     $result.='&nbsp;<b>'.&mt('Use CODE').': </b> <input type="text" name="CODE" value="" /> <br />'."\n";      # Show list of students to select for grading
       $result.='<br /><input type="button" '.
     $result.='&nbsp;<input type="button" '.  
              'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";               'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";
   
     $request->print($result);      $request->print($result);
Line 4630  sub displayPage { Line 4662  sub displayPage {
         '</span>'."\n".          '</span>'."\n".
  &Apache::loncommon::start_data_table().   &Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
  '<th align="center">&nbsp;Prob.&nbsp;</th>'.   '<th>'.&mt('Prob.').'</th>'.
  '<th>&nbsp;'.($env{'form.vProb'} eq 'no' ? &mt('Title') : &mt('Problem Text')).'/'.&mt('Grade').'</th>'.   '<th>&nbsp;'.($env{'form.vProb'} eq 'no' ? &mt('Title') : &mt('Problem Text')).'/'.&mt('Grade').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
   
Line 5142  my %subdivided_bubble_lines;       # no. Line 5174  my %subdivided_bubble_lines;       # no.
   
 my %responsetype_per_response;     # responsetype for each response  my %responsetype_per_response;     # responsetype for each response
   
   my %masterseq_id_responsenum;      # src_id (e.g., 12.3_0.11 etc.) for each
                                      # numbered response. Needed when randomorder
                                      # or randompick are in use. Key is ID, value 
                                      # is response number.
   
 # Save and restore the bubble lines array to the form env.  # Save and restore the bubble lines array to the form env.
   
   
Line 5155  sub save_bubble_lines { Line 5192  sub save_bubble_lines {
         $env{"form.scantron.responsetype.$line"} =          $env{"form.scantron.responsetype.$line"} =
             $responsetype_per_response{$line};              $responsetype_per_response{$line};
     }      }
       foreach my $resid (keys(%masterseq_id_responsenum)) {
           my $line = $masterseq_id_responsenum{$resid};
           $env{"form.scantron.residpart.$line"} = $resid;
       }
 }  }
   
   
 sub restore_bubble_lines {  sub restore_bubble_lines {
     my $line = 0;      my $line = 0;
     %bubble_lines_per_response = ();      %bubble_lines_per_response = ();
       %masterseq_id_responsenum = ();
     while ($env{"form.scantron.bubblelines.$line"}) {      while ($env{"form.scantron.bubblelines.$line"}) {
  my $value = $env{"form.scantron.bubblelines.$line"};   my $value = $env{"form.scantron.bubblelines.$line"};
  $bubble_lines_per_response{$line} = $value;   $bubble_lines_per_response{$line} = $value;
Line 5170  sub restore_bubble_lines { Line 5212  sub restore_bubble_lines {
             $env{"form.scantron.sub_bubblelines.$line"};              $env{"form.scantron.sub_bubblelines.$line"};
         $responsetype_per_response{$line} =          $responsetype_per_response{$line} =
             $env{"form.scantron.responsetype.$line"};              $env{"form.scantron.responsetype.$line"};
           my $id = $env{"form.scantron.residpart.$line"};
           $masterseq_id_responsenum{$id} = $line;
  $line++;   $line++;
     }      }
 }  }
Line 5501  sub scantron_selectphase { Line 5545  sub scantron_selectphase {
   
     &Apache::lonpickcode::code_list($r,2);      &Apache::lonpickcode::code_list($r,2);
   
     $r->print('<br /><form method="post" name="checkscantron">'.      $r->print('<br /><form method="post" name="checkscantron" action="">'.
              $default_form_data."\n".               $default_form_data."\n".
              &Apache::loncommon::start_data_table('LC_scantron_action')."\n".               &Apache::loncommon::start_data_table('LC_scantron_action')."\n".
              &Apache::loncommon::start_data_table_header_row()."\n".               &Apache::loncommon::start_data_table_header_row()."\n".
Line 5834  sub digits_to_letters { Line 5878  sub digits_to_letters {
                        (see scantron_getfile for more information)                         (see scantron_getfile for more information)
     just_header      - True if should not process question answers but only      just_header      - True if should not process question answers but only
                        the stuff to the left of the answers.                         the stuff to the left of the answers.
       randomorder      - True if randomorder in use
       randompick       - True if randompick in use
       sequence         - Exam folder URL
       master_seq       - Ref to array containing symbs in exam folder
       symb_to_resource - Ref to hash of symbs for resources in exam folder
                          (corresponding values are resource objects)
       partids_by_symb  - Ref to hash of symb -> array ref of partIDs
       orderedforcode   - Ref to hash of arrays. keys are CODEs and values
                          are refs to an array of resource objects, ordered
                          according to order used for CODE, when randomorder
                          and or randompick are in use.
       respnumlookup    - Ref to hash mapping question numbers in bubble lines
                          for current line to question number used for same question
                           in "Master Sequence" (as seen by Course Coordinator).
       startline        - Ref to hash where key is question number (0 is first)
                          and value is number of first bubble line for current 
                          student or code-based randompick and/or randomorder.
       totalref         - Ref of scalar used to score total number of bubble
                          lines needed for responses in a scan line (used when
                          randompick in use. 
       
  Returns:   Returns:
    Hash containing the result of parsing the scanline     Hash containing the result of parsing the scanline
   
Line 5878  sub digits_to_letters { Line 5943  sub digits_to_letters {
 =cut  =cut
   
 sub scantron_parse_scanline {  sub scantron_parse_scanline {
     my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;      my ($line,$whichline,$scantron_config,$scan_data,$just_header,$idmap,
           $randomorder,$randompick,$sequence,$master_seq,$symb_to_resource,
           $partids_by_symb,$orderedforcode,$respnumlookup,$startline,$totalref)=@_;
   
     my %record;      my %record;
     my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};      my $data=substr($line,0,$$scantron_config{'Qstart'}-1); # stuff before answers
     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 ||      if (!($$scantron_config{'CODElocation'} eq 0 ||
   $$scantron_config{'CODElocation'} eq 'none')) {    $$scantron_config{'CODElocation'} eq 'none')) {
  if ($$scantron_config{'CODElocation'} < 0 ||   if ($$scantron_config{'CODElocation'} < 0 ||
Line 5919  sub scantron_parse_scanline { Line 5984  sub scantron_parse_scanline {
     my $questnum=0;      my $questnum=0;
     my $ansnum  =1; # Multiple 'answer lines'/question.      my $ansnum  =1; # Multiple 'answer lines'/question.
   
       my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};
       if ($randompick || $randomorder) {
           my $total = &get_respnum_lookups($sequence,$scan_data,$idmap,$line,\%record,
                                            $master_seq,$symb_to_resource,
                                            $partids_by_symb,$orderedforcode,
                                            $respnumlookup,$startline);
           if ($total) {
               $lastpos = $total*$$scantron_config{'Qlength'}; 
           }
           if (ref($totalref)) {
               $$totalref = $total;
           }
       }
       my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers
     chomp($questions); # Get rid of any trailing \n.      chomp($questions); # Get rid of any trailing \n.
     $questions =~ s/\r$//;      # Get rid of trailing \r too (MAC or Win uploads).      $questions =~ s/\r$//;      # Get rid of trailing \r too (MAC or Win uploads).
     while (length($questions)) {      while (length($questions)) {
  my $answers_needed = $bubble_lines_per_response{$questnum};          my $answers_needed;
           if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
               $answers_needed = $bubble_lines_per_response{$respnumlookup->{$questnum}};
           } else {
       $answers_needed = $bubble_lines_per_response{$questnum};
           }
         my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)          my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)
                              || 1;                               || 1;
         $questnum++;          $questnum++;
Line 5931  sub scantron_parse_scanline { Line 6015  sub scantron_parse_scanline {
         $questions       = substr($questions,$answer_length);          $questions       = substr($questions,$answer_length);
         if (length($currentquest) < $answer_length) { next; }          if (length($currentquest) < $answer_length) { next; }
   
         if ($subdivided_bubble_lines{$questnum-1} =~ /,/) {          my $subdivided;
           if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
               $subdivided = $subdivided_bubble_lines{$respnumlookup->{$questnum-1}};
           } else {
               $subdivided = $subdivided_bubble_lines{$questnum-1};
           }
           if ($subdivided =~ /,/) {
             my $subquestnum = 1;              my $subquestnum = 1;
             my $subquestions = $currentquest;              my $subquestions = $currentquest;
             my @subanswers_needed =               my @subanswers_needed = split(/,/,$subdivided);
                 split(/,/,$subdivided_bubble_lines{$questnum-1});    
             foreach my $subans (@subanswers_needed) {              foreach my $subans (@subanswers_needed) {
                 my $subans_length =                  my $subans_length =
                     ($$scantron_config{'Qlength'} * $subans)  || 1;                      ($$scantron_config{'Qlength'} * $subans)  || 1;
Line 5946  sub scantron_parse_scanline { Line 6035  sub scantron_parse_scanline {
                     ($$scantron_config{'Qon'} eq 'number')) {                      ($$scantron_config{'Qon'} eq 'number')) {
                     $ansnum = &scantron_validator_lettnum($ansnum,                       $ansnum = &scantron_validator_lettnum($ansnum, 
                         $questnum,$quest_id,$subans,$currsubquest,$whichline,                          $questnum,$quest_id,$subans,$currsubquest,$whichline,
                         \@alphabet,\%record,$scantron_config,$scan_data);                          \@alphabet,\%record,$scantron_config,$scan_data,
                           $randomorder,$randompick,$respnumlookup);
                 } else {                  } else {
                     $ansnum = &scantron_validator_positional($ansnum,                      $ansnum = &scantron_validator_positional($ansnum,
                         $questnum,$quest_id,$subans,$currsubquest,$whichline,                        \@alphabet,\%record,$scantron_config,$scan_data);                          $questnum,$quest_id,$subans,$currsubquest,$whichline,
                           \@alphabet,\%record,$scantron_config,$scan_data,
                           $randomorder,$randompick,$respnumlookup);
                 }                  }
                 $subquestnum ++;                  $subquestnum ++;
             }              }
Line 5958  sub scantron_parse_scanline { Line 6050  sub scantron_parse_scanline {
                 ($$scantron_config{'Qon'} eq 'number')) {                  ($$scantron_config{'Qon'} eq 'number')) {
                 $ansnum = &scantron_validator_lettnum($ansnum,$questnum,                  $ansnum = &scantron_validator_lettnum($ansnum,$questnum,
                     $quest_id,$answers_needed,$currentquest,$whichline,                      $quest_id,$answers_needed,$currentquest,$whichline,
                     \@alphabet,\%record,$scantron_config,$scan_data);                      \@alphabet,\%record,$scantron_config,$scan_data,
                       $randomorder,$randompick,$respnumlookup);
             } else {              } else {
                 $ansnum = &scantron_validator_positional($ansnum,$questnum,                  $ansnum = &scantron_validator_positional($ansnum,$questnum,
                     $quest_id,$answers_needed,$currentquest,$whichline,                      $quest_id,$answers_needed,$currentquest,$whichline,
                     \@alphabet,\%record,$scantron_config,$scan_data);                      \@alphabet,\%record,$scantron_config,$scan_data,
                       $randomorder,$randompick,$respnumlookup);
             }              }
         }          }
     }      }
Line 5970  sub scantron_parse_scanline { Line 6064  sub scantron_parse_scanline {
     return \%record;      return \%record;
 }  }
   
   sub get_master_seq {
       my ($resources,$master_seq,$symb_to_resource) = @_;
       return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') && 
                      (ref($symb_to_resource) eq 'HASH'));
       my $resource_error;
       foreach my $resource (@{$resources}) {
           my $ressymb;
           if (ref($resource)) {
               $ressymb = $resource->symb();
               push(@{$master_seq},$ressymb);
               $symb_to_resource->{$ressymb} = $resource;
           } else {
               $resource_error = 1;
               last;
           }
       }
       return $resource_error;
   }
   
   sub get_respnum_lookups {
       my ($sequence,$scan_data,$idmap,$line,$record,$master_seq,$symb_to_resource,
           $partids_by_symb,$orderedforcode,$respnumlookup,$startline) = @_;
       return unless ((ref($record) eq 'HASH') && (ref($master_seq) eq 'ARRAY') &&
                      (ref($symb_to_resource) eq 'HASH') && (ref($partids_by_symb) eq 'HASH') &&
                      (ref($orderedforcode) eq 'HASH') && (ref($respnumlookup) eq 'HASH') &&
                      (ref($startline) eq 'HASH'));
       my ($user,$scancode);
       if ((exists($record->{'scantron.CODE'})) &&
           (&Apache::lonnet::validCODE($record->{'scantron.CODE'}))) {
           $scancode = $record->{'scantron.CODE'};
       } else {
           $user = &scantron_find_student($record,$scan_data,$idmap,$line);
       }
       my @mapresources =
           &users_order($user,$scancode,$sequence,$master_seq,$symb_to_resource,
                        $orderedforcode);
       my $total = 0;
       my $count = 0;
       foreach my $resource (@mapresources) {
           my $id = $resource->id();
           my $symb = $resource->symb();
           if (ref($partids_by_symb->{$symb}) eq 'ARRAY') {
               foreach my $partid (@{$partids_by_symb->{$symb}}) {
                   my $respnum = $masterseq_id_responsenum{$id.'_'.$partid};
                   if ($respnum ne '') {
                       $respnumlookup->{$count} = $respnum;
                       $startline->{$count} = $total;
                       $total += $bubble_lines_per_response{$respnum};
                       $count ++;
                   }
               }
           }
       }
       return $total;
   }
   
 sub scantron_validator_lettnum {  sub scantron_validator_lettnum {
     my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,$whichline,      my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,$whichline,
         $alphabet,$record,$scantron_config,$scan_data) = @_;          $alphabet,$record,$scantron_config,$scan_data,$randomorder,
           $randompick,$respnumlookup) = @_;
   
     # Qon 'letter' implies for each slot in currquest we have:      # Qon 'letter' implies for each slot in currquest we have:
     #    ? or * for doubles, a letter in A-Z for a bubble, and      #    ? or * for doubles, a letter in A-Z for a bubble, and
Line 5991  sub scantron_validator_lettnum { Line 6142  sub scantron_validator_lettnum {
         $matchon = '\d';          $matchon = '\d';
     }      }
     my $occurrences = 0;      my $occurrences = 0;
     if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||      my $responsenum = $questnum-1;
         ($responsetype_per_response{$questnum-1} eq 'formularesponse') ||      if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
         ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||         $responsenum = $respnumlookup->{$questnum-1} 
         ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||      }
         ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||      if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
         ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {          ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
           ($responsetype_per_response{$responsenum} eq 'stringresponse') ||
           ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
           ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
           ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
         my @singlelines = split('',$currquest);          my @singlelines = split('',$currquest);
         foreach my $entry (@singlelines) {          foreach my $entry (@singlelines) {
             $occurrences = &occurence_count($entry,$matchon);              $occurrences = &occurence_count($entry,$matchon);
             if ($occurrences > 1) {              if ($occurrences > 1) {
                 last;                  last;
             }              }
         }           }
     } else {      } else {
         $occurrences = &occurence_count($currquest,$matchon);           $occurrences = &occurence_count($currquest,$matchon); 
     }      }
Line 6051  sub scantron_validator_lettnum { Line 6206  sub scantron_validator_lettnum {
   
 sub scantron_validator_positional {  sub scantron_validator_positional {
     my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,      my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,
         $whichline,$alphabet,$record,$scantron_config,$scan_data) = @_;          $whichline,$alphabet,$record,$scantron_config,$scan_data,
           $randomorder,$randompick,$respnumlookup) = @_;
   
     # Otherwise there's a positional notation;      # Otherwise there's a positional notation;
     # each bubble line requires Qlength items, and there are filled in      # each bubble line requires Qlength items, and there are filled in
Line 6093  sub scantron_validator_positional { Line 6249  sub scantron_validator_positional {
         #  record all the bubbles filled in as well as the          #  record all the bubbles filled in as well as the
         #  fact this response consists of multiple bubbles.          #  fact this response consists of multiple bubbles.
         #          #
         if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||          my $responsenum = $questnum-1;
             ($responsetype_per_response{$questnum-1} eq 'formularesponse') ||          if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
             ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||              $responsenum = $respnumlookup->{$questnum-1}
             ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||          }
             ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||          if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
             ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {              ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
               ($responsetype_per_response{$responsenum} eq 'stringresponse') ||
               ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
               ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
               ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
             my $doubleerror = 0;              my $doubleerror = 0;
             while (($currquest >= $$scantron_config{'Qlength'}) &&               while (($currquest >= $$scantron_config{'Qlength'}) && 
                    (!$doubleerror)) {                     (!$doubleerror)) {
Line 6503  SCANTRONFORM Line 6663  SCANTRONFORM
            '<input type="hidden" name="scantron.sub_bubblelines.'.$line.'" value="'.$env{"form.scantron.sub_bubblelines.$line"}.'" />'."\n";             '<input type="hidden" name="scantron.sub_bubblelines.'.$line.'" value="'.$env{"form.scantron.sub_bubblelines.$line"}.'" />'."\n";
        $chunk .=         $chunk .=
            '<input type="hidden" name="scantron.responsetype.'.$line.'" value="'.$env{"form.scantron.responsetype.$line"}.'" />'."\n";             '<input type="hidden" name="scantron.responsetype.'.$line.'" value="'.$env{"form.scantron.responsetype.$line"}.'" />'."\n";
          $chunk .=
              '<input type="hidden" name="scantron.residpart.'.$line.'" value="'.$env{"form.scantron.residpart.$line"}.'" />'."\n";
        $result .= $chunk;         $result .= $chunk;
        $line++;         $line++;
    }      }
     return $result;      return $result;
 }  }
   
Line 6577  sub scantron_validate_file { Line 6739  sub scantron_validate_file {
     while (!$stop && $currentphase < scalar(@validate_phases)) {      while (!$stop && $currentphase < scalar(@validate_phases)) {
  $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');   $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');
  $r->rflush();   $r->rflush();
        
  my $which="scantron_validate_".$validate_phases[$currentphase];   my $which="scantron_validate_".$validate_phases[$currentphase];
  {   {
     no strict 'refs';      no strict 'refs';
Line 7072  sub scantron_validate_ID { Line 7235  sub scantron_validate_ID {
   
   
 sub scantron_get_correction {  sub scantron_get_correction {
     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;      my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,
           $randomorder,$randompick,$respnumlookup,$startline)=@_;
 #FIXME in the case of a duplicated ID the previous line, probably need  #FIXME in the case of a duplicated ID the previous line, probably need
 #to show both the current line and the previous one and allow skipping  #to show both the current line and the previous one and allow skipping
 #the previous one or the current one  #the previous one or the current one
Line 7206  ENDSCRIPT Line 7370  ENDSCRIPT
  # The form field scantron_questions is acutally a list of line numbers.   # The form field scantron_questions is acutally a list of line numbers.
  # represented by this form so:   # represented by this form so:
   
  my $line_list = &questions_to_line_list($arg);   my $line_list = &questions_to_line_list($arg,$randomorder,$randompick,
                                                   $respnumlookup,$startline);
   
  $r->print('<input type="hidden" name="scantron_questions" value="'.   $r->print('<input type="hidden" name="scantron_questions" value="'.
   $line_list.'" />');    $line_list.'" />');
Line 7214  ENDSCRIPT Line 7379  ENDSCRIPT
  $r->print("<p>".&mt("Please indicate which bubble should be used for grading")."</p>");   $r->print("<p>".&mt("Please indicate which bubble should be used for grading")."</p>");
  foreach my $question (@{$arg}) {   foreach my $question (@{$arg}) {
     my @linenums = &prompt_for_corrections($r,$question,$scan_config,      my @linenums = &prompt_for_corrections($r,$question,$scan_config,
                                                    $scan_record, $error);                                                     $scan_record, $error,
                                                      $randomorder,$randompick,
                                                      $respnumlookup,$startline);
             push(@lines_to_correct,@linenums);              push(@lines_to_correct,@linenums);
  }   }
         $r->print(&verify_bubbles_checked(@lines_to_correct));          $r->print(&verify_bubbles_checked(@lines_to_correct));
Line 7227  ENDSCRIPT Line 7394  ENDSCRIPT
  # The form field scantron_questions is actually a list of line numbers not   # The form field scantron_questions is actually a list of line numbers not
  # a list of question numbers. Therefore:   # a list of question numbers. Therefore:
  #   #
   
  my $line_list = &questions_to_line_list($arg);   my $line_list = &questions_to_line_list($arg,$randomorder,$randompick,
                                                   $respnumlookup,$startline);
   
  $r->print('<input type="hidden" name="scantron_questions" value="'.   $r->print('<input type="hidden" name="scantron_questions" value="'.
   $line_list.'" />');    $line_list.'" />');
  foreach my $question (@{$arg}) {   foreach my $question (@{$arg}) {
     my @linenums = &prompt_for_corrections($r,$question,$scan_config,      my @linenums = &prompt_for_corrections($r,$question,$scan_config,
                                                    $scan_record, $error);                                                     $scan_record, $error,
                                                      $randomorder,$randompick,
                                                      $respnumlookup,$startline);
             push(@lines_to_correct,@linenums);              push(@lines_to_correct,@linenums);
  }   }
         $r->print(&verify_bubbles_checked(@lines_to_correct));          $r->print(&verify_bubbles_checked(@lines_to_correct));
Line 7285  used to fill in the scantron_questions f Line 7455  used to fill in the scantron_questions f
   
   Arguments:    Arguments:
      questions    - Reference to an array of questions.       questions    - Reference to an array of questions.
        randomorder  - True if randomorder in use.
        randompick   - True if randompick in use.
        respnumlookup - Reference to HASH mapping question numbers in bubble lines
                        for current line to question number used for same question
                        in "Master Seqence" (as seen by Course Coordinator).
        startline    - Reference to hash where key is question number (0 is first)
                       and key is number of first bubble line for current student
                       or code-based randompick and/or randomorder.
   
 =cut  =cut
   
   
 sub questions_to_line_list {  sub questions_to_line_list {
     my ($questions) = @_;      my ($questions,$randomorder,$randompick,$respnumlookup,$startline) = @_;
     my @lines;      my @lines;
   
     foreach my $item (@{$questions}) {      foreach my $item (@{$questions}) {
Line 7299  sub questions_to_line_list { Line 7477  sub questions_to_line_list {
         if ($item =~ /^(\d+)\.(\d+)$/) {          if ($item =~ /^(\d+)\.(\d+)$/) {
             $question = $1;              $question = $1;
             my $subquestion = $2;              my $subquestion = $2;
             $first = $first_bubble_line{$question-1} + 1;              my $responsenum = $question-1;
             my @subans = split(/,/,$subdivided_bubble_lines{$question-1});              if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
                   $responsenum = $respnumlookup->{$question-1};
                   if (ref($startline) eq 'HASH') {
                       $first = $startline->{$question-1} + 1;
                   }
               } else {
                   $first = $first_bubble_line{$responsenum} + 1;
               }
               my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
             my $subcount = 1;              my $subcount = 1;
             while ($subcount<$subquestion) {              while ($subcount<$subquestion) {
                 $first += $subans[$subcount-1];                  $first += $subans[$subcount-1];
Line 7308  sub questions_to_line_list { Line 7494  sub questions_to_line_list {
             }              }
             $count = $subans[$subquestion-1];              $count = $subans[$subquestion-1];
         } else {          } else {
     $first   = $first_bubble_line{$question-1} + 1;              my $responsenum = $question-1;
     $count   = $bubble_lines_per_response{$question-1};              if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
                   $responsenum = $respnumlookup->{$question-1};
                   if (ref($startline) eq 'HASH') {
                       $first = $startline->{$question-1} + 1;
                   }
               } else {
                   $first = $first_bubble_line{$responsenum} + 1;
               }
       $count   = $bubble_lines_per_response{$responsenum};
         }          }
         $last = $first+$count-1;          $last = $first+$count-1;
         push(@lines, ($first..$last));          push(@lines, ($first..$last));
Line 7331  for multi and missing bubble cases). Line 7525  for multi and missing bubble cases).
    $scan_config - The scantron file configuration hash.     $scan_config - The scantron file configuration hash.
    $scan_record - Reference to the hash that has the the parsed scanlines.     $scan_record - Reference to the hash that has the the parsed scanlines.
    $error       - Type of error     $error       - Type of error
      $randomorder - True if randomorder in use.
      $randompick  - True if randompick in use.
      $respnumlookup - Reference to HASH mapping question numbers in bubble lines
                       for current line to question number used for same question
                       in "Master Seqence" (as seen by Course Coordinator).
      $startline   - Reference to hash where key is question number (0 is first)
                     and value is number of first bubble line for current student
                     or code-based randompick and/or randomorder.
   
   
  Implicit inputs:   Implicit inputs:
    %bubble_lines_per_response   - Starting line numbers for each question.     %bubble_lines_per_response   - Starting line numbers for each question.
Line 7355  for multi and missing bubble cases). Line 7558  for multi and missing bubble cases).
 =cut  =cut
   
 sub prompt_for_corrections {  sub prompt_for_corrections {
     my ($r, $question, $scan_config, $scan_record, $error) = @_;      my ($r, $question, $scan_config, $scan_record, $error, $randomorder,
           $randompick, $respnumlookup, $startline) = @_;
     my ($current_line,$lines);      my ($current_line,$lines);
     my @linenums;      my @linenums;
     my $questionnum = $question;      my $questionnum = $question;
       my ($first,$responsenum);
     if ($question =~ /^(\d+)\.(\d+)$/) {      if ($question =~ /^(\d+)\.(\d+)$/) {
         $question = $1;          $question = $1;
         $current_line = $first_bubble_line{$question-1} + 1 ;  
         my $subquestion = $2;          my $subquestion = $2;
         my @subans = split(/,/,$subdivided_bubble_lines{$question-1});          if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
               $responsenum = $respnumlookup->{$question-1};
               if (ref($startline) eq 'HASH') {
                   $first = $startline->{$question-1};
               }
           } else {
               $responsenum = $question-1;
               $first = $first_bubble_line{$responsenum} + 1;
           }
           $current_line = $first + 1 ;
           my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
         my $subcount = 1;          my $subcount = 1;
         while ($subcount<$subquestion) {          while ($subcount<$subquestion) {
             $current_line += $subans[$subcount-1];              $current_line += $subans[$subcount-1];
Line 7371  sub prompt_for_corrections { Line 7585  sub prompt_for_corrections {
         }          }
         $lines = $subans[$subquestion-1];          $lines = $subans[$subquestion-1];
     } else {      } else {
         $current_line = $first_bubble_line{$question-1} + 1 ;          if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
         $lines        = $bubble_lines_per_response{$question-1};              $responsenum = $respnumlookup->{$question-1};
               if (ref($startline) eq 'HASH') { 
                   $first = $startline->{$question-1};
               }
           } else {
               $responsenum = $question-1;
               $first = $first_bubble_line{$responsenum};
           }
           $current_line = $first + 1;
           $lines        = $bubble_lines_per_response{$responsenum};
     }      }
     if ($lines > 1) {      if ($lines > 1) {
         $r->print(&mt('The group of bubble lines below responds to a single question.').'<br />');          $r->print(&mt('The group of bubble lines below responds to a single question.').'<br />');
         if (($responsetype_per_response{$question-1} eq 'essayresponse') ||          if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
             ($responsetype_per_response{$question-1} eq 'formularesponse') ||              ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
             ($responsetype_per_response{$question-1} eq 'stringresponse') ||              ($responsetype_per_response{$responsenum} eq 'stringresponse') ||
             ($responsetype_per_response{$question-1} eq 'imageresponse') ||              ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
             ($responsetype_per_response{$question-1} eq 'reactionresponse') ||              ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
             ($responsetype_per_response{$question-1} eq 'organicresponse')) {              ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
             $r->print(              $r->print(
                 &mt("Although this particular question type requires handgrading, the instructions for this question in the bubblesheet exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines)                  &mt("Although this particular question type requires handgrading, the instructions for this question in the bubblesheet exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines)
                .'<br /><br />'                 .'<br /><br />'
Line 7398  sub prompt_for_corrections { Line 7621  sub prompt_for_corrections {
     }      }
     for (my $i =0; $i < $lines; $i++) {      for (my $i =0; $i < $lines; $i++) {
         my $selected = $$scan_record{"scantron.$current_line.answer"};          my $selected = $$scan_record{"scantron.$current_line.answer"};
  &scantron_bubble_selector($r,$scan_config,$current_line,    &scantron_bubble_selector($r,$scan_config,$current_line,
           $questionnum,$error,split('', $selected));            $questionnum,$error,split('', $selected));
         push(@linenums,$current_line);          push(@linenums,$current_line);
  $current_line++;   $current_line++;
Line 7652  sub scantron_validate_doublebubble { Line 7875  sub scantron_validate_doublebubble {
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my (undef,undef,$sequence)=
           &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
       my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb,%orderedforcode,%respnumlookup,%startline);
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
   
     my $nav_error;      my $nav_error;
       if (ref($map)) {
           $randomorder = $map->randomorder();
           $randompick = $map->randompick();
           if ($randomorder || $randompick) {
               $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
               if ($nav_error) {
                   $r->print(&navmap_errormsg());
                   return(1,$currentphase);
               }
               &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                                       \%grader_randomlists_by_symb,$bubbles_per_row);
           }
       } else {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
     &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble line array.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble line array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
Line 7667  sub scantron_validate_doublebubble { Line 7921  sub scantron_validate_doublebubble {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
  my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,   my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
  $scan_data);   $scan_data,undef,\%idmap,$randomorder,
                                                    $randompick,$sequence,\@master_seq,
                                                    \%symb_to_resource,\%grader_partids_by_symb,
                                                    \%orderedforcode,\%respnumlookup,\%startline);
  if (!defined($$scan_record{'scantron.doubleerror'})) { next; }   if (!defined($$scan_record{'scantron.doubleerror'})) { next; }
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line,
  'doublebubble',   'doublebubble',
  $$scan_record{'scantron.doubleerror'});   $$scan_record{'scantron.doubleerror'},
                                    $randomorder,$randompick,\%respnumlookup,\%startline);
     return (1,$currentphase);      return (1,$currentphase);
     }      }
     return (0,$currentphase+1);      return (0,$currentphase+1);
Line 7710  sub scantron_get_maxbubble { Line 7968  sub scantron_get_maxbubble {
     %first_bubble_line         = ();      %first_bubble_line         = ();
     %subdivided_bubble_lines   = ();      %subdivided_bubble_lines   = ();
     %responsetype_per_response = ();      %responsetype_per_response = ();
       %masterseq_id_responsenum  = ();
   
     my $response_number = 0;      my $response_number = 0;
     my $bubble_line     = 0;      my $bubble_line     = 0;
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
           my $resid = $resource->id(); 
         my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,          my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,
                                                           $udom,undef,$bubbles_per_row);                                                            $udom,undef,$bubbles_per_row);
         if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {          if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
Line 7764  sub scantron_get_maxbubble { Line 8024  sub scantron_get_maxbubble {
         $bubble_lines_per_response{$response_number} = $lines;          $bubble_lines_per_response{$response_number} = $lines;
                 $responsetype_per_response{$response_number} =                   $responsetype_per_response{$response_number} = 
                     $analysis->{$part_id.'.type'};                      $analysis->{$part_id.'.type'};
                   $masterseq_id_responsenum{$resid.'_'.$part_id} = $response_number;  
         $response_number++;          $response_number++;
   
         $bubble_line +=  $lines;          $bubble_line +=  $lines;
Line 7796  sub scantron_validate_missingbubbles { Line 8057  sub scantron_validate_missingbubbles {
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my (undef,undef,$sequence)=
           &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
       my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb,%orderedforcode,%respnumlookup,%startline);
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
   
     my $nav_error;      my $nav_error;
       if (ref($map)) {
           $randomorder = $map->randomorder();
           $randompick = $map->randompick();
           if ($randomorder || $randompick) {
               $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
               if ($nav_error) {
                   $r->print(&navmap_errormsg());
                   return(1,$currentphase);
               }
               &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                                       \%grader_randomlists_by_symb,$bubbles_per_row);
           }
       } else {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
   
     my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);      my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
           $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
     }      }
   
     if (!$max_bubble) { $max_bubble=2**31; }      if (!$max_bubble) { $max_bubble=2**31; }
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
  my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,   my $scan_record =
  $scan_data);              &scantron_parse_scanline($line,$i,\%scantron_config,$scan_data,undef,\%idmap,
        $randomorder,$randompick,$sequence,\@master_seq,
                                        \%symb_to_resource,\%grader_partids_by_symb,
                                        \%orderedforcode,\%respnumlookup,\%startline);
  if (!defined($$scan_record{'scantron.missingerror'})) { next; }   if (!defined($$scan_record{'scantron.missingerror'})) { next; }
  my @to_correct;   my @to_correct;
   
Line 7821  sub scantron_validate_missingbubbles { Line 8120  sub scantron_validate_missingbubbles {
             if ($missing =~ /^(\d+)\.(\d+)$/) {              if ($missing =~ /^(\d+)\.(\d+)$/) {
                my $question = $1;                 my $question = $1;
                my $subquestion = $2;                 my $subquestion = $2;
                if (!defined($first_bubble_line{$question -1})) { next; }                 my ($first,$responsenum);
                my $first = $first_bubble_line{$question-1};                 if ($randomorder || $randompick) {
                my @subans = split(/,/,$subdivided_bubble_lines{$question-1});                     $responsenum = $respnumlookup{$question-1};
                      $first = $startline{$question-1};
                  } else {
                      $responsenum = $question-1; 
                      $first = $first_bubble_line{$responsenum};
                  }
                  if (!defined($first)) { next; }
                  my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
                my $subcount = 1;                 my $subcount = 1;
                while ($subcount<$subquestion) {                 while ($subcount<$subquestion) {
                    $first += $subans[$subcount-1];                     $first += $subans[$subcount-1];
Line 7832  sub scantron_validate_missingbubbles { Line 8138  sub scantron_validate_missingbubbles {
                my $count = $subans[$subquestion-1];                 my $count = $subans[$subquestion-1];
                $lastbubble = $first + $count;                 $lastbubble = $first + $count;
             } else {              } else {
                 if (!defined($first_bubble_line{$missing - 1})) { next; }                 my ($first,$responsenum);
                 $lastbubble = $first_bubble_line{$missing - 1} + $bubble_lines_per_response{$missing - 1};                 if ($randomorder || $randompick) {
                      $responsenum = $respnumlookup{$missing-1};
                      $first = $startline{$missing-1};
                  } else {
                      $responsenum = $missing-1;
                      $first = $first_bubble_line{$responsenum};
                  }
                  if (!defined($first)) { next; }
                  $lastbubble = $first + $bubble_lines_per_response{$responsenum};
             }              }
             if ($lastbubble > $max_bubble) { next; }              if ($lastbubble > $max_bubble) { next; }
     push(@to_correct,$missing);      push(@to_correct,$missing);
  }   }
  if (@to_correct) {   if (@to_correct) {
     &scantron_get_correction($r,$i,$scan_record,\%scantron_config,      &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
      $line,'missingbubble',\@to_correct);       $line,'missingbubble',\@to_correct,
                                        $randomorder,$randompick,\%respnumlookup,
                                        \%startline);
     return (1,$currentphase);      return (1,$currentphase);
  }   }
   
Line 7894  sub scantron_process_students { Line 8210  sub scantron_process_students {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row =      my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); 
         &bubblesheet_bubbles_per_row(\%scantron_config);  
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
Line 7903  sub scantron_process_students { Line 8218  sub scantron_process_students {
     unless (ref($navmap)) {      unless (ref($navmap)) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
     }        }
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my ($randomorder,$randompick);      my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb);
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
       } else {
           $r->print(&navmap_errormsg());
           return '';
     }      }
       my $nav_error;
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     my (%grader_partids_by_symb,%grader_randomlists_by_symb,%ordered);      if ($randomorder || $randompick) {
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,          $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
                             \%grader_randomlists_by_symb,$bubbles_per_row);          if ($nav_error) {
     my ($resource_error,%symb_to_resource,@master_seq);              $r->print(&navmap_errormsg());
     foreach my $resource (@resources) {              return '';
         my $ressymb;  
         if (ref($resource)) {  
             $ressymb = $resource->symb();  
             push(@master_seq,$ressymb);  
             $symb_to_resource{$ressymb} = $resource;  
         } else {  
             $resource_error = 1;  
             last;  
         }          }
         my ($analysis,$parts) =  
             &scantron_partids_tograde($resource,$env{'request.course.id'},  
                                       $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') {  
                 $grader_randomlists_by_symb{$ressymb} =   
                     $analysis->{'parts_withrandomlist'};  
             }  
         }  
     }  
     if ($resource_error) {  
         $r->print(&navmap_errormsg());  
         return '';  
     }      }
       &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                               \%grader_randomlists_by_symb,$bubbles_per_row);
   
     my ($uname,$udom);      my ($uname,$udom);
     my $result= <<SCANTRONFORM;      my $result= <<SCANTRONFORM;
Line 7962  SCANTRONFORM Line 8261  SCANTRONFORM
     my $i=-1;      my $i=-1;
     my $started;      my $started;
   
     my $nav_error;  
     &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
Line 7981  SCANTRONFORM Line 8279  SCANTRONFORM
   
     my %lettdig = &letter_to_digits();      my %lettdig = &letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
       my %orderedforcode;
   
     while ($i<$scanlines->{'count'}) {      while ($i<$scanlines->{'count'}) {
   ($uname,$udom)=('','');    ($uname,$udom)=('','');
Line 7991  SCANTRONFORM Line 8290  SCANTRONFORM
     &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student');      &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student');
  }   }
  $started=1;   $started=1;
           my %respnumlookup = ();
           my %startline = ();
           my $total;
   my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
   $scan_data);                                                   $scan_data,undef,\%idmap,$randomorder,
                                                    $randompick,$sequence,\@master_seq,
                                                    \%symb_to_resource,\%grader_partids_by_symb,
                                                    \%orderedforcode,\%respnumlookup,\%startline,
                                                    \$total);
   unless ($uname=&scantron_find_student($scan_record,$scan_data,    unless ($uname=&scantron_find_student($scan_record,$scan_data,
        \%idmap,$i)) {         \%idmap,$i)) {
      &scantron_add_delay(\@delayqueue,$line,       &scantron_add_delay(\@delayqueue,$line,
Line 8019  SCANTRONFORM Line 8325  SCANTRONFORM
         my @mapresources = @resources;          my @mapresources = @resources;
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             @mapresources =               @mapresources = 
                 &users_order($user,$scancode,$sequence,\@master_seq,\%ordered,                  &users_order($user,$scancode,$sequence,\@master_seq,\%symb_to_resource,
                              \%symb_to_resource);                               \%orderedforcode);
         }          }
         my (%partids_by_symb,$res_error);          my (%partids_by_symb,$res_error);
         foreach my $resource (@mapresources) {          foreach my $resource (@mapresources) {
Line 8057  SCANTRONFORM Line 8363  SCANTRONFORM
   
         if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,          if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                    \@mapresources,\%partids_by_symb,                                     \@mapresources,\%partids_by_symb,
                                    $bubbles_per_row) eq 'ssi_error') {                                     $bubbles_per_row,$randomorder,$randompick,
                                      \%respnumlookup,\%startline) 
               eq 'ssi_error') {
             $ssi_error = 0; # So end of handler error message does not trigger.              $ssi_error = 0; # So end of handler error message does not trigger.
             $r->print("</form>");              $r->print("</form>");
             &ssi_print_error($r);              &ssi_print_error($r);
Line 8065  SCANTRONFORM Line 8373  SCANTRONFORM
             return '';      # Why return ''?  Beats me.              return '';      # Why return ''?  Beats me.
         }          }
   
           if (($scancode) && ($randomorder || $randompick)) {
               my $parmresult =
                   &Apache::lonparmset::storeparm_by_symb($symb,
                                                          '0_examcode',2,$scancode,
                                                          'string_examcode',$uname,
                                                          $udom);
           }
  $completedstudents{$uname}={'line'=>$line};   $completedstudents{$uname}={'line'=>$line};
         if ($env{'form.verifyrecord'}) {          if ($env{'form.verifyrecord'}) {
             my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};              my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};
               if ($randompick) {
                   if ($total) {
                       $lastpos = $total*$scantron_config{'Qlength'};
                   }
               }
   
             my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos);              my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos);
             chomp($studentdata);              chomp($studentdata);
             $studentdata =~ s/\r$//;              $studentdata =~ s/\r$//;
Line 8078  SCANTRONFORM Line 8399  SCANTRONFORM
                 ($counter,my $recording) =                  ($counter,my $recording) =
                     &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},                      &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                              $counter,$studentdata,$partids_by_symb{$ressymb},                                               $counter,$studentdata,$partids_by_symb{$ressymb},
                                              \%scantron_config,\%lettdig,$numletts);                                               \%scantron_config,\%lettdig,$numletts,$randomorder,
                                                $randompick,\%respnumlookup,\%startline);
                 $studentrecord .= $recording;                  $studentrecord .= $recording;
             }              }
             if ($studentrecord ne $studentdata) {              if ($studentrecord ne $studentdata) {
                 &Apache::lonxml::clear_problem_counter();                  &Apache::lonxml::clear_problem_counter();
                 if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,                  if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                            \@mapresources,\%partids_by_symb,                                             \@mapresources,\%partids_by_symb,
                                            $bubbles_per_row) eq 'ssi_error') {                                             $bubbles_per_row,$randomorder,$randompick,
                                              \%respnumlookup,\%startline) 
                       eq 'ssi_error') {
                     $ssi_error = 0; # So end of handler error message does not trigger.                      $ssi_error = 0; # So end of handler error message does not trigger.
                     $r->print("</form>");                      $r->print("</form>");
                     &ssi_print_error($r);                      &ssi_print_error($r);
Line 8100  SCANTRONFORM Line 8424  SCANTRONFORM
                     ($counter,my $recording) =                      ($counter,my $recording) =
                         &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},                          &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                                  $counter,$studentdata,$partids_by_symb{$ressymb},                                                   $counter,$studentdata,$partids_by_symb{$ressymb},
                                                  \%scantron_config,\%lettdig,$numletts);                                                   \%scantron_config,\%lettdig,$numletts,
                                                    $randomorder,$randompick,\%respnumlookup,
                                                    \%startline);
                     $studentrecord .= $recording;                      $studentrecord .= $recording;
                 }                  }
                 if ($studentrecord ne $studentdata) {                  if ($studentrecord ne $studentdata) {
Line 8178  sub graders_resources_pass { Line 8504  sub graders_resources_pass {
   if this is a CODEd exam, or based on student's identity if this is a     if this is a CODEd exam, or based on student's identity if this is a 
   "NAMEd" exam.    "NAMEd" exam.
   
   Should be used when randomorder applied when the corresponding exam was    Should be used when randomorder and/or randompick applied when the 
   printed, prior to students completing bubblesheets for the version of the    corresponding exam was printed, prior to students completing bubblesheets 
   exam the student received.     for the version of the exam the student received.
   
 =cut  =cut
   
 sub users_order  {  sub users_order  {
     my ($user,$scancode,$mapurl,$master_seq,$ordered,$symb_to_resource) = @_;      my ($user,$scancode,$mapurl,$master_seq,$symb_to_resource,$orderedforcode) = @_;
     my @mapresources;      my @mapresources;
     unless ((ref($ordered) eq 'HASH') && (ref($symb_to_resource) eq 'HASH')) {      unless ((ref($master_seq) eq 'ARRAY') && (ref($symb_to_resource) eq 'HASH')) {
         return @mapresources;          return @mapresources;
     }        }
     if (($scancode) && (ref($ordered->{$scancode}) eq 'ARRAY')) {      if ($scancode) {
         @mapresources = @{$ordered->{$scancode}};          if ((ref($orderedforcode) eq 'HASH') && (ref($orderedforcode->{$scancode}) eq 'ARRAY')) {
     } elsif ($scancode) {              @mapresources = @{$orderedforcode->{$scancode}};
         $env{'form.CODE'} = $scancode;          } else {
         my $actual_seq =              $env{'form.CODE'} = $scancode;
             &Apache::lonprintout::master_seq_to_person_seq($mapurl,              my $actual_seq =
                                                            $master_seq,                  &Apache::lonprintout::master_seq_to_person_seq($mapurl,
                                                            $user,$scancode,1);                                                                 $master_seq,
         if (ref($actual_seq) eq 'ARRAY') {                                                                 $user,$scancode,1);
             @{$ordered->{$scancode}} =              if (ref($actual_seq) eq 'ARRAY') {
                 map { $symb_to_resource->{$_}; } @{$actual_seq};                  @mapresources = map { $symb_to_resource->{$_}; } @{$actual_seq};
             @mapresources = @{$ordered->{$scancode}};                  if (ref($orderedforcode) eq 'HASH') {
                       if (@mapresources > 0) { 
                           $orderedforcode->{$scancode} = \@mapresources;
                       }
                   }
               }
               delete($env{'form.CODE'});
         }          }
         delete($env{'form.CODE'});  
     } else {      } else {
         my $actual_seq =          my $actual_seq =
             &Apache::lonprintout::master_seq_to_person_seq($mapurl,              &Apache::lonprintout::master_seq_to_person_seq($mapurl,
Line 8213  sub users_order  { Line 8544  sub users_order  {
             @mapresources =               @mapresources = 
                 map { $symb_to_resource->{$_}; } @{$actual_seq};                  map { $symb_to_resource->{$_}; } @{$actual_seq};
         }          }
      }      }
      return @mapresources;      return @mapresources;
 }  }
   
 sub grade_student_bubbles {  sub grade_student_bubbles {
     my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts,$bubbles_per_row) = @_;       my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts,$bubbles_per_row,
           $randomorder,$randompick,$respnumlookup,$startline) = @_;
       my $uselookup = 0;
       if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH') &&
           (ref($startline) eq 'HASH')) {
           $uselookup = 1;
       }
   
     if (ref($resources) eq 'ARRAY') {      if (ref($resources) eq 'ARRAY') {
         my $count = 0;          my $count = 0;
         foreach my $resource (@{$resources}) {          foreach my $resource (@{$resources}) {
Line 8240  sub grade_student_bubbles { Line 8578  sub grade_student_bubbles {
             if (ref($parts) eq 'HASH') {              if (ref($parts) eq 'HASH') {
                 if (ref($parts->{$ressymb}) eq 'ARRAY') {                  if (ref($parts->{$ressymb}) eq 'ARRAY') {
                     foreach my $part (@{$parts->{$ressymb}}) {                      foreach my $part (@{$parts->{$ressymb}}) {
                         $form{'scantron_questnum_start.'.$part} =                          if ($uselookup) {
                             1+$env{'form.scantron.first_bubble_line.'.$count};                              $form{'scantron_questnum_start.'.$part} = $startline->{$count} + 1;
                           } else {
                               $form{'scantron_questnum_start.'.$part} =
                                   1+$env{'form.scantron.first_bubble_line.'.$count};
                           }
                         $count++;                          $count++;
                     }                      }
                 }                  }
Line 8522  sub checkscantron_results { Line 8864  sub checkscantron_results {
         return '';          return '';
     }      }
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my ($randomorder,$randompick,@master_seq,%symb_to_resource);      my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb,%orderedforcode);
     if (ref($map)) {       if (ref($map)) { 
         $randomorder=$map->randomorder();          $randomorder=$map->randomorder();
         $randompick=$map->randompick();          $randompick=$map->randompick();
     }      }
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     foreach my $resource (@resources) {      my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
         if (ref($resource)) {      if ($nav_error) {
             my $ressymb = $resource->symb();          $r->print(&navmap_errormsg());
             push(@master_seq,$ressymb);          return '';
             $symb_to_resource{$ressymb} = $resource;  
         }  
     }      }
     my (%grader_partids_by_symb,%grader_randomlists_by_symb);  
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,      &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                             \%grader_randomlists_by_symb,$bubbles_per_row);                              \%grader_randomlists_by_symb,$bubbles_per_row);
     my ($uname,$udom);      my ($uname,$udom);
Line 8546  sub checkscantron_results { Line 8886  sub checkscantron_results {
     my @delayqueue;      my @delayqueue;
     my %completedstudents;      my %completedstudents;
   
     my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);      my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
     my ($username,$domain,$started,%ordered);      my ($username,$domain,$started,%ordered);
     my $nav_error;  
     &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
Line 8572  sub checkscantron_results { Line 8911  sub checkscantron_results {
         my $scan_record=          my $scan_record=
             &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,              &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,
                                                      $scan_data);                                                       $scan_data);
         unless ($uname=&Apache::grades::scantron_find_student($scan_record,$scan_data,          unless ($uname=&scantron_find_student($scan_record,$scan_data,
                                                               \%idmap,$i)) {                                                \%idmap,$i)) {
             &Apache::grades::scantron_add_delay(\@delayqueue,$line,              &Apache::grades::scantron_add_delay(\@delayqueue,$line,
                                 'Unable to find a student that matches',1);                                  'Unable to find a student that matches',1);
             next;              next;
Line 8586  sub checkscantron_results { Line 8925  sub checkscantron_results {
         my $pid = $scan_record->{'scantron.ID'};          my $pid = $scan_record->{'scantron.ID'};
         $lastname{$pid} = $scan_record->{'scantron.LastName'};          $lastname{$pid} = $scan_record->{'scantron.LastName'};
         push(@{$bylast{$lastname{$pid}}},$pid);          push(@{$bylast{$lastname{$pid}}},$pid);
         my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};  
         $scandata{$pid} = substr($line,$scantron_config{'Qstart'}-1,$lastpos);  
         chomp($scandata{$pid});  
         $scandata{$pid} =~ s/\r$//;  
         my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];          my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
         my $user = $uname.':'.$usec;          my $user = $uname.':'.$usec;
         ($username,$domain)=split(/:/,$uname);          ($username,$domain)=split(/:/,$uname);
Line 8603  sub checkscantron_results { Line 8938  sub checkscantron_results {
         }          }
   
         my @mapresources = @resources;          my @mapresources = @resources;
           my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};
           my %respnumlookup=();
           my %startline=();
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             @mapresources =              @mapresources =
                 &users_order($user,$scancode,$sequence,\@master_seq,\%ordered,                  &users_order($user,$scancode,$sequence,\@master_seq,\%symb_to_resource,
                              \%symb_to_resource);                               \%orderedforcode);
               my $total = &get_respnum_lookups($sequence,$scan_data,\%idmap,$line,
                                                $scan_record,\@master_seq,\%symb_to_resource,
                                                \%grader_partids_by_symb,\%orderedforcode,
                                                \%respnumlookup,\%startline);
               if ($randompick && $total) {
                   $lastpos = $total*$scantron_config{'Qlength'};
               }
         }          }
           $scandata{$pid} = substr($line,$scantron_config{'Qstart'}-1,$lastpos);
           chomp($scandata{$pid});
           $scandata{$pid} =~ s/\r$//;
   
         my $counter = -1;          my $counter = -1;
         foreach my $resource (@mapresources) {          foreach my $resource (@mapresources) {
             my $parts;              my $parts;
Line 8624  sub checkscantron_results { Line 8973  sub checkscantron_results {
             ($counter,my $recording) =              ($counter,my $recording) =
                 &verify_scantron_grading($resource,$domain,$username,$cid,$counter,                  &verify_scantron_grading($resource,$domain,$username,$cid,$counter,
                                          $scandata{$pid},$parts,                                           $scandata{$pid},$parts,
                                          \%scantron_config,\%lettdig,$numletts);                                           \%scantron_config,\%lettdig,$numletts,
                                            $randomorder,$randompick,
                                            \%respnumlookup,\%startline);
             $record{$pid} .= $recording;              $record{$pid} .= $recording;
         }          }
     }      }
Line 8702  sub checkscantron_results { Line 9053  sub checkscantron_results {
   
 sub verify_scantron_grading {  sub verify_scantron_grading {
     my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,      my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,
         $scantron_config,$lettdig,$numletts) = @_;          $scantron_config,$lettdig,$numletts,$randomorder,$randompick,
           $respnumlookup,$startline) = @_;
     my ($record,%expected,%startpos);      my ($record,%expected,%startpos);
     return ($counter,$record) if (!ref($resource));      return ($counter,$record) if (!ref($resource));
     return ($counter,$record) if (!$resource->is_problem());      return ($counter,$record) if (!$resource->is_problem());
Line 8711  sub verify_scantron_grading { Line 9063  sub verify_scantron_grading {
     foreach my $part_id (@{$partids}) {      foreach my $part_id (@{$partids}) {
         $counter ++;          $counter ++;
         $expected{$part_id} = 0;          $expected{$part_id} = 0;
         if ($env{"form.scantron.sub_bubblelines.$counter"}) {          my $respnum = $counter;
             my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"});          if ($randomorder || $randompick) {
               $respnum = $respnumlookup->{$counter};
               $startpos{$part_id} = $startline->{$counter} + 1;
           } else {
               $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};
           }
           if ($env{"form.scantron.sub_bubblelines.$respnum"}) {
               my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$respnum"});
             foreach my $item (@sub_lines) {              foreach my $item (@sub_lines) {
                 $expected{$part_id} += $item;                  $expected{$part_id} += $item;
             }              }
         } else {          } else {
             $expected{$part_id} = $env{"form.scantron.bubblelines.$counter"};              $expected{$part_id} = $env{"form.scantron.bubblelines.$respnum"};
         }          }
         $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};  
     }      }
     if ($symb) {      if ($symb) {
         my %recorded;          my %recorded;
Line 8815  sub verify_scantron_grading { Line 9173  sub verify_scantron_grading {
     return ($counter,$record);      return ($counter,$record);
 }  }
   
 sub letter_to_digits {   sub letter_to_digits {
     my %lettdig = (      my %lettdig = (
                     A => 1,                      A => 1,
                     B => 2,                      B => 2,
Line 9453  ENDHEADER Line 9811  ENDHEADER
              &mt('Found [_1] registered and [_2] unregistered clickers.',$student_count,$unknown_count);               &mt('Found [_1] registered and [_2] unregistered clickers.',$student_count,$unknown_count);
     if (($env{'form.gradingmechanism'} ne 'attendance') && ($env{'form.gradingmechanism'} ne 'given')) {      if (($env{'form.gradingmechanism'} ne 'attendance') && ($env{'form.gradingmechanism'} ne 'given')) {
        if ($correct_count==0) {         if ($correct_count==0) {
           $errormsg.="Found no correct answers answers for grading!";            $errormsg.="Found no correct answers for grading!";
        } elsif ($correct_count>1) {         } elsif ($correct_count>1) {
           $result.='<br /><span class="LC_warning">'.&mt("Found [_1] entries for grading!",$correct_count).'</span>';            $result.='<br /><span class="LC_warning">'.&mt("Found [_1] entries for grading!",$correct_count).'</span>';
        }         }
Line 9625  sub assign_clicker_grades { Line 9983  sub assign_clicker_grades {
        if ($user) {         if ($user) {
           if ($users{$user}) {            if ($users{$user}) {
              $result.='<br /><span class="LC_warning">'.               $result.='<br /><span class="LC_warning">'.
                       &mt("More than one entry found for <tt>[_1]</tt>!",$user).                        &mt('More than one entry found for [_1]!','<tt>'.$user.'</tt>').
                       '</span><br />';                        '</span><br />';
           }            }
           $users{$user}=1;             $users{$user}=1; 
Line 10098  Side Effects: None. Line 10456  Side Effects: None.
          - missingbubble - array ref of the bubble lines that have missing           - missingbubble - array ref of the bubble lines that have missing
                            bubble errors                             bubble errors
   
      $randomorder - True if exam folder has randomorder set
      $randompick  - True if exam folder has randompick set
      $respnumlookup - Reference to HASH mapping question numbers in bubble lines
                        for current line to question number used for same question
                        in "Master Seqence" (as seen by Course Coordinator).
      $startline   - Reference to hash where key is question number (0 is first)
                     and value is number of first bubble line for current student
                     or code-based randompick and/or randomorder.
   
   
   
 =item  scantron_get_maxbubble() :   =item  scantron_get_maxbubble() : 
   
    Arguments:     Arguments:
Line 10118  Side Effects: None. Line 10487  Side Effects: None.
    $env{'form.scantron.bubble_lines.n'},      $env{'form.scantron.bubble_lines.n'}, 
    $env{'form.scantron.first_bubble_line.n'} and     $env{'form.scantron.first_bubble_line.n'} and
    $env{"form.scantron.sub_bubblelines.n"}     $env{"form.scantron.sub_bubblelines.n"}
    which are the total number of bubble, lines, the number of bubble     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,     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     and a comma separated list of numbers of bubble lines for sub-questions
    (for optionresponse, matchresponse, and rankresponse items), for response n.       (for optionresponse, matchresponse, and rankresponse items), for response n.  

Removed from v.1.690  
changed lines
  Added in v.1.702


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>