Diff for /loncom/homework/grades.pm between versions 1.432 and 1.551

version 1.432, 2007/08/21 22:21:54 version 1.551, 2009/02/14 19:45:26
Line 26 Line 26
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
   
   
   
 package Apache::grades;  package Apache::grades;
 use strict;  use strict;
 use Apache::style;  use Apache::style;
Line 35  use Apache::loncommon; Line 37  use Apache::loncommon;
 use Apache::lonhtmlcommon;  use Apache::lonhtmlcommon;
 use Apache::lonnavmaps;  use Apache::lonnavmaps;
 use Apache::lonhomework;  use Apache::lonhomework;
   use Apache::lonpickcode;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use Apache::lonmsg();  use Apache::lonmsg();
 use Apache::Constants qw(:common);  use Apache::Constants qw(:common);
Line 45  use LONCAPA; Line 48  use LONCAPA;
   
 use POSIX qw(floor);  use POSIX qw(floor);
   
   
   
 my %perm=();  my %perm=();
   
 # ----- These first few routines are general use routines.----  #  These variables are used to recover from ssi errors
   
   my $ssi_retries = 5;
   my $ssi_error;
   my $ssi_error_resource;
   my $ssi_error_message;
   
   
   sub ssi_with_retries {
       my ($resource, $retries, %form) = @_;
       my ($content, $response) = &Apache::loncommon::ssi_with_retries($resource, $retries, %form);
       if ($response->is_error) {
    $ssi_error          = 1;
    $ssi_error_resource = $resource;
    $ssi_error_message  = $response->code . " " . $response->message;
       }
   
       return $content;
   
   }
   #
   #  Prodcuces an ssi retry failure error message to the user:
   #
   
   sub ssi_print_error {
       my ($r) = @_;
       my $helpurl = &Apache::loncommon::top_nav_help('Helpdesk');
       $r->print('
   <br />
   <h2>'.&mt('An unrecoverable network error occurred:').'</h2>
   <p>
   '.&mt('Unable to retrieve a resource from a server:').'<br />
   '.&mt('Resource:').' '.$ssi_error_resource.'<br />
   '.&mt('Error:').' '.$ssi_error_message.'
   </p>
   <p>'.
   &mt('It is recommended that you try again later, as this error may mean the server was just temporarily unavailable, or is down for maintenance.').'<br />'.
   &mt('If the error persists, please contact the [_1] for assistance.',$helpurl).
   '</p>');
       return;
   }
   
 #  #
 # --- Retrieve the parts from the metadata file.---  # --- Retrieve the parts from the metadata file.---
 sub getpartlist {  sub getpartlist {
     my ($symb) = @_;      my ($symb) = @_;
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);  
     my $partorder = &Apache::lonnet::metadata($url, 'partorder');      my $navmap   = Apache::lonnavmaps::navmap->new();
     my @parts;      my $res      = $navmap->getBySymb($symb);
     if ($partorder) {      my $partlist = $res->parts();
  for my $part (split (/,/,$partorder)) {      my $url      = $res->src();
     if (!&Apache::loncommon::check_if_partid_hidden($part,$symb)) {      my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));
  push(@parts, $part);  
     }  
  }      
     } else {  
  my $metadata = &Apache::lonnet::metadata($url, 'packages');  
  foreach (split(/\,/,$metadata)) {  
     if ($_ =~ /^part_(.*)$/) {  
  if (!&Apache::loncommon::check_if_partid_hidden($1,$symb)) {  
     push(@parts, $1);  
  }  
     }  
  }  
     }  
     my @stores;      my @stores;
     foreach my $part (@parts) {      foreach my $part (@{ $partlist }) {
  my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));  
  foreach my $key (@metakeys) {   foreach my $key (@metakeys) {
     if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); }      if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); }
  }   }
Line 101  sub get_symb { Line 134  sub get_symb {
 sub nameUserString {  sub nameUserString {
     my ($type,$fullname,$uname,$udom) = @_;      my ($type,$fullname,$uname,$udom) = @_;
     if ($type eq 'header') {      if ($type eq 'header') {
  return '<b>&nbsp;Fullname&nbsp;</b><span class="LC_internal_info">(Username)</span>';   return '<b>&nbsp;'.&mt('Fullname').'&nbsp;</b><span class="LC_internal_info">('.&mt('Username').')</span>';
     } else {      } else {
  return '&nbsp;'.$fullname.'<span class="LC_internal_info">&nbsp;('.$uname.   return '&nbsp;'.$fullname.'<span class="LC_internal_info">&nbsp;('.$uname.
     ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</span>';      ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</span>';
Line 169  sub showResourceInfo { Line 202  sub showResourceInfo {
     my %resptype = ();      my %resptype = ();
     my $hdgrade='no';      my $hdgrade='no';
     my %partsseen;      my %partsseen;
     foreach my $partID (sort keys(%$responseType)) {      foreach my $partID (sort(keys(%$responseType))) {
  foreach my $resID (sort keys(%{ $responseType->{$partID} })) {   foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {
     my $handgrade=$$handgrade{$partID.'_'.$resID};      my $handgrade=$$handgrade{$partID.'_'.$resID};
     my $responsetype = $responseType->{$partID}->{$resID};      my $responsetype = $responseType->{$partID}->{$resID};
     $hdgrade = $handgrade if ($handgrade eq 'yes');      $hdgrade = $handgrade if ($handgrade eq 'yes');
Line 184  sub showResourceInfo { Line 217  sub showResourceInfo {
  $partsseen{$partID}=1;   $partsseen{$partID}=1;
     }      }
     my $display_part=&get_display_part($partID,$symb);      my $display_part=&get_display_part($partID,$symb);
     $result.='<td><b>Part: </b>'.$display_part.' <span class="LC_internal_info">'.      $result.='<td><b>'.&mt('Part').': </b>'.$display_part.
  $resID.'</span></td>'.                  ' <span class="LC_internal_info">'.$resID.'</span></td>'.
  '<td><b>Type: </b>'.$responsetype.'</td></tr>';   '<td><b>'.&mt('Type').': </b>'.$responsetype.'</td></tr>';
 #    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';  #    '<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td></tr>';
  }   }
     }      }
     $result.='</table>'."\n";      $result.='</table>'."\n";
     return $result,$responseType,$hdgrade,$partlist,$handgrade;      return $result,$responseType,$hdgrade,$partlist,$handgrade;
 }  }
   
   sub reset_caches {
       &reset_analyze_cache();
       &reset_perm();
   }
   
 sub get_order {  {
     my ($partid,$respid,$symb,$uname,$udom)=@_;      my %analyze_cache;
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);  
     $url=&Apache::lonnet::clutter($url);      sub reset_analyze_cache {
     my $subresult=&Apache::lonnet::ssi($url,   undef(%analyze_cache);
        ('grade_target' => 'analyze'),      }
        ('grade_domain' => $udom),  
        ('grade_symb' => $symb),      sub get_analyze {
        ('grade_courseid' =>    my ($symb,$uname,$udom,$no_increment)=@_;
         $env{'request.course.id'}),   my $key = "$symb\0$uname\0$udom";
        ('grade_username' => $uname));   return $analyze_cache{$key} if (exists($analyze_cache{$key}));
     (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);  
     my %analyze=&Apache::lonnet::str2hash($subresult);   my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     return ($analyze{"$partid.$respid.shown"});   $url=&Apache::lonnet::clutter($url);
    my $subresult=&ssi_with_retries($url, $ssi_retries,
      ('grade_target' => 'analyze',
       'grade_domain' => $udom,
       'grade_symb' => $symb,
       'grade_courseid' => 
       $env{'request.course.id'},
       'grade_username' => $uname,
                                               'grade_noincrement' => $no_increment));
    (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
    my %analyze=&Apache::lonnet::str2hash($subresult);
    return $analyze_cache{$key} = \%analyze;
       }
   
       sub get_order {
    my ($partid,$respid,$symb,$uname,$udom,$no_increment)=@_;
    my $analyze = &get_analyze($symb,$uname,$udom,$no_increment);
    return $analyze->{"$partid.$respid.shown"};
       }
   
       sub get_radiobutton_correct_foil {
    my ($partid,$respid,$symb,$uname,$udom)=@_;
    my $analyze = &get_analyze($symb,$uname,$udom);
    foreach my $foil (@{&get_order($partid,$respid,$symb,$uname,$udom)}) {
       if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
    return $foil;
       }
    }
       }
 }  }
   
 #--- Clean response type for display  #--- Clean response type for display
 #--- Currently filters option/rank/radiobutton/match/essay/Task  #--- Currently filters option/rank/radiobutton/match/essay/Task
 #        response types only.  #        response types only.
Line 230  sub cleanRecord { Line 296  sub cleanRecord {
     $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';      $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';
  }   }
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Option ID</span></td>'.      '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
     $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';      $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
     } elsif ($response eq 'match') {      } elsif ($response eq 'match') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
Line 250  sub cleanRecord { Line 316  sub cleanRecord {
     $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';      $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';
  }   }
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Item ID</span></td>'.      '<tr valign="top"><td>'.$grayFont.&mt('Item ID').'</span></td>'.
     $middlerow.'</tr>'.      $middlerow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Option ID</span></td>'.      '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
     $bottomrow.'</tr>'.'</table></blockquote>';      $bottomrow.'</tr>'.'</table></blockquote>';
     } elsif ($response eq 'radiobutton') {      } elsif ($response eq 'radiobutton') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
  my ($toprow,$bottomrow);   my ($toprow,$bottomrow);
  my $correct=($order->[0])+1;   my $correct = 
  for (my $i=1;$i<=$#$order;$i++) {      &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom);
     my $foil=$order->[$i];   foreach my $foil (@$order) {
     if (exists($answer{$foil})) {      if (exists($answer{$foil})) {
  if ($i == $correct) {   if ($foil eq $correct) {
     $toprow.='<td><b>true</b></td>';      $toprow.='<td><b>'.&mt('true').'</b></td>';
  } else {   } else {
     $toprow.='<td><i>true</i></td>';      $toprow.='<td><i>'.&mt('true').'</i></td>';
  }   }
     } else {      } else {
  $toprow.='<td>false</td>';   $toprow.='<td>'.&mt('false').'</td>';
     }      }
     $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';      $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';
  }   }
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Option ID</span></td>'.      '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
     $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';      $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
     } elsif ($response eq 'essay') {      } elsif ($response eq 'essay') {
  if (! exists ($env{'form.'.$symb})) {   if (! exists ($env{'form.'.$symb})) {
Line 326  sub cleanRecord { Line 392  sub cleanRecord {
     $result.='</ul>';      $result.='</ul>';
     return $result;      return $result;
  }   }
              } elsif ( $response =~ m/(?:numerical|formula)/) {
    $answer = 
       &Apache::loncommon::format_previous_attempt_value('submission',
         $answer);
     }      }
     return $answer;      return $answer;
 }  }
Line 370  COMMONJSFUNCTIONS Line 439  COMMONJSFUNCTIONS
 #--- Dumps the class list with usernames,list of sections,  #--- Dumps the class list with usernames,list of sections,
 #--- section, ids and fullnames for each user.  #--- section, ids and fullnames for each user.
 sub getclasslist {  sub getclasslist {
     my ($getsec,$filterlist) = @_;      my ($getsec,$filterlist,$getgroup) = @_;
     my @getsec;      my @getsec;
       my @getgroup;
       my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     if (!ref($getsec)) {      if (!ref($getsec)) {
  if ($getsec ne '' && $getsec ne 'all') {   if ($getsec ne '' && $getsec ne 'all') {
     @getsec=($getsec);      @getsec=($getsec);
Line 380  sub getclasslist { Line 451  sub getclasslist {
  @getsec=@{$getsec};   @getsec=@{$getsec};
     }      }
     if (grep(/^all$/,@getsec)) { undef(@getsec); }      if (grep(/^all$/,@getsec)) { undef(@getsec); }
       if (!ref($getgroup)) {
    if ($getgroup ne '' && $getgroup ne 'all') {
       @getgroup=($getgroup);
    }
       } else {
    @getgroup=@{$getgroup};
       }
       if (grep(/^all$/,@getgroup)) { undef(@getgroup); }
   
     my $classlist=&Apache::loncoursedata::get_classlist();      my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist();
     # Bail out if we were unable to get the classlist      # Bail out if we were unable to get the classlist
     return if (! defined($classlist));      return if (! defined($classlist));
       &Apache::loncoursedata::get_group_memberships($classlist,$keylist);
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
Line 400  sub getclasslist { Line 480  sub getclasslist {
             $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()];              $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()];
         my $status   =           my $status   = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()];              $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()];
           my $group   = 
               $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
  # filter students according to status selected   # filter students according to status selected
  if ($filterlist && $env{'form.Status'} ne 'Any') {   if ($filterlist && (!($stu_status =~ /Any/))) {
     if ($env{'form.Status'} ne $status) {      if (!($stu_status =~ $status)) {
  delete ($classlist->{$student});   delete($classlist->{$student});
  next;   next;
     }      }
  }   }
    # filter students according to groups selected
    my @stu_groups = split(/,/,$group);
    if (@getgroup) {
       my $exclude = 1;
       foreach my $grp (@getgroup) {
           foreach my $stu_group (@stu_groups) {
               if ($stu_group eq $grp) {
                   $exclude = 0;
                  } 
           }
              if (($grp eq 'none') && !$group) {
                  $exclude = 0;
           }
       }
       if ($exclude) {
           delete($classlist->{$student});
       }
    }
  $section = ($section ne '' ? $section : 'none');   $section = ($section ne '' ? $section : 'none');
  if (&canview($section)) {   if (&canview($section)) {
     if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
  $sections{$section}++;   $sections{$section}++;
  $fullnames{$student}=$fullname;   if ($classlist->{$student}) {
       $fullnames{$student}=$fullname;
    }
     } else {      } else {
  delete($classlist->{$student});   delete($classlist->{$student});
     }      }
Line 484  sub student_gradeStatus { Line 586  sub student_gradeStatus {
 # Shows a student's view of problem and submission  # Shows a student's view of problem and submission
 sub jscriptNform {  sub jscriptNform {
     my ($symb) = @_;      my ($symb) = @_;
       my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $jscript='<script type="text/javascript" language="javascript">'."\n".      my $jscript='<script type="text/javascript" language="javascript">'."\n".
  '    function viewOneStudent(user,domain) {'."\n".   '    function viewOneStudent(user,domain) {'."\n".
  ' document.onestudent.student.value = user;'."\n".   ' document.onestudent.student.value = user;'."\n".
Line 495  sub jscriptNform { Line 598  sub jscriptNform {
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n".   '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n".
  '<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".   '<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".
  '<input type="hidden" name="command" value="submission" />'."\n".   '<input type="hidden" name="command" value="submission" />'."\n".
  '<input type="hidden" name="student" value="" />'."\n".   '<input type="hidden" name="student" value="" />'."\n".
  '<input type="hidden" name="userdom" value="" />'."\n".   '<input type="hidden" name="userdom" value="" />'."\n".
Line 503  sub jscriptNform { Line 606  sub jscriptNform {
     return $jscript;      return $jscript;
 }  }
   
   
   
 # Given the score (as a number [0-1] and the weight) what is the final  # Given the score (as a number [0-1] and the weight) what is the final
 # point value? This function will round to the nearest tenth, third,  # point value? This function will round to the nearest tenth, third,
 # or quarter if one of those is within the tolerance of .00001.  # or quarter if one of those is within the tolerance of .00001.
Line 592  sub verifyreceipt { Line 697  sub verifyreceipt {
     $receipt     =~ s/[^\-\d]//g;      $receipt     =~ s/[^\-\d]//g;
     my ($symb)   = &get_symb($request);      my ($symb)   = &get_symb($request);
   
     my $title.='<h3><span class="LC_info">Verifying Submission Receipt '.      my $title.=
  $receipt.'</h3></span>'."\n".   '<h3><span class="LC_info">'.
  '<h4><b>Resource: </b>'.$env{'form.probTitle'}.'</h4><br /><br />'."\n";   &mt('Verifying Submission Receipt [_1]',$receipt).
    '</span></h3>'."\n".
    '<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}).
    '</h4>'."\n";
   
     my ($string,$contents,$matches) = ('','',0);      my ($string,$contents,$matches) = ('','',0);
     my (undef,undef,$fullname) = &getclasslist('all','0');      my (undef,undef,$fullname) = &getclasslist('all','0');
Line 604  sub verifyreceipt { Line 712  sub verifyreceipt {
  $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; }   $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; }
     my $parts=['0'];      my $parts=['0'];
     if ($receiptparts) { ($parts)=&response_type($symb); }      if ($receiptparts) { ($parts)=&response_type($symb); }
       
       my $header = 
    &Apache::loncommon::start_data_table().
    &Apache::loncommon::start_data_table_header_row().
    '<th>&nbsp;'.&mt('Fullname').'&nbsp;</th>'."\n".
    '<th>&nbsp;'.&mt('Username').'&nbsp;</th>'."\n".
    '<th>&nbsp;'.&mt('Domain').'&nbsp;</th>';
       if ($receiptparts) {
    $header.='<th>&nbsp;'.&mt('Problem Part').'&nbsp;</th>';
       }
       $header.=
    &Apache::loncommon::end_data_table_header_row();
   
     foreach (sort       foreach (sort 
      {       {
  if (lc($$fullname{$a}) ne lc($$fullname{$b})) {   if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
Line 614  sub verifyreceipt { Line 735  sub verifyreceipt {
  my ($uname,$udom)=split(/\:/);   my ($uname,$udom)=split(/\:/);
  foreach my $part (@$parts) {   foreach my $part (@$parts) {
     if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) {      if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) {
  $contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".   $contents.=
       &Apache::loncommon::start_data_table_row().
       '<td>&nbsp;'."\n".
     '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.      '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
     '\');" target="_self">'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".      '\');" target="_self">'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".
     '<td>&nbsp;'.$uname.'&nbsp;</td>'.      '<td>&nbsp;'.$uname.'&nbsp;</td>'.
Line 622  sub verifyreceipt { Line 745  sub verifyreceipt {
  if ($receiptparts) {   if ($receiptparts) {
     $contents.='<td>&nbsp;'.$part.'&nbsp;</td>';      $contents.='<td>&nbsp;'.$part.'&nbsp;</td>';
  }   }
  $contents.='</tr>'."\n";   $contents.= 
       &Apache::loncommon::end_data_table_row()."\n";
   
  $matches++;   $matches++;
     }      }
  }   }
     }      }
     if ($matches == 0) {      if ($matches == 0) {
  $string = $title.'No match found for the above receipt.';   $string = $title.&mt('No match found for the above receipt.');
     } else {      } else {
  $string = &jscriptNform($symb).$title.   $string = &jscriptNform($symb).$title.
     'The above receipt matches the following student'.      '<p>'.
     ($matches <= 1 ? '.' : 's.')."\n".      &mt('The above receipt matches the following [numerate,_1,student].',$matches).
     '<table border="0"><tr><td bgcolor="#777777">'."\n".      '</p>'.
     '<table border="0"><tr bgcolor="#e6ffff">'."\n".      $header.
     '<td><b>&nbsp;Fullname&nbsp;</b></td>'."\n".      $contents.
     '<td><b>&nbsp;Username&nbsp;</b></td>'."\n".      &Apache::loncommon::end_data_table()."\n";
     '<td><b>&nbsp;Domain&nbsp;</b></td>';  
  if ($receiptparts) {  
     $string.='<td>&nbsp;Problem Part&nbsp;</td>';  
  }  
  $string.='</tr>'."\n".$contents.  
     '</table></td></tr></table>'."\n";  
     }      }
     return $string.&show_grading_menu_form($symb);      return $string.&show_grading_menu_form($symb);
 }  }
Line 659  sub listStudents { Line 777  sub listStudents {
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};      my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
       my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
     my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};      my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
   
     my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';      my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
     $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ?       $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
  &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};   &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
   
     my $result='<h3><span class="LC_info">&nbsp;'.$viewgrade.      my $result='<h3><span class="LC_info">&nbsp;'
  ' Submissions for a Student or a Group of Students</span></h3>';   .&mt("$viewgrade Submissions for a Student or a Group of Students")
    .'</span></h3>';
   
     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));      my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
   
       my %lt = ( 'multiple' =>
          &mt("Please select a student or group of students before clicking on the Next button."),
          'single'   =>
          &mt("Please select the student before clicking on the Next button."),
          );
       %lt = &Apache::lonlocal::texthash(%lt);
     $request->print(<<LISTJAVASCRIPT);      $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkSelect(checkBox) {      function checkSelect(checkBox) {
Line 681  sub listStudents { Line 806  sub listStudents {
     ctr++;      ctr++;
  }   }
     }      }
     sense = "a student or group of students";      sense = '$lt{'multiple'}';
  } else {   } else {
     if (checkBox.checked) {      if (checkBox.checked) {
  ctr = 1;   ctr = 1;
     }      }
     sense = "the student";      sense = '$lt{'single'}';
  }   }
  if (ctr == 0) {   if (ctr == 0) {
     alert("Please select "+sense+" before clicking on the Next button.");      alert(sense);
     return false;      return false;
  }   }
  document.gradesub.submit();   document.gradesub.submit();
Line 709  LISTJAVASCRIPT Line 834  LISTJAVASCRIPT
     my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : '';      my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : '';
     my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : '';      my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : '';
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.      my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
  "\n".$table.   "\n".$table;
  '&nbsp;<b>View Problem Text: </b><label><input type="radio" name="vProb" value="no" checked="checked" /> no </label>'."\n".  
  '<label><input type="radio" name="vProb" value="yes" /> one student </label>'."\n".      $gradeTable .= 
  '<label><input type="radio" name="vProb" value="all" /> all students </label><br />'."\n".   '&nbsp;<b>'.&mt('View Problem Text').': </b>'.
  '&nbsp;<b>View Answer: </b><label><input type="radio" name="vAns" value="no"  /> no </label>'."\n".      '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".
  '<label><input type="radio" name="vAns" value="yes" /> one student </label>'."\n".      '<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n".
  '<label><input type="radio" name="vAns" value="all" checked="checked" /> all students </label><br />'."\n".      '<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n";
  '&nbsp;<b>Submissions: </b>'."\n";      $gradeTable .= 
    '&nbsp;<b>'.&mt('View Answer').': </b>'.
       '<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n".
       '<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n".
       '<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n";
   
       my $submission_options;
     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {      if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
  $gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";   $submission_options.=
       '<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> '.&mt('essay part only').' </label>'."\n";
     }      }
       my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'};      my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;
     $env{'form.Status'} = $saveStatus;      $env{'form.Status'} = $saveStatus;
     $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".      $submission_options.=
  '<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".   '<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> '.&mt('last submission only').' </label>'."\n".
  '<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n".   '<label><input type="radio" name="lastSub" value="last" /> '.&mt('last submission &amp; parts info').' </label>'."\n".
  '<label><input type="radio" name="lastSub" value="all" /> all details</label><br />'."\n".   '<label><input type="radio" name="lastSub" value="datesub" /> '.&mt('by dates and submissions').' </label>'."\n".
         '&nbsp;<b>Grading Increments:</b> <select name="increment">'.   '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').'</label>';
         '<option value="1">Whole Points</option>'.      $gradeTable .= 
         '<option value=".5">Half Points</option>'.   '&nbsp;<b>'.&mt('Submissions').': </b>'.$submission_options.'<br />'."\n";
         '<option value=".25">Quarter Points</option>'.  
         '<option value=".1">Tenths of a Point</option>'.      $gradeTable .= 
         '</select>'.          '&nbsp;<b>'.&mt('Grading Increments').': </b>'.
       '<select name="increment">'.
       '<option value="1">'.&mt('Whole Points').'</option>'.
       '<option value=".5">'.&mt('Half Points').'</option>'.
       '<option value=".25">'.&mt('Quarter Points').'</option>'.
       '<option value=".1">'.&mt('Tenths of a Point').'</option>'.
       '</select>';
       
       $gradeTable .= 
         &build_section_inputs().          &build_section_inputs().
  '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".   '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
  '<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".   '<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".
Line 743  LISTJAVASCRIPT Line 883  LISTJAVASCRIPT
  '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
   
     if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {      if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
  $gradeTable.='<input type="hidden" name="Status"   value="'.$env{'form.Status'}.'" />'."\n";   $gradeTable.='<input type="hidden" name="Status"   value="'.$stu_status.'" />'."\n";
     } else {      } else {
  $gradeTable.='<b>Student Status:</b> '.   $gradeTable.=&mt('<b>Student Status:</b> [_1]',
     &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />';   &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'<br />';
     }      }
   
     $gradeTable.='To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '.      $gradeTable.=&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.").'<br />'."\n".
  'next to the student\'s name(s). Then click on the Next button.<br />'."\n".  
  '<input type="hidden" name="command" value="processGroup" />'."\n";   '<input type="hidden" name="command" value="processGroup" />'."\n";
   
 # checkall buttons  # checkall buttons
     $gradeTable.=&check_script('gradesub', 'stuinfo');      $gradeTable.=&check_script('gradesub', 'stuinfo');
     $gradeTable.='<input type="button" '."\n".      $gradeTable.='<input type="button" '."\n".
  'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".   'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
  'value="Next->" /> <br />'."\n";   'value="'.&mt('Next').' &rarr;" /> <br />'."\n";
     $gradeTable.=&check_buttons();      $gradeTable.=&check_buttons();
     $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="checked" />Check For Plagiarism</label>';      $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="checked" />'.&mt('Check For Plagiarism').'</label>';
     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1');      my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup);
     $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.      $gradeTable.= &Apache::loncommon::start_data_table().
  '<table border="0"><tr bgcolor="#e6ffff">';   &Apache::loncommon::start_data_table_header_row();
     my $loop = 0;      my $loop = 0;
     while ($loop < 2) {      while ($loop < 2) {
  $gradeTable.='<td><b>&nbsp;No.</b>&nbsp;</td><td><b>&nbsp;Select&nbsp;</b></td>'.   $gradeTable.='<th>'.&mt('No.').'</th><th>'.&mt('Select').'</th>'.
     '<td>'.&nameUserString('header').'&nbsp;Section/Group</td>';      '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>';
  if ($env{'form.showgrading'} eq 'yes'    if ($env{'form.showgrading'} eq 'yes' 
     && $submitonly ne 'queued'      && $submitonly ne 'queued'
     && $submitonly ne 'all') {      && $submitonly ne 'all') {
     foreach (sort(@$partlist)) {      foreach my $part (sort(@$partlist)) {
  my $display_part=&get_display_part((split(/_/))[0],$symb);   my $display_part=
  $gradeTable.='<td><b>&nbsp;Part: '.$display_part.      &get_display_part((split(/_/,$part))[0],$symb);
     ' Status&nbsp;</b></td>';   $gradeTable.=
       '<th>'.&mt('Part: [_1] Status',$display_part).'</th>';
     }      }
  } elsif ($submitonly eq 'queued') {   } elsif ($submitonly eq 'queued') {
     $gradeTable.='<td><b>&nbsp;'.&mt('Queue Status').'&nbsp;</b></td>';      $gradeTable.='<th>'.&mt('Queue Status').'&nbsp;</th>';
  }   }
  $loop++;   $loop++;
 # $gradeTable.='<td></td>' if ($loop%2 ==1);  # $gradeTable.='<td></td>' if ($loop%2 ==1);
     }      }
     $gradeTable.='</tr>'."\n";      $gradeTable.=&Apache::loncommon::end_data_table_header_row()."\n";
   
     my $ctr = 0;      my $ctr = 0;
     foreach my $student (sort       foreach my $student (sort 
Line 835  LISTJAVASCRIPT Line 975  LISTJAVASCRIPT
   
  $ctr++;   $ctr++;
  my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];   my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
           my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
  if ( $perm{'vgr'} eq 'F' ) {   if ( $perm{'vgr'} eq 'F' ) {
     $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1);      if ($ctr%2 ==1) {
    $gradeTable.= &Apache::loncommon::start_data_table_row();
       }
     $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.      $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.
                '<td align="center"><label><input type=checkbox name="stuinfo" value="'.                 '<td align="center"><label><input type=checkbox name="stuinfo" value="'.
                $student.':'.$$fullname{$student}.':::SECTION'.$section.                 $student.':'.$$fullname{$student}.':::SECTION'.$section.
        ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.         ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
        &nameUserString(undef,$$fullname{$student},$uname,$udom).         &nameUserString(undef,$$fullname{$student},$uname,$udom).
        '&nbsp;'.$section.'</td>'."\n";         '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'."\n";
   
     if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {      if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  foreach (sort keys(%status)) {   foreach (sort(keys(%status))) {
     next if (/^resource.*?submitted_by$/);      next if ($_ =~ /^resource.*?submitted_by$/);
     $gradeTable.='<td align="center">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";      $gradeTable.='<td align="center">&nbsp;'.&mt($status{$_}).'&nbsp;</td>'."\n";
  }   }
     }      }
 #    $gradeTable.='<td></td>' if ($ctr%2 ==1);  #    $gradeTable.='<td></td>' if ($ctr%2 ==1);
     $gradeTable.='</tr>'."\n" if ($ctr%2 ==0);      if ($ctr%2 ==0) {
    $gradeTable.=&Apache::loncommon::end_data_table_row()."\n";
       }
  }   }
     }      }
     if ($ctr%2 ==1) {      if ($ctr%2 ==1) {
Line 866  LISTJAVASCRIPT Line 1010  LISTJAVASCRIPT
     } elsif ($submitonly eq 'queued') {      } elsif ($submitonly eq 'queued') {
  $gradeTable.='<td>&nbsp;</td>';   $gradeTable.='<td>&nbsp;</td>';
     }      }
  $gradeTable.='</tr>';   $gradeTable.=&Apache::loncommon::end_data_table_row();
     }      }
   
     $gradeTable.='</table></td></tr></table>'."\n".      $gradeTable.=&Apache::loncommon::end_data_table()."\n".
  '<input type="button" '.   '<input type="button" '.
  'onClick="javascript:checkSelect(this.form.stuinfo);" '.   'onClick="javascript:checkSelect(this.form.stuinfo);" '.
  'value="Next->" /></form>'."\n";   'value="'.&mt('Next').' &rarr;" /></form>'."\n";
     if ($ctr == 0) {      if ($ctr == 0) {
  my $num_students=(scalar(keys(%$fullname)));   my $num_students=(scalar(keys(%$fullname)));
  if ($num_students eq 0) {   if ($num_students eq 0) {
     $gradeTable='<br />&nbsp;<span class="LC_warning">There are no students currently enrolled.</span>';      $gradeTable='<br />&nbsp;<span class="LC_warning">'.&mt('There are no students currently enrolled.').'</span>';
  } else {   } else {
     my $submissions='submissions';      my $submissions='submissions';
     if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }      if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }
     if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }      if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }
     if ($submitonly eq 'queued'   ) { $submissions = 'queued submissions'; }      if ($submitonly eq 'queued'   ) { $submissions = 'queued submissions'; }
     $gradeTable='<br />&nbsp;<span class="LC_warning">'.      $gradeTable='<br />&nbsp;<span class="LC_warning">'.
  'No '.$submissions.' found for this resource for any students. ('.$num_students.   &mt('No '.$submissions.' found for this resource for any students. ([_1] students checked for '.$submissions.')',
  ' students checked for '.$submissions.')</span><br />';      $num_students).
    '</span><br />';
  }   }
     } elsif ($ctr == 1) {      } elsif ($ctr == 1) {
  $gradeTable =~ s/type=checkbox/type=checkbox checked/;   $gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/;
     }      }
     $gradeTable.=&show_grading_menu_form($symb);      $gradeTable.=&show_grading_menu_form($symb);
     $request->print($gradeTable);      $request->print($gradeTable);
Line 934  sub check_script { Line 1079  sub check_script {
 }  }
   
 sub check_buttons {  sub check_buttons {
     my $buttons.='<input type="button" onclick="checkall()" value="Check All" />';      my $buttons.='<input type="button" onclick="checkall()" value="'.&mt('Check All').'" />';
     $buttons.='<input type="button" onclick="uncheckall()" value="Uncheck All" />&nbsp;';      $buttons.='<input type="button" onclick="uncheckall()" value="'.&mt('Uncheck All').'" />&nbsp;';
     $buttons.='<input type="button" onclick="checksec()" value="Check Section/Group" />';      $buttons.='<input type="button" onclick="checksec()" value="'.&mt('Check Section/Group').'" />';
     $buttons.='<input type="text" size="5" name="chksec" />&nbsp;';      $buttons.='<input type="text" size="5" name="chksec" />&nbsp;';
     return $buttons;      return $buttons;
 }  }
Line 967  sub processGroup { Line 1112  sub processGroup {
 #--- Javascript to handle the submission page functionality ---  #--- Javascript to handle the submission page functionality ---
 sub sub_page_js {  sub sub_page_js {
     my $request = shift;      my $request = shift;
       my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
     $request->print(<<SUBJAVASCRIPT);      $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function updateRadio(formname,id,weight) {      function updateRadio(formname,id,weight) {
Line 977  sub sub_page_js { Line 1123  sub sub_page_js {
  gradeBox.value = pts;   gradeBox.value = pts;
  var resetbox = false;   var resetbox = false;
  if (isNaN(pts) || pts < 0) {   if (isNaN(pts) || pts < 0) {
     alert("A number equal or greater than 0 is expected. Entered value = "+pts);      alert("$alertmsg"+pts);
     for (var i=0; i<radioButton.length; i++) {      for (var i=0; i<radioButton.length; i++) {
  if (radioButton[i].checked) {   if (radioButton[i].checked) {
     gradeBox.value = i;      gradeBox.value = i;
Line 1222  INNERJS Line 1368  INNERJS
   
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();      my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;      $docopen=~s/^document\.//;
       my $alertmsg = &mt('Please select a word or group of words from document and then click this link.');
     $request->print(<<SUBJAVASCRIPT);      $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
   
Line 1254  INNERJS Line 1401  INNERJS
     else return;      else return;
     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");      var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
     if (cleantxt=="") {      if (cleantxt=="") {
  alert("Please select a word or group of words from document and then click this link.");   alert("$alertmsg");
  return;   return;
     }      }
     var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);      var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
Line 1339  INNERJS Line 1486  INNERJS
   
     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");      pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");      pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
     pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Compose Message for \"+fullname+\"</span></h3><br /><br />");      pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Compose Message for \"+fullname+\"<\\/span><\\/h3><br /><br />");
   
     pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");      pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
     pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");      pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
     pDoc.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>");      pDoc.write("<td><b>Type<\\/b><\\/td><td><b>Include<\\/b><\\/td><td><b>Message<\\/td><\\/tr>");
 }  }
     function displaySubject(msg,shwsel) {      function displaySubject(msg,shwsel) {
     pDoc = pWin.document;      pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");      pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
     pDoc.write("<td>Subject</td>");      pDoc.write("<td>Subject<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");      pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");      pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"><\\/td><\\/tr>");
 }  }
   
   function displaySavedMsg(ctr,msg,shwsel) {    function displaySavedMsg(ctr,msg,shwsel) {
     pDoc = pWin.document;      pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");      pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
     pDoc.write("<td align=\\"center\\">"+ctr+"</td>");      pDoc.write("<td align=\\"center\\">"+ctr+"<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");      pDoc.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><textarea name=\\"msg"+ctr+"\\" cols=\\"60\\" rows=\\"3\\">"+msg+"</textarea></td></tr>");      pDoc.write("<td><textarea name=\\"msg"+ctr+"\\" cols=\\"60\\" rows=\\"3\\">"+msg+"<\\/textarea><\\/td><\\/tr>");
 }  }
   
   function newMsg(newmsg,shwsel) {    function newMsg(newmsg,shwsel) {
     pDoc = pWin.document;      pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");      pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
     pDoc.write("<td align=\\"center\\">New</td>");      pDoc.write("<td align=\\"center\\">New<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");      pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"</textarea></td></tr>");      pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"<\\/textarea><\\/td><\\/tr>");
 }  }
   
   function msgTail() {    function msgTail() {
     pDoc = pWin.document;      pDoc = pWin.document;
     pDoc.write("</table>");      pDoc.write("<\\/table>");
     pDoc.write("</td></tr></table>&nbsp;");      pDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");      pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");      pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
     pDoc.write("</form>");      pDoc.write("<\\/form>");
     pDoc.write('$end_page_msg_central');      pDoc.write('$end_page_msg_central');
     pDoc.close();      pDoc.close();
 }  }
Line 1424  INNERJS Line 1571  INNERJS
     hDoc.$docopen;      hDoc.$docopen;
     hDoc.write('$start_page_highlight_central');      hDoc.write('$start_page_highlight_central');
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");      hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
     hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Keyword Highlight Options</span></h3><br /><br />");      hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Keyword Highlight Options<\\/span><\\/h3><br /><br />");
   
     hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");      hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
     hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");      hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
     hDoc.write("<td><b>Text Color</b></td><td><b>Font Size</b></td><td><b>Font Style</td></tr>");      hDoc.write("<td><b>Text Color<\\/b><\\/td><td><b>Font Size<\\/b><\\/td><td><b>Font Style<\\/td><\\/tr>");
   }    }
   
   function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) {     function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
     var hDoc = hwdWin.document;      var hDoc = hwdWin.document;
     hDoc.write("<tr bgcolor=\\"#ffffdd\\">");      hDoc.write("<tr bgcolor=\\"#ffffdd\\">");
     hDoc.write("<td align=\\"left\\">");      hDoc.write("<td align=\\"left\\">");
     hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"</td>");      hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"<\\/td>");
     hDoc.write("<td align=\\"left\\">");      hDoc.write("<td align=\\"left\\">");
     hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"</td>");      hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"<\\/td>");
     hDoc.write("<td align=\\"left\\">");      hDoc.write("<td align=\\"left\\">");
     hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"</td>");      hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"<\\/td>");
     hDoc.write("</tr>");      hDoc.write("<\\/tr>");
   }    }
   
   function highlightend() {     function highlightend() { 
     var hDoc = hwdWin.document;      var hDoc = hwdWin.document;
     hDoc.write("</table>");      hDoc.write("<\\/table>");
     hDoc.write("</td></tr></table>&nbsp;");      hDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
     hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");      hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
     hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");      hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
     hDoc.write("</form>");      hDoc.write("<\\/form>");
     hDoc.write('$end_page_highlight_central');      hDoc.write('$end_page_highlight_central');
     hDoc.close();      hDoc.close();
   }    }
Line 1471  sub get_increment { Line 1618  sub get_increment {
 sub gradeBox {  sub gradeBox {
     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;      my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
     my $checkIcon = '<img alt="'.&mt('Check Mark').      my $checkIcon = '<img alt="'.&mt('Check Mark').
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.&Apache::loncommon::lonhttpdurl($request->dir_config('lonIconsURL').'/check.gif').'" height="16" border="0" />';
  '/check.gif" height="16" border="0" />';  
     my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);      my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
     my $wgtmsg = ($wgt > 0 ? '(problem weight)' :       my $wgtmsg = ($wgt > 0) ? &mt('(problem weight)') 
   '<span class="LC_info">problem weight assigned by computer</span>');                             : '<span class="LC_info">'.&mt('problem weight assigned by computer').'</span>';
     $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 $result='<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]);
     my $aggtries = $$record{'resource.'.$partid.'.tries'};      my $aggtries = $$record{'resource.'.$partid.'.tries'};
     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.='<table border="0"><tr><td>'.      $result.='<table border="0"><tr>';
  '<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n";  
     my $ctr = 0;      my $ctr = 0;
     my $thisweight = 0;      my $thisweight = 0;
     my $increment = &get_increment();      my $increment = &get_increment();
     $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across  
       my $radio.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
     while ($thisweight<=$wgt) {      while ($thisweight<=$wgt) {
  $result.= '<td><span style="white-space: nowrap;"><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.   $radio.= '<td><span class="LC_nobreak"><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
     'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.      'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
     $thisweight.')" value="'.$thisweight.'" '.      $thisweight.')" value="'.$thisweight.'" '.
     ($score eq $thisweight ? 'checked="checked"':'').' /> '.$thisweight."</label></span></td>\n";      ($score eq $thisweight ? 'checked="checked"':'').' /> '.$thisweight."</label></span></td>\n";
  $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');   $radio.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
         $thisweight += $increment;          $thisweight += $increment;
  $ctr++;   $ctr++;
     }      }
     $result.='</tr></table>';      $radio.='</tr></table>';
     $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>'."\n";  
     $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.      my $line.='<input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
  ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.   ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
  'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.   'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
  $wgt.')" /></td>'."\n";   $wgt.')" /></td>'."\n";
     $result.='<td>/'.$wgt.' '.$wgtmsg.      $line.='<td>/'.$wgt.' '.$wgtmsg.
  ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').   ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
  ' </td><td>'."\n";   ' </td><td><b>'.&mt('Grade Status').':</b>'."\n";
     $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.      $line.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
  'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";   'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {      if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
  $result.='<option></option>'.   $line.='<option></option>'.
     '<option selected="selected">excused</option>';      '<option value="excused" selected="selected">'.&mt('excused').'</option>';
     } else {      } else {
  $result.='<option selected="selected"></option>'.   $line.='<option selected="selected"></option>'.
     '<option>excused</option>';      '<option value="excused" >'.&mt('excused').'</option>';
     }      }
     $result.='<option>reset status</option></select>'."\n";      $line.='<option value="reset status">'.&mt('reset status').'</option></select>'."\n";
     $result.="&nbsp;&nbsp;\n";  
   
    #&mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line);
       $result .= 
       '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
       
       $result.='</tr></table>'."\n";
     $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 1530  sub gradeBox { Line 1682  sub gradeBox {
         $$record{'resource.'.$partid.'.tries'}.'" />'."\n".          $$record{'resource.'.$partid.'.tries'}.'" />'."\n".
         '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.          '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.
         $aggtries.'" />'."\n";          $aggtries.'" />'."\n";
     $result.='</td></tr></table>'."\n";  
     $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record);      $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record);
     return $result;      return $result;
 }  }
Line 1562  sub handback_box { Line 1713  sub handback_box {
     '<span class="LC_filename">'.$file_disp.'</span>');      '<span class="LC_filename">'.$file_disp.'</span>');
            $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";             $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";
            $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';             $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';
            $result.='(File will be uploaded when you click on Save & Next below.)<br />';             $result.='('.&mt('File will be uploaded when you click on Save &amp; Next below.').')<br />';
            $file_counter++;             $file_counter++;
     }      }
  }   }
Line 1598  sub show_problem { Line 1749  sub show_problem {
  $companswer=~s|</form>||g;   $companswer=~s|</form>||g;
  $companswer=~s|name="submit"|name="would_have_been_submit"|g;   $companswer=~s|name="submit"|name="would_have_been_submit"|g;
     }      }
     my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';      $rendered=
     $result.='<table border="0" width="100%">';   '<div class="LC_grade_show_problem_header">'.
     if ($viewon) {   &mt('View of the problem').
  $result.='<tr><td bgcolor="#e6ffff"><b> ';   '</div><div class="LC_grade_show_problem_problem">'.
  if ($mode eq 'both' or $mode eq 'text') {   $rendered.
     $result.='View of the problem - ';   '</div>';
  } else {      $companswer=
     $result.='Correct answer: ';   '<div class="LC_grade_show_problem_header">'.
  }   &mt('Correct answer').
  $result.=$env{'form.fullname'}.'</b></td></tr>';   '</div><div class="LC_grade_show_problem_problem">'.
     }   $companswer.
    '</div>';
       my $result;
     if ($mode eq 'both') {      if ($mode eq 'both') {
  $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';   $result=$rendered.$companswer;
  $result.='<b>Correct answer:</b><br />'.$companswer;  
     } elsif ($mode eq 'text') {      } elsif ($mode eq 'text') {
  $result.='<tr><td bgcolor="#ffffff">'.$rendered;   $result=$rendered;
     } elsif ($mode eq 'answer') {      } elsif ($mode eq 'answer') {
  $result.='<tr><td bgcolor="#ffffff">'.$companswer;   $result=$companswer;
     }      }
     $result.='</td></tr></table>';      $result='<div class="LC_grade_show_problem">'.$result.'</div>';
     $result.='</td></tr></table><br />';  
     return $result;      return $result;
 }  }
   
Line 1651  sub download_all_link { Line 1802  sub download_all_link {
  join("\n",&Apache::loncommon::get_env_multiple('form.vPart'));   join("\n",&Apache::loncommon::get_env_multiple('form.vPart'));
   
     my $identifier = &Apache::loncommon::get_cgi_id();      my $identifier = &Apache::loncommon::get_cgi_id();
     &Apache::lonnet::appenv('cgi.'.$identifier.'.students' => $all_students,      &Apache::lonnet::appenv({'cgi.'.$identifier.'.students' => $all_students,
                             'cgi.'.$identifier.'.symb' => $symb,                               'cgi.'.$identifier.'.symb' => $symb,
                             'cgi.'.$identifier.'.parts' => $parts,);                               'cgi.'.$identifier.'.parts' => $parts,});
     $r->print('<a href="/cgi-bin/multidownload.pl?'.$identifier.'">'.      $r->print('<a href="/cgi-bin/multidownload.pl?'.$identifier.'">'.
       &mt('Download All Submitted Documents').'</a>');        &mt('Download All Submitted Documents').'</a>');
     return      return
Line 1665  sub build_section_inputs { Line 1816  sub build_section_inputs {
         $section_inputs .= '<input type="hidden" name="section" value="all" />'."\n";          $section_inputs .= '<input type="hidden" name="section" value="all" />'."\n";
     } else {      } else {
         my @sections = &Apache::loncommon::get_env_multiple('form.section');          my @sections = &Apache::loncommon::get_env_multiple('form.section');
         foreach my $section(@sections) {          foreach my $section (@sections) {
             $section_inputs .= '<input type="hidden" name="section" value="'.$section.'" />'."\n";              $section_inputs .= '<input type="hidden" name="section" value="'.$section.'" />'."\n";
         }          }
     }      }
Line 1675  sub build_section_inputs { Line 1826  sub build_section_inputs {
 # --------------------------- show submissions of a student, option to grade   # --------------------------- show submissions of a student, option to grade 
 sub submission {  sub submission {
     my ($request,$counter,$total) = @_;      my ($request,$counter,$total) = @_;
   
     my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});      my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});
     $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';      $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
   
     my $symb = &get_symb($request);       my $symb = &get_symb($request); 
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
   
Line 1710  sub submission { Line 1859  sub submission {
  if ($env{'form.handgrade'} eq 'yes' && &files_exist($request, $symb)) {   if ($env{'form.handgrade'} eq 'yes' && &files_exist($request, $symb)) {
     &download_all_link($request, $symb);      &download_all_link($request, $symb);
  }   }
  $request->print('<h3>&nbsp;<span class="LC_info">Submission Record</span></h3>'."\n".   $request->print('<h3>&nbsp;<span class="LC_info">'.&mt('Submission Record').'</span></h3>'."\n".
  '<h4>&nbsp;<b>Resource: </b>'.$env{'form.probTitle'}.'</h4>'."\n");   '<h4>&nbsp;'.&mt('<b>Resource: </b> [_1]',$env{'form.probTitle'}).'</h4>'."\n");
   
  if ($env{'form.handgrade'} eq 'no') {  
     my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.  
  $checkIcon.' symbol.'."\n";  
     $request->print($checkMark);  
  }  
   
  # option to display problem, only once else it cause problems    # option to display problem, only once else it cause problems 
         # with the form later since the problem has a form.          # with the form later since the problem has a form.
Line 1733  sub submission { Line 1876  sub submission {
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
     $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));      $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));
  }   }
   
  # kwclr is the only variable that is guaranteed to be non blank    # kwclr is the only variable that is guaranteed to be non blank 
         # if this subroutine has been called once.          # if this subroutine has been called once.
  my %keyhash = ();   my %keyhash = ();
Line 1752  sub submission { Line 1895  sub submission {
     $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';      $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
  }   }
  my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};   my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};
    my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
  $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".   $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".
  '<input type="hidden" name="command"    value="handgrade" />'."\n".   '<input type="hidden" name="command"    value="handgrade" />'."\n".
  '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="Status"     value="'.$env{'form.Status'}.'" />'."\n".   '<input type="hidden" name="Status"     value="'.$stu_status.'" />'."\n".
  '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".   '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".
  '<input type="hidden" name="probTitle"  value="'.$env{'form.probTitle'}.'" />'."\n".   '<input type="hidden" name="probTitle"  value="'.$env{'form.probTitle'}.'" />'."\n".
  '<input type="hidden" name="refresh"    value="off" />'."\n".   '<input type="hidden" name="refresh"    value="off" />'."\n".
Line 1818  KEYWORDS Line 1962  KEYWORDS
         }          }
     }      }
   
   # This is where output for one specific student would start
       my $add_class = ($counter%2) ? 'LC_grade_show_user_odd_row' : '';
       $request->print("\n\n".
                       '<div class="LC_grade_show_user '.$add_class.'">'.
       '<div class="LC_grade_user_name">'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'</div>'.
       '<div class="LC_grade_show_user_body">'."\n");
   
     if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {      if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {
  $request->print('<br /><br /><br />') if ($counter > 0);  
  my $mode;   my $mode;
  if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {   if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {
     $mode='both';      $mode='both';
Line 1829  KEYWORDS Line 1979  KEYWORDS
     $mode='answer';      $mode='answer';
  }   }
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
  $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode));   $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode,{'request.prefix' => 'ctr'.$counter}));
     }      }
   
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
Line 1837  KEYWORDS Line 1987  KEYWORDS
   
     # Display student info      # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));      $request->print(($counter == 0 ? '' : '<br />'));
     my $result='<table border="0" width="100%"><tr><td bgcolor="#777777">'."\n".      my $result='<div class="LC_grade_submissions">';
  '<table border="0" width="100%"><tr bgcolor="#edffff"><td>'."\n";      
       $result.='<div class="LC_grade_submissions_header">';
     $result.='<b>Fullname: </b>'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'<br />'."\n";      $result.= &mt('Submissions');
     $result.='<input type="hidden" name="name'.$counter.      $result.='<input type="hidden" name="name'.$counter.
  '" value="'.$env{'form.fullname'}.'" />'."\n";   '" value="'.$env{'form.fullname'}.'" />'."\n";
       if ($env{'form.handgrade'} eq 'no') {
    $result.='<span class="LC_grade_check_note">'.
       &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)."</span>\n";
   
       }
   
   
   
     # If any part of the problem is an essay-response (handgraded), then check for collaborators      # If any part of the problem is an essay-response (handgraded), then check for collaborators
     my @col_fullnames;      my $fullname;
     my ($classlist,$fullname);      my $col_fullnames = [];
     if ($env{'form.handgrade'} eq 'yes') {      if ($env{'form.handgrade'} eq 'yes') {
  ($classlist,undef,$fullname) = &getclasslist('all','0');   (my $sub_result,$fullname,$col_fullnames)=
  for (keys (%$handgrade)) {      &check_collaborators($symb,$uname,$udom,\%record,$handgrade,
     my $ncol = &Apache::lonnet::EXT('resource.'.$_.   $counter);
     '.maxcollaborators',   $result.=$sub_result;
                                             $symb,$udom,$uname);  
     next if ($ncol <= 0);  
             s/\_/\./g;  
             next if ($record{'resource.'.$_.'.collaborators'} eq '');  
             my @goodcollaborators = ();  
             my @badcollaborators  = ();  
     foreach (split(/,?\s+/,$record{'resource.'.$_.'.collaborators'})) {   
  $_ =~ s/[\$\^\(\)]//g;  
  next if ($_ eq '');  
  my ($co_name,$co_dom) = split /\@|:/,$_;  
  $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);  
  next if ($co_name eq $uname && $co_dom eq $udom);  
  # Doing this grep allows 'fuzzy' specification  
  my @Matches = grep /^$co_name:$co_dom$/i,keys %$classlist;  
  if (! scalar(@Matches)) {  
     push @badcollaborators,$_;  
  } else {  
     push @goodcollaborators, @Matches;  
  }  
     }  
             if (scalar(@goodcollaborators) != 0) {  
                 $result.='<b>Collaborators: </b>';  
                 foreach (@goodcollaborators) {  
     my ($lastname,$givenn) = split(/,/,$$fullname{$_});  
     push @col_fullnames, $givenn.' '.$lastname;  
     $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';  
  }  
                 $result.='<br />'."\n";  
  my ($part)=split(/\./,$_);  
  $result.='<input type="hidden" name="collaborator'.$counter.  
     '" value="'.$part.':'.(join ':',@goodcollaborators).'" />'.  
     "\n";  
     }  
     if (scalar(@badcollaborators) > 0) {  
  $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';  
  $result.='This student has submitted ';  
  $result.=(scalar(@badcollaborators) == 1) ? 'an invalid collaborator' : 'invalid collaborators';  
  $result .= ': '.join(', ',@badcollaborators);  
  $result .= '</td></tr></table>';  
     }           
     if (scalar(@badcollaborators > $ncol)) {  
  $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';  
  $result .= 'This student has submitted too many '.  
     'collaborators.  Maximum is '.$ncol.'.';  
  $result .= '</td></tr></table>';  
     }  
  }  
     }      }
     $request->print($result."\n");      $request->print($result."\n");
       $request->print('</div>'."\n");
     # print student answer/submission      # print student answer/submission
     # Options are (1) Handgaded submission only      # Options are (1) Handgaded submission only
     #             (2) Last submission, includes submission that is not handgraded       #             (2) Last submission, includes submission that is not handgraded 
Line 1910  KEYWORDS Line 2020  KEYWORDS
     #             (4) The whole record for this student      #             (4) The whole record for this student
     if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {      if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
  my ($string,$timestamp)= &get_last_submission(\%record);   my ($string,$timestamp)= &get_last_submission(\%record);
  my $lastsubonly=''.  
     ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.   my $lastsubonly;
      $$timestamp)."</td></tr>\n";  
  if ($$timestamp eq '') {   if ($$timestamp eq '') {
     $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0];       $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>'; 
  } else {   } else {
       $lastsubonly = '<div class="LC_grade_submissions_body"> <b>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) {
Line 1938  KEYWORDS Line 2050  KEYWORDS
  }   }
  my $responsetype = $responseType->{$partid}->{$respid};   my $responsetype = $responseType->{$partid}->{$respid};
  if (!exists($record{"resource.$partid.$respid.submission"})) {   if (!exists($record{"resource.$partid.$respid.submission"})) {
     $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.      $lastsubonly.="\n".'<div class="LC_grade_submission_part"><b>Part:</b> '.
  $display_part.' <span class="LC_internal_info">( ID '.$respid.   $display_part.' <span class="LC_internal_info">( ID '.$respid.
  ' )</span>&nbsp; &nbsp;'.   ' )</span>&nbsp; &nbsp;'.
  '<span class="LC_warning">Nothing submitted - no attempts</span><br /><br />';   '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
     next;      next;
  }   }
  foreach (@$string) {   foreach my $submission (@$string) {
     my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;      my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
     if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }      if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
     my ($ressub,$subval) = split(/:/,$_,2);      my ($ressub,$subval) = split(/:/,$submission,2);
     # Similarity check      # Similarity check
     my $similar='';      my $similar='';
     if($env{'form.checkPlag'}){      if($env{'form.checkPlag'}){
Line 1977  KEYWORDS Line 2089  KEYWORDS
  ($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.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.   $lastsubonly.='<div class="LC_grade_submission_part"><b>Part:</b> '.
     $display_part.' <span class="LC_internal_info">( ID '.$respid.      $display_part.' <span class="LC_internal_info">( ID '.$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 (@$files) {
     $lastsubonly.='<br /><span class="LC_warning">Like all files provided by users, this file may contain virusses</span><br />';      $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />';
     my $file_counter = 0;      my $file_counter = 0;
     foreach my $file (@$files) {      foreach my $file (@$files) {
         $file_counter ++;          $file_counter++;
  &Apache::lonnet::allowuploaded('/adm/grades',$file);   &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 /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>';
     }      }
     $lastsubonly.='<br />';      $lastsubonly.='<br />';
  }   }
  $lastsubonly.='<b>Submitted Answer: </b>'.   $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.
     &cleanRecord($subval,$responsetype,$symb,$partid,      &cleanRecord($subval,$responsetype,$symb,$partid,
  $respid,\%record,$order);   $respid,\%record,$order);
  if ($similar) {$lastsubonly.="<br /><br />$similar\n";}   if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
    $lastsubonly.='</div>';
     }      }
  }   }
     }      }
       $lastsubonly.='</div>'."\n";
  }   }
  $lastsubonly.='</td></tr><tr bgcolor="#ffffff"><td>'."\n";  
  $request->print($lastsubonly);   $request->print($lastsubonly);
     } elsif ($env{'form.lastSub'} eq 'datesub') {     } elsif ($env{'form.lastSub'} eq 'datesub') {
  my (undef,$responseType,undef,$parts) = &showResourceInfo($symb);   my (undef,$responseType,undef,$parts) = &showResourceInfo($symb);
  $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)$/) {      } elsif ($env{'form.lastSub'} =~ /^(last|all)$/) {
Line 2013  KEYWORDS Line 2126  KEYWORDS
   
     $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'      $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
  .$udom.'" />'."\n");   .$udom.'" />'."\n");
       
     # return if view submission with no grading option      # return if view submission with no grading option
     if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {      if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {
  my $toGrade.='<input type="button" value="Grade Student" '.   my $toGrade.='<input type="button" value="Grade Student" '.
     'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''      'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''
     .$counter.'\');" target="_self" /> &nbsp;'."\n" if (&canmodify($usec));      .$counter.'\');" target="_self" /> &nbsp;'."\n" if (&canmodify($usec));
  $toGrade.='</td></tr></table></td></tr></table>'."\n";   $toGrade.='</div>'."\n";
  if (($env{'form.command'} eq 'submission') ||    if (($env{'form.command'} eq 'submission') || 
     ($env{'form.command'} eq 'processGroup' && $counter == $total)) {      ($env{'form.command'} eq 'processGroup' && $counter == $total)) {
     $toGrade.='</form>'.&show_grading_menu_form($symb);       $toGrade.='</form>'.&show_grading_menu_form($symb); 
Line 2027  KEYWORDS Line 2139  KEYWORDS
  $request->print($toGrade);   $request->print($toGrade);
  return;   return;
     } else {      } else {
  $request->print('</td></tr></table></td></tr></table>'."\n");   $request->print('</div>'."\n");
     }      }
   
     # essay grading message center      # essay grading message center
     if ($env{'form.handgrade'} eq 'yes') {      if ($env{'form.handgrade'} eq 'yes') {
    my $result='<div class="LC_grade_message_center">';
       
    $result.='<div class="LC_grade_message_center_header">'.
       &mt('Send Message').'</div><div class="LC_grade_message_center_body">';
  my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});   my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
  my $msgfor = $givenn.' '.$lastname;   my $msgfor = $givenn.' '.$lastname;
  if (scalar(@col_fullnames) > 0) {   if (scalar(@$col_fullnames) > 0) {
     my $lastone = pop @col_fullnames;      my $lastone = pop(@$col_fullnames);
     $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';      $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';
  }   }
  $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript   $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
  $result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".   $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
     '<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" name="mailicon'.$counter.'" />'."\n".
     '<br />&nbsp;('.      '<br />&nbsp;('.
     &mt('Message will be sent when you click on Save & Next below.').")\n";      &mt('Message will be sent when you click on Save &amp; Next below.').")\n";
    $result.='</div></div>';
  $request->print($result);   $request->print($result);
     }      }
     if ($perm{'vgr'}) {  
  $request->print('<br />'.  
     &Apache::loncommon::track_student_link(&mt('View recent activity'),  
    $uname,$udom,'check'));  
     }  
     if ($perm{'opa'}) {  
  $request->print('<br />'.  
     &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),  
  $uname,$udom,$symb,'check'));  
     }  
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id = &flatten_responseType($responseType);
       $request->print('<div class="LC_grade_assign">'.
       
       '<div class="LC_grade_assign_header">'.
       &mt('Assign Grades').'</div>'.
       '<div class="LC_grade_assign_body">');
     foreach my $part_response_id (@part_response_id) {      foreach my $part_response_id (@part_response_id) {
     my ($partid,$respid) = @{ $part_response_id };      my ($partid,$respid) = @{ $part_response_id };
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
Line 2073  KEYWORDS Line 2185  KEYWORDS
  $seen{$partid}++;   $seen{$partid}++;
  next if ($$handgrade{$part_resp} ne 'yes'    next if ($$handgrade{$part_resp} ne 'yes' 
  && $env{'form.lastSub'} eq 'hdgrade');   && $env{'form.lastSub'} eq 'hdgrade');
  push @partlist,$partid;   push(@partlist,$partid);
  push @gradePartRespid,$partid.'.'.$respid;   push(@gradePartRespid,$partid.'.'.$respid);
  $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));   $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
     }      }
       $request->print('</div></div>');
   
       $request->print('<div class="LC_grade_info_links">');
       if ($perm{'vgr'}) {
    $request->print(
       &Apache::loncommon::track_student_link(&mt('View recent activity'),
      $uname,$udom,'check'));
       }
       if ($perm{'opa'}) {
    $request->print(
       &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
    $uname,$udom,$symb,'check'));
       }
       $request->print('</div>');
   
     $result='<input type="hidden" name="partlist'.$counter.      $result='<input type="hidden" name="partlist'.$counter.
  '" value="'.(join ":",@partlist).'" />'."\n";   '" value="'.(join ":",@partlist).'" />'."\n";
     $result.='<input type="hidden" name="gradePartRespid'.      $result.='<input type="hidden" name="gradePartRespid'.
Line 2087  KEYWORDS Line 2214  KEYWORDS
     $partlist[$ctr].'" />'."\n";      $partlist[$ctr].'" />'."\n";
  $ctr++;   $ctr++;
     }      }
     $request->print($result.'</td></tr></table></td></tr></table>'."\n");      $request->print($result.''."\n");
   
   # Done with printing info for one student
   
       $request->print('</div>');#LC_grade_show_user_body
       $request->print('</div>');#LC_grade_show_user
   
   
     # print end of form      # print end of form
     if ($counter == $total) {      if ($counter == $total) {
  my $endform='<table border="0"><tr><td>'."\n";   my $endform='<table border="0"><tr><td>'."\n";
  $endform.='<input type="button" value="Save & Next" '.   $endform.='<input type="button" value="'.&mt('Save &amp; Next').'" '.
     'onClick="javascript:checksubmit(this.form,\'Save & Next\','.      'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
     $total.','.scalar(@partlist).');" target="_self" /> &nbsp;'."\n";      $total.','.scalar(@partlist).');" target="_self" /> &nbsp;'."\n";
  my $ntstu ='<select name="NTSTU">'.   my $ntstu ='<select name="NTSTU">'.
Line 2101  KEYWORDS Line 2234  KEYWORDS
     '<option>7</option><option>10</option></select>'."\n";      '<option>7</option><option>10</option></select>'."\n";
  my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');   my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
  $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;   $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
  $endform.=$ntstu.'student(s) &nbsp;&nbsp;';   $endform.=&mt('[quant,_1,student]',$ntstu);
  $endform.='<input type="button" value="Previous" '.   $endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.
     'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".      'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
     '<input type="button" value="Next" '.      '<input type="button" value="'.&mt('Next').'" '.
     'onClick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';      'onClick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';
  $endform.='(Next and Previous (student) do not save the scores.)'."\n" ;   $endform.=&mt('(Next and Previous (student) do not save the scores.)')."\n" ;
         $endform.="<input type='hidden' value='".&get_increment().          $endform.="<input type='hidden' value='".&get_increment().
             "' name='increment' />";              "' name='increment' />";
  $endform.='</td><tr></table></form>';   $endform.='</td></tr></table></form>';
  $endform.=&show_grading_menu_form($symb);   $endform.=&show_grading_menu_form($symb);
  $request->print($endform);   $request->print($endform);
     }      }
     return '';      return '';
 }  }
   
   sub check_collaborators {
       my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;
       my ($result,@col_fullnames);
       my ($classlist,undef,$fullname) = &getclasslist('all','0');
       foreach my $part (keys(%$handgrade)) {
    my $ncol = &Apache::lonnet::EXT('resource.'.$part.
    '.maxcollaborators',
    $symb,$udom,$uname);
    next if ($ncol <= 0);
    $part =~ s/\_/\./g;
    next if ($record->{'resource.'.$part.'.collaborators'} eq '');
    my (@good_collaborators, @bad_collaborators);
    foreach my $possible_collaborator
       (split(/,?\s+/,$record->{'resource.'.$part.'.collaborators'})) { 
       $possible_collaborator =~ s/[\$\^\(\)]//g;
       next if ($possible_collaborator eq '');
       my ($co_name,$co_dom) = split(/\@|:/,$possible_collaborator);
       $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
       next if ($co_name eq $uname && $co_dom eq $udom);
       # Doing this grep allows 'fuzzy' specification
       my @matches = grep(/^\Q$co_name\E:\Q$co_dom\E$/i, 
          keys(%$classlist));
       if (! scalar(@matches)) {
    push(@bad_collaborators, $possible_collaborator);
       } else {
    push(@good_collaborators, @matches);
       }
    }
    if (scalar(@good_collaborators) != 0) {
       $result.='<br />'.&mt('Collaborators: ');
       foreach my $name (@good_collaborators) {
    my ($lastname,$givenn) = split(/,/,$$fullname{$name});
    push(@col_fullnames, $givenn.' '.$lastname);
    $result.=$fullname->{$name}.'&nbsp; &nbsp; &nbsp;';
       }
       $result.='<br />'."\n";
       my ($part)=split(/\./,$part);
       $result.='<input type="hidden" name="collaborator'.$counter.
    '" value="'.$part.':'.(join ':',@good_collaborators).'" />'.
    "\n";
    }
    if (scalar(@bad_collaborators) > 0) {
       $result.='<div class="LC_warning">';
       $result.=&mt('This student has submitted [quant,_1,invalid collaborator]: [_2]',scalar(@bad_collaborators),join(', ',@bad_collaborators));
       $result .= '</div>';
    }         
    if (scalar(@bad_collaborators > $ncol)) {
       $result .= '<div class="LC_warning">';
       $result .= &mt('This student has submitted too many '.
    'collaborators.  Maximum is [_1].',$ncol);
       $result .= '</div>';
    }
       }
       return ($result,$fullname,\@col_fullnames);
   }
   
 #--- Retrieve the last submission for all the parts  #--- Retrieve the last submission for all the parts
 sub get_last_submission {  sub get_last_submission {
     my ($returnhash)=@_;      my ($returnhash)=@_;
Line 2128  sub get_last_submission { Line 2317  sub get_last_submission {
  $$returnhash{$version.':keys'}))) {   $$returnhash{$version.':keys'}))) {
  $lasthash{$key}=$$returnhash{$version.':'.$key};   $lasthash{$key}=$$returnhash{$version.':'.$key};
  $timestamp =    $timestamp = 
     scalar(localtime($$returnhash{$version.':timestamp'}));      &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
     }      }
  }   }
  foreach my $key (keys(%lasthash)) {   foreach my $key (keys(%lasthash)) {
Line 2142  sub get_last_submission { Line 2331  sub get_last_submission {
     }      }
     if (!@string) {      if (!@string) {
  $string[0] =   $string[0] =
     '<span class="LC_warning">Nothing submitted - no attempts.</span>';      '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';
     }      }
     return (\@string,\$timestamp);      return (\@string,\$timestamp);
 }  }
Line 2324  sub processHandGrade { Line 2513  sub processHandGrade {
   
     my (@parsedlist,@nextlist);      my (@parsedlist,@nextlist);
     my ($nextflg) = 0;      my ($nextflg) = 0;
     foreach (sort       foreach my $item (sort 
      {       {
  if (lc($$fullname{$a}) ne lc($$fullname{$b})) {   if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
      return (lc($$fullname{$a}) cmp lc($$fullname{$b}));       return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
Line 2332  sub processHandGrade { Line 2521  sub processHandGrade {
  return $a cmp $b;   return $a cmp $b;
      } (keys(%$fullname))) {       } (keys(%$fullname))) {
  if ($nextflg == 1 && $button =~ /Next$/) {   if ($nextflg == 1 && $button =~ /Next$/) {
     push @parsedlist,$_;      push(@parsedlist,$item);
  }   }
  $nextflg = 1 if ($_ eq $laststu);   $nextflg = 1 if ($item eq $laststu);
  if ($button eq 'Previous') {   if ($button eq 'Previous') {
     last if ($_ eq $firststu);      last if ($item eq $firststu);
     push @parsedlist,$_;      push(@parsedlist,$item);
  }   }
     }      }
     $ctr = 0;      $ctr = 0;
Line 2360  sub processHandGrade { Line 2549  sub processHandGrade {
     my $submitted = 0;      my $submitted = 0;
     my $ungraded = 0;      my $ungraded = 0;
     my $incorrect = 0;      my $incorrect = 0;
     foreach (keys(%status)) {      foreach my $item (keys(%status)) {
  $submitted = 1 if ($status{$_} ne 'nothing');   $submitted = 1 if ($status{$item} ne 'nothing');
  $ungraded = 1 if ($status{$_} =~ /^ungraded/);   $ungraded = 1 if ($status{$item} =~ /^ungraded/);
  $incorrect = 1 if ($status{$_} =~ /^incorrect/);   $incorrect = 1 if ($status{$item} =~ /^incorrect/);
  my ($foo,$partid,$foo1) = split(/\./,$_);   my ($foo,$partid,$foo1) = split(/\./,$item);
  if ($status{'resource.'.$partid.'.submitted_by'} ne '') {   if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
     $submitted = 0;      $submitted = 0;
  }   }
Line 2375  sub processHandGrade { Line 2564  sub processHandGrade {
     next if (!$ungraded && ($submitonly eq 'graded'));      next if (!$ungraded && ($submitonly eq 'graded'));
     next if (!$incorrect && $submitonly eq 'incorrect');      next if (!$incorrect && $submitonly eq 'incorrect');
  }   }
  push @nextlist,$student if ($ctr < $ntstu);   push(@nextlist,$student) if ($ctr < $ntstu);
  last if ($ctr == $ntstu);   last if ($ctr == $ntstu);
  $ctr++;   $ctr++;
     }      }
Line 2383  sub processHandGrade { Line 2572  sub processHandGrade {
     $ctr = 0;      $ctr = 0;
     my $total = scalar(@nextlist)-1;      my $total = scalar(@nextlist)-1;
   
     foreach (sort @nextlist) {      foreach (sort(@nextlist)) {
  my ($uname,$udom,$submitter) = split(/:/);   my ($uname,$udom,$submitter) = split(/:/);
  $env{'form.student'}  = $uname;   $env{'form.student'}  = $uname;
  $env{'form.userdom'}  = $udom;   $env{'form.userdom'}  = $udom;
Line 2392  sub processHandGrade { Line 2581  sub processHandGrade {
  $ctr++;   $ctr++;
     }      }
     if ($total < 0) {      if ($total < 0) {
  my $the_end = '<h3><span class="LC_info">LON-CAPA User Message</span></h3><br />'."\n";   my $the_end = '<h3><span class="LC_info">'.&mt('LON-CAPA User Message').'</span></h3><br />'."\n";
  $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";   $the_end.=&mt('<b>Message: </b> No more students for this section or class.').'<br /><br />'."\n";
  $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";   $the_end.=&mt('Click on the button below to return to the grading menu.').'<br /><br />'."\n";
  $the_end.=&show_grading_menu_form($symb);   $the_end.=&show_grading_menu_form($symb);
  $request->print($the_end);   $request->print($the_end);
     }      }
Line 2429  sub saveHandGrade { Line 2618  sub saveHandGrade {
     }      }
  } elsif ($dropMenu eq 'reset status'   } elsif ($dropMenu eq 'reset status'
  && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts   && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
     foreach my $key (keys (%record)) {      foreach my $key (keys(%record)) {
  if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; }   if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; }
     }      }
     $newrecord{'resource.'.$new_part.'.regrader'}=      $newrecord{'resource.'.$new_part.'.regrader'}=
Line 2464  sub saveHandGrade { Line 2653  sub saveHandGrade {
                 &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);                  &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
  next;   next;
     } else {      } else {
         push @parts_graded, $new_part;          push(@parts_graded,$new_part);
     }      }
     if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {      if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
  $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;   $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
Line 2491  sub saveHandGrade { Line 2680  sub saveHandGrade {
         $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' ||          $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' ||
         $dropMenu eq 'reset status')          $dropMenu eq 'reset status')
    {     {
     push (@version_parts,$new_part);      push(@version_parts,$new_part);
  }   }
     }      }
     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
Line 2539  sub check_and_remove_from_queue { Line 2728  sub check_and_remove_from_queue {
   
 sub handback_files {  sub handback_files {
     my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;      my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;
     my $portfolio_root = &propath($domain,$stuname).'/userfiles/portfolio';      my $portfolio_root = '/userfiles/portfolio';
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my ($partlist,$handgrade,$responseType) = &response_type($symb);
   
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id = &flatten_responseType($responseType);
Line 2557  sub handback_files { Line 2746  sub handback_files {
                     my ($answer_name,$answer_ver,$answer_ext) =                      my ($answer_name,$answer_ver,$answer_ext) =
         &file_name_version_ext($answer_file);          &file_name_version_ext($answer_file);
     my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);      my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
     my @dir_list = &Apache::lonnet::dirlist($portfolio_path,$domain,$stuname,$portfolio_root);                      my $getpropath = 1;
       my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,$domain,$stuname,$getpropath);
     my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);      my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
                     # fix file name                      # fix file name
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);                      my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
Line 2565  sub handback_files { Line 2755  sub handback_files {
                                            $newflg.'_'.$part_resp.'_returndoc'.$file_counter,                                             $newflg.'_'.$part_resp.'_returndoc'.$file_counter,
                                            $save_file_name);                                             $save_file_name);
                     if ($result !~ m|^/uploaded/|) {                      if ($result !~ m|^/uploaded/|) {
                         $request->print('<span class="LC_error">An error occurred ('.$result.                          $request->print('<br /><span class="LC_error">'.
                         ') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'</span><br />');                              &mt('An error occurred ([_1]) while trying to upload [_2].',
                                   $result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter).
                                           '</span>');
                     } else {                      } else {
                         # mark the file as read only                          # mark the file as read only
                         my @files = ($save_file_name);                          my @files = ($save_file_name);
Line 2663  sub decrement_aggs { Line 2855  sub decrement_aggs {
     if ($aggtries == $totaltries) {      if ($aggtries == $totaltries) {
         $decrement{'users'} = 1;          $decrement{'users'} = 1;
     }      }
     foreach my $type (keys (%decrement)) {      foreach my $type (keys(%decrement)) {
         $$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type};          $$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type};
     }      }
     return;      return;
Line 2693  sub version_portfiles { Line 2885  sub version_portfiles {
     my $version_parts = join('|',@$v_flag);      my $version_parts = join('|',@$v_flag);
     my @returned_keys;      my @returned_keys;
     my $parts = join('|', @$parts_graded);      my $parts = join('|', @$parts_graded);
     my $portfolio_root = &propath($domain,$stu_name).      my $portfolio_root = '/userfiles/portfolio';
  '/userfiles/portfolio';  
     foreach my $key (keys(%$record)) {      foreach my $key (keys(%$record)) {
         my $new_portfiles;          my $new_portfiles;
         if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {          if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
Line 2705  sub version_portfiles { Line 2896  sub version_portfiles {
                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);                  my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
  my ($answer_name,$answer_ver,$answer_ext) =   my ($answer_name,$answer_ver,$answer_ext) =
     &file_name_version_ext($answer_file);      &file_name_version_ext($answer_file);
                 my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stu_name,$portfolio_root);                  my $getpropath = 1;    
                   my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,$stu_name,$getpropath);
                 my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);                  my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
                 my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);                  my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);
                 if ($new_answer ne 'problem getting file') {                  if ($new_answer ne 'problem getting file') {
Line 2751  sub version_selected_portfile { Line 2943  sub version_selected_portfile {
     my $new_answer;      my $new_answer;
     $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");      $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");
     if($env{'form.copy'} eq '-1') {      if($env{'form.copy'} eq '-1') {
         &Apache::lonnet::logthis('problem getting file '.$file_name);  
         $new_answer = 'problem getting file';          $new_answer = 'problem getting file';
     } else {      } else {
         $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;          $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;
Line 2786  sub file_name_version_ext { Line 2977  sub file_name_version_ext {
 sub viewgrades_js {  sub viewgrades_js {
     my ($request) = shift;      my ($request) = shift;
   
       my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
     $request->print(<<VIEWJAVASCRIPT);      $request->print(<<VIEWJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
    function writePoint(partid,weight,point) {     function writePoint(partid,weight,point) {
Line 2794  sub viewgrades_js { Line 2986  sub viewgrades_js {
  if (point == "textval") {   if (point == "textval") {
     point = document.classgrade["TEXTVAL_"+partid].value;      point = document.classgrade["TEXTVAL_"+partid].value;
     if (isNaN(point) || parseFloat(point) < 0) {      if (isNaN(point) || parseFloat(point) < 0) {
  alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));   alert("$alertmsg"+parseFloat(point));
  var resetbox = false;   var resetbox = false;
  for (var i=0; i<radioButton.length; i++) {   for (var i=0; i<radioButton.length; i++) {
     if (radioButton[i].checked) {      if (radioButton[i].checked) {
Line 2892  sub viewgrades_js { Line 3084  sub viewgrades_js {
  var weight = document.classgrade["weight_"+partid].value;   var weight = document.classgrade["weight_"+partid].value;
   
  if (isNaN(point) || parseFloat(point) < 0) {   if (isNaN(point) || parseFloat(point) < 0) {
     alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));      alert("$alertmsg"+parseFloat(point));
     textbox.value = "";      textbox.value = "";
     return;      return;
  }   }
Line 2966  sub viewgrades { Line 3158  sub viewgrades {
     &Apache::lonnet::clear_EXT_cache_status();      &Apache::lonnet::clear_EXT_cache_status();
   
     my $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>';      my $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>';
     $result.='<h4><b>Current Resource: </b>'.$env{'form.probTitle'}.'</h4>'."\n";      $result.='<h4>'.&mt('<b>Current Resource: </b>[_1]',$env{'form.probTitle'}).'</h4>'."\n";
   
     #view individual student submission form - called using Javascript viewOneStudent      #view individual student submission form - called using Javascript viewOneStudent
     $result.=&jscriptNform($symb);      $result.=&jscriptNform($symb);
   
     #beginning of class grading form      #beginning of class grading form
       my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".      $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="command" value="editgrades" />'."\n".   '<input type="hidden" name="command" value="editgrades" />'."\n".
  &build_section_inputs().   &build_section_inputs().
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="Status" value="'.$env{'form.Status'}.'" />'."\n".   '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".
  '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";   '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
   
     my $sectionClass;      my $sectionClass;
     my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));      my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     if ($env{'form.section'} eq 'all') {      if ($env{'form.section'} eq 'all') {
  $sectionClass='Class </h3>';   $sectionClass=&mt('Class');
     } elsif ($env{'form.section'} eq 'none') {      } elsif ($env{'form.section'} eq 'none') {
  $sectionClass=&mt('Students in no Section').'</h3>';   $sectionClass=&mt('Students in no Section');
     } else {      } else {
  $sectionClass=&mt('Students in Section(s) [_1]',$section_display).'</h3>';   $sectionClass=&mt('Students in Section(s) [_1]');
     }      }
     $result.='<h3>'.&mt('Assign Common Grade To [_1]',$sectionClass);      $result.=
     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".   '<h3>'.
  '<table border=0><tr bgcolor="#ffffdd"><td>';   &mt("Assign Common Grade to [_1]",$sectionClass,$section_display).'</h3>';
       $result.= &Apache::loncommon::start_data_table();
     #radio buttons/text box for assigning points for a section or class.      #radio buttons/text box for assigning points for a section or class.
     #handles different parts of a problem      #handles different parts of a problem
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my ($partlist,$handgrade,$responseType) = &response_type($symb);
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     $result.='<table border="0">';  
     my %seen = ();      my %seen = ();
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {      foreach my $part_response_id (@part_response_id) {
Line 3009  sub viewgrades { Line 3202  sub viewgrades {
  my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);   my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
  $weight{$partid} = $wgt eq '' ? '1' : $wgt;   $weight{$partid} = $wgt eq '' ? '1' : $wgt;
   
  $result.='<input type="hidden" name="partid_'.  
     $ctsparts.'" value="'.$partid.'" />'."\n";  
  $result.='<input type="hidden" name="weight_'.  
     $partid.'" value="'.$weight{$partid}.'" />'."\n";  
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  $result.='<tr><td><b>Part:</b> '.$display_part.'&nbsp; &nbsp;<b>Point:</b> </td><td>';   my $radio.='<table border="0"><tr>';  
  $result.='<table border="0"><tr>';    
  my $ctr = 0;   my $ctr = 0;
  while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across   while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
     $result.= '<td><label><input type="radio" name="RADVAL_'.$partid.'" '.      $radio.= '<td><label><input type="radio" name="RADVAL_'.$partid.'" '.
  'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.   'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.
  ','.$ctr.')" />'.$ctr."</label></td>\n";   ','.$ctr.')" />'.$ctr."</label></td>\n";
     $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');      $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
     $ctr++;      $ctr++;
  }   }
  $result.='</tr></table>';   $radio.='</tr></table>';
  $result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'.   my $line = '<input type="text" name="TEXTVAL_'.
     $partid.'" size="4" '.'onChange="javascript:writePoint(\''.      $partid.'" size="4" '.'onChange="javascript:writePoint(\''.
  $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.   $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
     $weight{$partid}.' (problem weight)</td>'."\n";      $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";
  $result.= '</td><td><select name="SELVAL_'.$partid.'"'.   $line.= '<td><b>'.&mt('Grade Status').':</b><select name="SELVAL_'.$partid.'"'.
     'onChange="javascript:writeRadText(\''.$partid.'\','.      'onChange="javascript:writeRadText(\''.$partid.'\','.
  $weight{$partid}.')"> '.   $weight{$partid}.')"> '.
     '<option selected="selected"> </option>'.      '<option selected="selected"> </option>'.
     '<option>excused</option>'.      '<option value="excused">'.&mt('excused').'</option>'.
     '<option>reset status</option></select></td>'.      '<option value="reset status">'.&mt('reset status').'</option>'.
             '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";      '</select></td>'.
               '<td><label><input type="checkbox" name="FORCE_'.$partid.'" />'.&mt('Override "Correct"').'</label>';
    $line.='<input type="hidden" name="partid_'.
       $ctsparts.'" value="'.$partid.'" />'."\n";
    $line.='<input type="hidden" name="weight_'.
       $partid.'" value="'.$weight{$partid}.'" />'."\n";
   
    $result.=
       &Apache::loncommon::start_data_table_row()."\n".
       '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
       &Apache::loncommon::end_data_table_row()."\n";
  $ctsparts++;   $ctsparts++;
     }      }
     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".      $result.=&Apache::loncommon::end_data_table()."\n".
  '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';   '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
     $result.='<input type="button" value="Revert to Default" '.      $result.='<input type="button" value="'.&mt('Revert to Default').'" '.
  'onClick="javascript:resetEntry('.$ctsparts.');" target="_self" />';   'onClick="javascript:resetEntry('.$ctsparts.');" />';
   
     #table listing all the students in a section/class      #table listing all the students in a section/class
     #header of table      #header of table
     $result.= '<h3>Assign Grade to Specific Students in '.$sectionClass;      $result.= '<h3>'.&mt('Assign Grade to Specific Students in ').$sectionClass,
     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".   $section_display.'</h3>';
  '<table border=0><tr bgcolor="#deffff"><td>&nbsp;<b>No.</b>&nbsp;</td>'.      $result.= &Apache::loncommon::start_data_table().
  '<td>'.&nameUserString('header')."</td>\n";   &Apache::loncommon::start_data_table_header_row().
    '<th>'.&mt('No.').'</th>'.
    '<th>'.&nameUserString('header')."</th>\n";
     my (@parts) = sort(&getpartlist($symb));      my (@parts) = sort(&getpartlist($symb));
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);      my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     my @partids = ();      my @partids = ();
     foreach my $part (@parts) {      foreach my $part (@parts) {
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display');
  $display =~ s|^Number of Attempts|Tries<br />|; # makes the column narrower          my $narrowtext = &mt('Tries');
    $display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower
  if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }   if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
  my ($partid) = &split_part_type($part);   my ($partid) = &split_part_type($part);
         push(@partids, $partid);          push(@partids,$partid);
  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.='<td><b>Score Part:</b> '.$display_part.      $result.='<th>'.
  ' <br /><b>(weight = '.$weight{$partid}.')</b></td>'."\n";   &mt('Score Part: [_1]<br /> (weight = [_2])',
       $display_part,$weight{$partid}).'</th>'."\n";
     next;      next;
       
  } else {   } else {
     $display =~s/\[Part: \Q$partid\E\]/Part:<\/b> $display_part/;      if ($display =~ /Problem Status/) {
    my $grade_status_mt = &mt('Grade Status');
    $display =~ s{Problem Status}{$grade_status_mt<br />};
       }
       my $part_mt = &mt('Part:');
       $display =~s{\[Part: \Q$partid\E\]}{$part_mt $display_part};
  }   }
  $display =~ s|Problem Status|Grade Status<br />|;  
  $result.='<td><b>'.$display.'</td>'."\n";   $result.='<th>'.$display.'</th>'."\n";
     }      }
     $result.='</tr>';      $result.=&Apache::loncommon::end_data_table_header_row();
   
     my %last_resets =       my %last_resets = 
  &get_last_resets($symb,$env{'request.course.id'},\@partids);   &get_last_resets($symb,$env{'request.course.id'},\@partids);
Line 3089  sub viewgrades { Line 3297  sub viewgrades {
  $result.=&viewstudentgrade($symb,$env{'request.course.id'},   $result.=&viewstudentgrade($symb,$env{'request.course.id'},
    $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets);     $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets);
     }      }
     $result.='</table></td></tr></table>';      $result.=&Apache::loncommon::end_data_table();
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";      $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
     $result.='<input type="button" value="Save" '.      $result.='<input type="button" value="'.&mt('Save').'" '.
  'onClick="javascript:submit();" target="_self" /></form>'."\n";   'onClick="javascript:submit();" target="_self" /></form>'."\n";
     if (scalar(%$fullname) eq 0) {      if (scalar(%$fullname) eq 0) {
  my $colspan=3+scalar(@parts);   my $colspan=3+scalar(@parts);
  $result='<span class="LC_warning">There are no students in section "'.$env{'form.section'}.   my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     '" with enrollment status "'.$env{'form.Status'}.'" to modify or grade.</span>';          my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));
    $result='<span class="LC_warning">'.
       &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade.',
           $section_display, $stu_status).
       '</span>';
     }      }
     $result.=&show_grading_menu_form($symb);      $result.=&show_grading_menu_form($symb);
     return $result;      return $result;
Line 3108  sub viewstudentgrade { Line 3320  sub viewstudentgrade {
     my ($uname,$udom) = split(/:/,$student);      my ($uname,$udom) = split(/:/,$student);
     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);      my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
     my %aggregates = ();       my %aggregates = (); 
     my $result='<tr bgcolor="#ffffdd"><td align="right">'.      my $result=&Apache::loncommon::start_data_table_row().'<td align="right">'.
  '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.   '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.
  "\n".$ctr.'&nbsp;</td><td>&nbsp;'.   "\n".$ctr.'&nbsp;</td><td>&nbsp;'.
  '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.   '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
Line 3150  sub viewstudentgrade { Line 3362  sub viewstudentgrade {
     $result.='&nbsp;<select name="'.      $result.='&nbsp;<select name="'.
  'GD_'.$student.'_'.$part.'_solved" '.   'GD_'.$student.'_'.$part.'_solved" '.
  'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";   'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
     $result.= (($status eq 'excused') ? '<option> </option><option selected="selected">excused</option>'       $result.= (($status eq 'excused') ? '<option> </option><option selected="selected" value="excused">'.&mt('excused').'</option>' 
  : '<option selected="selected"> </option><option>excused</option>')."\n";   : '<option selected="selected"> </option><option value="excused">'.&mt('excused').'</option>')."\n";
     $result.='<option>reset status</option>';      $result.='<option value="reset status">'.&mt('reset status').'</option>';
     $result.="</select>&nbsp;</td>\n";      $result.="</select>&nbsp;</td>\n";
  } else {   } else {
     $result.='<input type="hidden" name="'.      $result.='<input type="hidden" name="'.
Line 3163  sub viewstudentgrade { Line 3375  sub viewstudentgrade {
  'value="'.$score.'" size="4" /></td>'."\n";   'value="'.$score.'" size="4" /></td>'."\n";
  }   }
     }      }
     $result.='</tr>';      $result.=&Apache::loncommon::end_data_table_row();
     return $result;      return $result;
 }  }
   
Line 3173  sub editgrades { Line 3385  sub editgrades {
     my ($request) = @_;      my ($request) = @_;
   
     my $symb=&get_symb($request);      my $symb=&get_symb($request);
     my $title='<h3><span class="LC_info">Current Grade Status</span></h3>';      my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     $title.='<h4><b>Current Resource: </b>'.$env{'form.probTitle'}.'</h4><br />'."\n";      my $title='<h2>'.&mt('Current Grade Status').'</h2>';
     $title.='<h4><b>Section: </b>'.$env{'form.section'}.'</h4>'."\n";      $title.='<h4>'.&mt('<b>Current Resource: </b>[_1]',$env{'form.probTitle'}).'</h4>'."\n";
       $title.='<h4>'.&mt('<b>Section: </b>[_1]',$section_display).'</h4>'."\n";
     my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";  
     $result.= '<table border="0"><tr bgcolor="#deffff">'.      my $result= &Apache::loncommon::start_data_table().
  '<td rowspan=2 valign="center">&nbsp;<b>No.</b>&nbsp;</td>'.   &Apache::loncommon::start_data_table_header_row().
  '<td rowspan=2 valign="center">'.&nameUserString('header')."</td>\n";   '<th rowspan="2" valign="middle">'.&mt('No.').'</th>'.
    '<th rowspan="2" valign="middle">'.&nameUserString('header')."</th>\n";
     my %scoreptr = (      my %scoreptr = (
     'correct'  =>'correct_by_override',      'correct'  =>'correct_by_override',
     'incorrect'=>'incorrect_by_override',      'incorrect'=>'incorrect_by_override',
Line 3200  sub editgrades { Line 3412  sub editgrades {
     my $header;      my $header;
     while ($ctr < $env{'form.totalparts'}) {      while ($ctr < $env{'form.totalparts'}) {
  my $partid = $env{'form.partid_'.$ctr};   my $partid = $env{'form.partid_'.$ctr};
  push @partid,$partid;   push(@partid,$partid);
  $weight{$partid} = $env{'form.weight_'.$partid};   $weight{$partid} = $env{'form.weight_'.$partid};
  $ctr++;   $ctr++;
     }      }
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  $header .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.   $header .= '<th align="center">'.&mt('Old Score').'</th>'.
     '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';      '<th align="center">'.&mt('New Score').'</th>';
  $columns{$partid}=2;   $columns{$partid}=2;
  foreach my $stores (@parts) {   foreach my $stores (@parts) {
     my ($part,$type) = &split_part_type($stores);      my ($part,$type) = &split_part_type($stores);
     if ($part !~ m/^\Q$partid\E/) { next;}      if ($part !~ m/^\Q$partid\E/) { next;}
     if ($type eq 'awarded' || $type eq 'solved') { next; }      if ($type eq 'awarded' || $type eq 'solved') { next; }
     my $display=&Apache::lonnet::metadata($url,$stores.'.display');      my $display=&Apache::lonnet::metadata($url,$stores.'.display');
     $display =~ s/\[Part: (\w)+\]//;      $display =~ s/\[Part: \Q$part\E\]//;
     $display =~ s/Number of Attempts/Tries/;              my $narrowtext = &mt('Tries');
     $header .= '<td align="center">&nbsp;<b>Old '.$display.'</b>&nbsp;</td>'.      $display =~ s/Number of Attempts/$narrowtext/;
  '<td align="center">&nbsp;<b>New '.$display.'</b>&nbsp;</td>';      $header .= '<th align="center">'.&mt('Old').' '.$display.'</th>'.
    '<th align="center">'.&mt('New').' '.$display.'</th>';
     $columns{$partid}+=2;      $columns{$partid}+=2;
  }   }
     }      }
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  $result .= '<td colspan="'.$columns{$partid}.   $result .= '<th colspan="'.$columns{$partid}.'" align="center">'.
     '" align="center"><b>Part:</b> '.$display_part.      &mt('Part: [_1] (Weight = [_2])',$display_part,$weight{$partid}).
     ' (Weight = '.$weight{$partid}.')</td>';      '</th>';
   
     }      }
     $result .= '</tr><tr bgcolor="#deffff">';      $result .= &Apache::loncommon::end_data_table_header_row().
     $result .= $header;   &Apache::loncommon::start_data_table_header_row().
     $result .= '</tr>'."\n";   $header.
     my $noupdate;   &Apache::loncommon::end_data_table_header_row();
       my @noupdate;
     my ($updateCtr,$noupdateCtr) = (1,1);      my ($updateCtr,$noupdateCtr) = (1,1);
     for ($i=0; $i<$env{'form.total'}; $i++) {      for ($i=0; $i<$env{'form.total'}; $i++) {
  my $line;   my $line;
Line 3243  sub editgrades { Line 3457  sub editgrades {
  my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
  if (!&canmodify($usec)) {   if (!&canmodify($usec)) {
     my $numcols=scalar(@partid)*4+2;      my $numcols=scalar(@partid)*4+2;
     $noupdate.=$line."<td colspan=\"$numcols\"><span class=\"LC_warning\">Not allowed to modify student</span></td></tr>";      push(@noupdate,
    $line."<td colspan=\"$numcols\"><span class=\"LC_warning\">".
    &mt('Not allowed to modify student')."</span></td></tr>");
     next;      next;
  }   }
         my %aggregate = ();          my %aggregate = ();
Line 3312  sub editgrades { Line 3528  sub editgrades {
     '<td align="center">'.$awarded.'&nbsp;</td>';      '<td align="center">'.$awarded.'&nbsp;</td>';
     }      }
  }   }
  $line.='</tr>'."\n";   $line.="\n";
   
  my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};   my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
  my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};   my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
Line 3345  sub editgrades { Line 3561  sub editgrades {
  }   }
     }      }
   
     $result.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line;      $result.=&Apache::loncommon::start_data_table_row().
    '<td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line.
    &Apache::loncommon::end_data_table_row();
     $updateCtr++;      $updateCtr++;
  } else {   } else {
     $noupdate.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line;      push(@noupdate,
    '<td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line);
     $noupdateCtr++;      $noupdateCtr++;
  }   }
         if ($aggregateflag) {          if ($aggregateflag) {
Line 3356  sub editgrades { Line 3575  sub editgrades {
   $cdom,$cnum);    $cdom,$cnum);
         }          }
     }      }
     if ($noupdate) {      if (@noupdate) {
 # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;  # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
  my $numcols=scalar(@partid)*4+2;   my $numcols=scalar(@partid)*4+2;
  $result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occurred For the Students Below</td></tr><tr bgcolor="#ffffde">'.$noupdate;   $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').
     }      '<td align="center" colspan="'.$numcols.'">'.
     $result .= '</table></td></tr></table>'."\n".      &mt('No Changes Occurred For the Students Below').
  &show_grading_menu_form ($symb);      '</td>'.
     my $msg = '<br /><b>Number of records updated = '.$rec_update.      &Apache::loncommon::end_data_table_row();
  ' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.   foreach my $line (@noupdate) {
  '<b>Total number of students = '.$env{'form.total'}.'</b><br />';      $result.=
    &Apache::loncommon::start_data_table_row().
    $line.
    &Apache::loncommon::end_data_table_row();
    }
       }
       $result .= &Apache::loncommon::end_data_table().
    &show_grading_menu_form($symb);
       my $msg = '<p><b>'.
    &mt('Number of records updated = [_1] for [quant,_2,student].',
       $rec_update,$count).'</b><br />'.
    '<b>'.&mt('Total number of students = [_1]',$env{'form.total'}).
    '</b></p>';
     return $title.$msg.$result;      return $title.$msg.$result;
 }  }
   
Line 3373  sub split_part_type { Line 3604  sub split_part_type {
     my ($partstr) = @_;      my ($partstr) = @_;
     my ($temp,@allparts)=split(/_/,$partstr);      my ($temp,@allparts)=split(/_/,$partstr);
     my $type=pop(@allparts);      my $type=pop(@allparts);
     my $part=join('.',@allparts);      my $part=join('_',@allparts);
     return ($part,$type);      return ($part,$type);
 }  }
   
Line 3541  ENDPICK Line 3772  ENDPICK
 }  }
   
 sub checkforfile_js {  sub checkforfile_js {
       my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
     my $result =<<CSVFORMJS;      my $result =<<CSVFORMJS;
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
     alert("Please use the browse button to select a file from your local directory.");      alert("$alertmsg");
     return false;      return false;
  }   }
  formname.submit();   formname.submit();
Line 3565  sub upcsvScores_form { Line 3797  sub upcsvScores_form {
     $result.=$table;      $result.=$table;
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";      $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";      $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
     $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource').      $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource.').
  '.</b></td></tr>'."\n";   '</b></td></tr>'."\n";
     $result.='<tr bgcolor=#ffffe6><td>'."\n";      $result.='<tr bgcolor=#ffffe6><td>'."\n";
     my $upload=&mt("Upload Scores");      my $upload=&mt("Upload Scores");
     my $upfile_select=&Apache::loncommon::upfile_select_html();      my $upfile_select=&Apache::loncommon::upfile_select_html();
Line 3757  sub csvuploadassign { Line 3989  sub csvuploadassign {
                 if ($wgt) {                  if ($wgt) {
                     $entries{$fields{$dest}}=~s/\s//g;                      $entries{$fields{$dest}}=~s/\s//g;
                     my $pcr=$entries{$fields{$dest}} / $wgt;                      my $pcr=$entries{$fields{$dest}} / $wgt;
                     my $award='correct_by_override';                      my $award=($pcr == 0) ? 'incorrect_by_override'
                                             : 'correct_by_override';
                     $grades{"resource.$part.awarded"}=$pcr;                      $grades{"resource.$part.awarded"}=$pcr;
                     $grades{"resource.$part.solved"}=$award;                      $grades{"resource.$part.solved"}=$award;
                     $points{$part}=1;                      $points{$part}=1;
Line 3777  sub csvuploadassign { Line 4010  sub csvuploadassign {
  $grades{$store_key}=$entries{$fields{$dest}};   $grades{$store_key}=$entries{$fields{$dest}};
     }      }
  }   }
  if (! %grades) { push(@skipped,"$username:$domain no data to save"); }   if (! %grades) { 
  $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";             push(@skipped,&mt("[_1]: no data to save","$username:$domain")); 
 # &Apache::lonnet::logthis(" storing ".(join('-',%grades)));          } else {
  my $result=&Apache::lonnet::cstore(\%grades,$symb,     $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
      my $result=&Apache::lonnet::cstore(\%grades,$symb,
    $env{'request.course.id'},     $env{'request.course.id'},
    $domain,$username);     $domain,$username);
  if ($result eq 'ok') {     if ($result eq 'ok') {
     $request->print('.');        $request->print('.');
  } else {     } else {
     $request->print("<p>        $request->print("<p><span class=\"LC_error\">".
                               <span class=\"LC_error\">                                &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
                                  Failed to save student $username:$domain.                                    "$username:$domain",$result)."</span></p>");
                                  Message when trying to save was ($result)     }
                               </span>     $request->rflush();
                              </p>" );     $countdone++;
  }          }
  $request->rflush();  
  $countdone++;  
     }      }
     $request->print("<br />Saved $countdone students\n");      $request->print('<br /><span class="LC_info">'.&mt("Saved [_1] students",$countdone)."</span>\n");
     if (@skipped) {      if (@skipped) {
  $request->print('<p><h4><b>Skipped Students</b></h4></p>');   $request->print('<p><span class="LC_warning">'.&mt('Skipped Students').'</span></p>');
  foreach my $student (@skipped) { $request->print("$student<br />\n"); }   foreach my $student (@skipped) { $request->print("$student<br />\n"); }
     }      }
     if (@notallowed) {      if (@notallowed) {
  $request->print('<p><span class="LC_error">Students Not Allowed to Modify</span></p>');   $request->print('<p><span class="LC_error">'.&mt('Students Not Allowed to Modify').'</span></p>');
  foreach my $student (@notallowed) { $request->print("$student<br />\n"); }   foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
     }      }
     $request->print("<br />\n");      $request->print("<br />\n");
Line 3819  sub csvuploadassign { Line 4051  sub csvuploadassign {
 sub pickStudentPage {  sub pickStudentPage {
     my ($request) = shift;      my ($request) = shift;
   
       my $alertmsg = &mt('Please select the student you wish to grade.');
     $request->print(<<LISTJAVASCRIPT);      $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
   
 function checkPickOne(formname) {  function checkPickOne(formname) {
     if (radioSelection(formname.student) == null) {      if (radioSelection(formname.student) == null) {
  alert("Please select the student you wish to grade.");   alert("$alertmsg");
  return;   return;
     }      }
     ptr = pullDownSelection(formname.selectpage);      ptr = pullDownSelection(formname.selectpage);
Line 3842  LISTJAVASCRIPT Line 4075  LISTJAVASCRIPT
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
   
     my $result='<h3><span class="LC_info">&nbsp;'.      my $result='<h3><span class="LC_info">&nbsp;'.
  'Manual Grading by Page or Sequence</span></h3>';   &mt('Manual Grading by Page or Sequence').'</span></h3>';
   
     $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";      $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
     $result.='&nbsp;<b>Problems from:</b> <select name="selectpage">'."\n";  
     my ($titles,$symbx) = &getSymbMap();      my ($titles,$symbx) = &getSymbMap();
     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";
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<option value="'.$ctr.'" '.   $select.='<option value="'.$ctr.'" '.
     ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').      ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').
     '>'.$showtitle.'</option>'."\n";      '>'.$showtitle.'</option>'."\n";
  $ctr++;   $ctr++;
     }      }
     $result.= '</select>'."<br />\n";      $select.= '</select>';
       $result.='&nbsp;<b>'.&mt('Problems from').':</b> '.$select."<br />\n";
   
     $ctr=0;      $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
Line 3869  LISTJAVASCRIPT Line 4104  LISTJAVASCRIPT
     $result.='<input type="hidden" name="page" />'."\n".      $result.='<input type="hidden" name="page" />'."\n".
  '<input type="hidden" name="title" />'."\n";   '<input type="hidden" name="title" />'."\n";
   
     $result.='&nbsp;<b>View Problems Text: </b><label><input type="radio" name="vProb" value="no" checked="checked" /> no </label>'."\n".      my $options =
  '<label><input type="radio" name="vProb" value="yes" /> yes </label>'."<br />\n";   '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".
    '<label><input type="radio" name="vProb" value="yes" /> '.&mt('yes').' </label>'."<br />\n";
     $result.='&nbsp;<b>Submission Details: </b>'.      $result.='&nbsp;<b>'.&mt('View Problem Text').': </b>'.$options;
  '<label><input type="radio" name="lastSub" value="none" /> none</label>'."\n".  
  '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> by dates and submissions</label>'."\n".      $options =
  '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n";   '<label><input type="radio" name="lastSub" value="none" /> '.&mt('none').' </label>'."\n".
    '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.&mt('by dates and submissions').'</label>'."\n".
    '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').' </label>'."\n";
       $result.='&nbsp;<b>'.&mt('Submissions').': </b>'.$options;
           
     $result.=&build_section_inputs();      $result.=&build_section_inputs();
     $result.='<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".      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="command" value="displayPage" />'."\n".
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";
   
     $result.='&nbsp;<b>'.&mt('Use CODE:').' </b>'.      $result.='&nbsp;<b>'.&mt('Use CODE').': </b> <input type="text" name="CODE" value="" /> <br />'."\n";
  '<input type="text" name="CODE" value="" /><br />'."\n";  
   
     $result.='&nbsp;<input type="button" '.      $result.='&nbsp;<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);"value="Next->" /><br />'."\n";   'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";
   
     $request->print($result);      $request->print($result);
   
     my $studentTable.='&nbsp;<b>Select a student you wish to grade and then click on the Next button.</b><br />'.      my $studentTable.='&nbsp;<b>'.&mt('Select a student you wish to grade and then click on the Next button.').'</b><br />'.
  '<table border="0"><tr><td bgcolor="#777777">'.   &Apache::loncommon::start_data_table().
  '<table border="0"><tr bgcolor="#e6ffff">'.   &Apache::loncommon::start_data_table_header_row().
  '<td align="right">&nbsp;<b>No.</b></td>'.   '<th align="right">&nbsp;'.&mt('No.').'</th>'.
  '<td>'.&nameUserString('header').'</td>'.   '<th>'.&nameUserString('header').'</th>'.
  '<td align="right">&nbsp;<b>No.</b></td>'.   '<th align="right">&nbsp;'.&mt('No.').'</th>'.
  '<td>'.&nameUserString('header').'</td></tr>';   '<th>'.&nameUserString('header').'</th>'.
    &Apache::loncommon::end_data_table_header_row();
     
     my (undef,undef,$fullname) = &getclasslist($getsec,'1');      my (undef,undef,$fullname) = &getclasslist($getsec,'1');
     my $ptr = 1;      my $ptr = 1;
Line 3909  LISTJAVASCRIPT Line 4148  LISTJAVASCRIPT
      return $a cmp $b;       return $a cmp $b;
  } (keys(%$fullname))) {   } (keys(%$fullname))) {
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
  $studentTable.=($ptr%2 == 1 ? '<tr bgcolor="#ffffe6">' : '</td>');   $studentTable.=($ptr%2==1 ? &Apache::loncommon::start_data_table_row()
                                     : '</td>');
  $studentTable.='<td align="right">'.$ptr.'&nbsp;</td>';   $studentTable.='<td align="right">'.$ptr.'&nbsp;</td>';
  $studentTable.='<td>&nbsp;<label><input type="radio" name="student" value="'.$student.'" /> '   $studentTable.='<td>&nbsp;<label><input type="radio" name="student" value="'.$student.'" /> '
     .&nameUserString(undef,$$fullname{$student},$uname,$udom)."</label>\n";      .&nameUserString(undef,$$fullname{$student},$uname,$udom)."</label>\n";
  $studentTable.=($ptr%2 == 0 ? '</td></tr>' : '');   $studentTable.=
       ($ptr%2 == 0 ? '</td>'.&Apache::loncommon::end_data_table_row() 
                            : '');
  $ptr++;   $ptr++;
     }      }
     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;</td></tr>' if ($ptr%2 == 0);      if ($ptr%2 == 0) {
     $studentTable.='</table></td></tr></table>'."\n";   $studentTable.='</td><td>&nbsp;</td><td>&nbsp;</td>'.
       &Apache::loncommon::end_data_table_row();
       }
       $studentTable.=&Apache::loncommon::end_data_table()."\n";
     $studentTable.='<input type="button" '.      $studentTable.='<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);"value="Next->" /></form>'."\n";   'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /></form>'."\n";
   
     $studentTable.=&show_grading_menu_form($symb);      $studentTable.=&show_grading_menu_form($symb);
     $request->print($studentTable);      $request->print($studentTable);
Line 3971  sub displayPage { Line 4216  sub displayPage {
     &Apache::lonnet::clear_EXT_cache_status();      &Apache::lonnet::clear_EXT_cache_status();
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print('<span class="LC_warning">Unable to view requested student.('.$env{'form.student'}.')</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to view requested student. ([_1])',$env{'form.student'}).'</span>');
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;   return;
     }      }
     my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';      my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';
     $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom).      $result.='<h3>&nbsp;'.&mt('Student: [_1]',&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom)).
  '</h3>'."\n";   '</h3>'."\n";
     if (&Apache::lonnet::validCODE($env{'form.CODE'})) {      $env{'form.CODE'} = uc($env{'form.CODE'});
  $result.='<h3>&nbsp;CODE: '.$env{'form.CODE'}.'</h3>'."\n";      if (&Apache::lonnet::validCODE(uc($env{'form.CODE'}))) {
    $result.='<h3>&nbsp;'.&mt('CODE: [_1]',$env{'form.CODE'}).'</h3>'."\n";
     } else {      } else {
  delete($env{'form.CODE'});   delete($env{'form.CODE'});
     }      }
Line 3990  sub displayPage { Line 4236  sub displayPage {
     my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});      my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {      if (!$map) {
  $request->print('<span class="LC_warning">Unable to view requested sequence. ('.$resUrl.')</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to view requested sequence. ([_1])',$resUrl).'</span>');
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;    return; 
     }      }
Line 4012  sub displayPage { Line 4258  sub displayPage {
     '<input type="hidden" name="CODE" value="'.$env{'form.CODE'}.'" />'."\n";      '<input type="hidden" name="CODE" value="'.$env{'form.CODE'}.'" />'."\n";
     }      }
     my $checkIcon = '<img alt="'.&mt('Check Mark').      my $checkIcon = '<img alt="'.&mt('Check Mark').
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.&Apache::loncommon::lonhttpdurl($request->dir_config('lonIconsURL').'/check.gif').'" height="16" border="0" />';
  '/check.gif" height="16" border="0" />';  
   
     $studentTable.='&nbsp;<b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon.      $studentTable.='&nbsp;'.&mt('<b>Note:</b> Problems graded correct by the computer are marked with a [_1] symbol.',$checkIcon)."\n".
  ' symbol.'."\n".   &Apache::loncommon::start_data_table().
  '<table border="0"><tr><td bgcolor="#777777">'.   &Apache::loncommon::start_data_table_header_row().
  '<table border="0"><tr bgcolor="#e6ffff">'.   '<th align="center">&nbsp;Prob.&nbsp;</th>'.
  '<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.   '<th>&nbsp;'.($env{'form.vProb'} eq 'no' ? &mt('Title') : &mt('Problem Text')).'/'.&mt('Grade').'</th>'.
  '<td><b>&nbsp;'.($env{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';   &Apache::loncommon::end_data_table_header_row();
   
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
     my ($depth,$question,$prob) = (1,1,1);      my ($depth,$question,$prob) = (1,1,1);
Line 4034  sub displayPage { Line 4279  sub displayPage {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
     $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.      $studentTable.=
  (scalar(@{$parts}) == 1 ? '' : '<br />('.scalar(@{$parts}).'&nbsp;parts)').'</td>';   &Apache::loncommon::start_data_table_row().
    '<td align="center" valign="top" >'.$prob.
    (scalar(@{$parts}) == 1 ? '' 
                           : '<br />('.&mt('[_1]&nbsp;parts)',
    scalar(@{$parts}))
    ).
    '</td>';
     $studentTable.='<td valign="top">';      $studentTable.='<td valign="top">';
     my %form = ('CODE' => $env{'form.CODE'},);      my %form = ('CODE' => $env{'form.CODE'},);
     if ($env{'form.vProb'} eq 'yes' ) {      if ($env{'form.vProb'} eq 'yes' ) {
Line 4050  sub displayPage { Line 4301  sub displayPage {
 #    $request->print('match='.$1."<br />\n");  #    $request->print('match='.$1."<br />\n");
 # }  # }
 # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;  # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
  $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>Correct answer:</b><br />'.$companswer;   $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;
     }      }
   
     my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
   
     if ($env{'form.lastSub'} eq 'datesub') {      if ($env{'form.lastSub'} eq 'datesub') {
  if ($record{'version'} eq '') {   if ($record{'version'} eq '') {
     $studentTable.='<br />&nbsp;<span class="LC_warning">No recorded submission for this problem</span><br />';      $studentTable.='<br />&nbsp;<span class="LC_warning">'.&mt('No recorded submission for this problem.').'</span><br />';
  } else {   } else {
     my %responseType = ();      my %responseType = ();
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
Line 4093  sub displayPage { Line 4344  sub displayPage {
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
   
     $studentTable.='</table></td></tr></table>'."\n".      $studentTable.='</table>'."\n".
  '<input type="button" value="Save" '.   '<input type="button" value="'.&mt('Save').'" '.
  'onClick="javascript:checkSubmitPage(this.form,'.$question.');" />'.   'onClick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
  '</form>'."\n";   '</form>'."\n";
     $studentTable.=&show_grading_menu_form($symb);      $studentTable.=&show_grading_menu_form($symb);
Line 4108  sub displaySubByDates { Line 4359  sub displaySubByDates {
     my $isCODE=0;      my $isCODE=0;
     my $isTask = ($symb =~/\.task$/);      my $isTask = ($symb =~/\.task$/);
     if (exists($record->{'resource.CODE'})) { $isCODE=1; }      if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.      my $studentTable=&Apache::loncommon::start_data_table().
  '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.   &Apache::loncommon::start_data_table_header_row().
  '<td><b>Date/Time</b></td>'.   '<th>'.&mt('Date/Time').'</th>'.
  ($isCODE?'<td><b>CODE</b></td>':'').   ($isCODE?'<th>'.&mt('CODE').'</th>':'').
  '<td><b>Submission</b></td>'.   '<th>'.&mt('Submission').'</th>'.
  '<td><b>Status&nbsp;</b></td></tr>';   '<th>'.&mt('Status').'</th>'.
    &Apache::loncommon::end_data_table_header_row();
     my ($version);      my ($version);
     my %mark;      my %mark;
     my %orders;      my %orders;
     $mark{'correct_by_student'} = $checkIcon;      $mark{'correct_by_student'} = $checkIcon;
     if (!exists($$record{'1:timestamp'})) {      if (!exists($$record{'1:timestamp'})) {
  return '<br />&nbsp;<span class="LC_warning">Nothing submitted - no attempts</span><br />';   return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
     }      }
   
     my $interaction;      my $interaction;
       my $no_increment = 1;
     for ($version=1;$version<=$$record{'version'};$version++) {      for ($version=1;$version<=$$record{'version'};$version++) {
  my $timestamp = scalar(localtime($$record{$version.':timestamp'}));   my $timestamp = 
       &Apache::lonlocal::locallocaltime($$record{$version.':timestamp'});
  if (exists($$record{$version.':resource.0.version'})) {   if (exists($$record{$version.':resource.0.version'})) {
     $interaction = $$record{$version.':resource.0.version'};      $interaction = $$record{$version.':resource.0.version'};
  }   }
   
  my $where = ($isTask ? "$version:resource.$interaction"   my $where = ($isTask ? "$version:resource.$interaction"
              : "$version:resource");               : "$version:resource");
  #&Apache::lonnet::logthis(" got $where");   $studentTable.=&Apache::loncommon::start_data_table_row().
  $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';      '<td>'.$timestamp.'</td>';
  if ($isCODE) {   if ($isCODE) {
     $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>';      $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>';
  }   }
Line 4151  sub displaySubByDates { Line 4405  sub displaySubByDates {
   
     my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)      my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));                 : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
     #&Apache::lonnet::logthis("match $matchKey $responseId (".$$record{$version.':'.$matchKey});      $displaySub[0].='<b>'.&mt('Part:').'</b>&nbsp;'.$display_part.'&nbsp;';
     $displaySub[0].='<b>Part:</b>&nbsp;'.$display_part.'&nbsp;';      $displaySub[0].='<span class="LC_internal_info">('.&mt('ID').'&nbsp;'.
     $displaySub[0].='<span class="LC_internal_info">(ID&nbsp;'.  
  $responseId.')</span>&nbsp;<b>';   $responseId.')</span>&nbsp;<b>';
     if ($$record{"$where.$partid.tries"} eq '') {      if ($$record{"$where.$partid.tries"} eq '') {
  $displaySub[0].='Trial&nbsp;not&nbsp;counted';   $displaySub[0].=&mt('Trial&nbsp;not&nbsp;counted');
     } else {      } else {
  $displaySub[0].='Trial&nbsp;'.   $displaySub[0].=&mt('Trial&nbsp;[_1]',
     $$record{"$where.$partid.tries"};      $$record{"$where.$partid.tries"});
     }      }
     my $responseType=($isTask ? 'Task'      my $responseType=($isTask ? 'Task'
                                               : $responseType->{$partid}->{$responseId});                                                : $responseType->{$partid}->{$responseId});
     if (!exists($orders{$partid})) { $orders{$partid}={}; }      if (!exists($orders{$partid})) { $orders{$partid}={}; }
     if (!exists($orders{$partid}->{$responseId})) {      if (!exists($orders{$partid}->{$responseId})) {
  $orders{$partid}->{$responseId}=   $orders{$partid}->{$responseId}=
     &get_order($partid,$responseId,$symb,$uname,$udom);      &get_order($partid,$responseId,$symb,$uname,$udom,
                                          $no_increment);
     }      }
     $displaySub[0].='</b>&nbsp; '.      $displaySub[0].='</b>&nbsp; '.
  &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />';   &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />';
  }   }
     }      }
     if (exists($$record{"$where.$partid.checkedin"})) {      if (exists($$record{"$where.$partid.checkedin"})) {
  $displaySub[1].='Checked in by '.   $displaySub[1].=&mt('Checked in by [_1] into slot [_2]',
     $$record{"$where.$partid.checkedin"}.' into slot '.      $$record{"$where.$partid.checkedin"},
     $$record{"$where.$partid.checkedin.slot"}.      $$record{"$where.$partid.checkedin.slot"}).
     '<br />';   '<br />';
     }      }
     if (exists $$record{"$where.$partid.award"}) {      if (exists $$record{"$where.$partid.award"}) {
  $displaySub[1].='<b>Part:</b>&nbsp;'.$display_part.' &nbsp;'.   $displaySub[1].='<b>'.&mt('Part:').'</b>&nbsp;'.$display_part.' &nbsp;'.
     lc($$record{"$where.$partid.award"}).' '.      lc($$record{"$where.$partid.award"}).' '.
     $mark{$$record{"$where.$partid.solved"}}.      $mark{$$record{"$where.$partid.solved"}}.
     '<br />';      '<br />';
Line 4199  sub displaySubByDates { Line 4453  sub displaySubByDates {
  }   }
  $studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1];   $studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1];
  if ($displaySub[2]) {   if ($displaySub[2]) {
     $studentTable.='Manually graded by '.$displaySub[2];      $studentTable.=&mt('Manually graded by [_1]',$displaySub[2]);
  }   }
  $studentTable.='&nbsp;</td></tr>';   $studentTable.='&nbsp;</td>'.
           &Apache::loncommon::end_data_table_row();
     }      }
     $studentTable.='</table></td></tr></table>';      $studentTable.=&Apache::loncommon::end_data_table();
     return $studentTable;      return $studentTable;
 }  }
   
Line 4219  sub updateGradeByPage { Line 4473  sub updateGradeByPage {
     my ($uname,$udom) = split(/:/,$env{'form.student'});      my ($uname,$udom) = split(/:/,$env{'form.student'});
     my $usec=$classlist->{$env{'form.student'}}[5];      my $usec=$classlist->{$env{'form.student'}}[5];
     if (!&canmodify($usec)) {      if (!&canmodify($usec)) {
  $request->print('<span class="LC_warning">Unable to modify requested student.('.$env{'form.student'}.'</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to modify requested student ([_1])',$env{'form.student'}).'</span>');
  $request->print(&show_grading_menu_form($env{'form.symb'}));   $request->print(&show_grading_menu_form($env{'form.symb'}));
  return;   return;
     }      }
     my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';      my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';
     $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).      $result.='<h3>&nbsp;'.&mt('Student: ').&nameUserString(undef,$env{'form.fullname'},$uname,$udom).
  '</h3>'."\n";   '</h3>'."\n";
   
     $request->print($result);      $request->print($result);
Line 4233  sub updateGradeByPage { Line 4487  sub updateGradeByPage {
     my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});      my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {      if (!$map) {
  $request->print('<span class="LC_warning">Unable to grade requested sequence. ('.$resUrl.')</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to grade requested sequence ([_1]).',$resUrl).'</span>');
  my ($symb)=&get_symb($request);   my ($symb)=&get_symb($request);
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;    return; 
Line 4241  sub updateGradeByPage { Line 4495  sub updateGradeByPage {
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
  $map->map_finish());   $map->map_finish());
   
     my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.      my $studentTable=
  '<table border="0"><tr bgcolor="#e6ffff">'.   &Apache::loncommon::start_data_table().
  '<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.   &Apache::loncommon::start_data_table_header_row().
  '<td><b>&nbsp;Title&nbsp;</b></td>'.   '<th align="center">&nbsp;'.&mt('Prob.').'&nbsp;</th>'.
  '<td><b>&nbsp;Previous Score&nbsp;</b></td>'.   '<th>&nbsp;'.&mt('Title').'&nbsp;</th>'.
  '<td><b>&nbsp;New Score&nbsp;</b></td></tr>';   '<th>&nbsp;'.&mt('Previous Score').'&nbsp;</th>'.
    '<th>&nbsp;'.&mt('New Score').'&nbsp;</th>'.
    &Apache::loncommon::end_data_table_header_row();
   
     $iterator->next(); # skip the first BEGIN_MAP      $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"      my $curRes = $iterator->next(); # for "current resource"
Line 4259  sub updateGradeByPage { Line 4515  sub updateGradeByPage {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
     $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.      $studentTable.=
  (scalar(@{$parts}) == 1 ? '' : '<br />('.scalar(@{$parts}).'&nbsp;parts)').'</td>';   &Apache::loncommon::start_data_table_row().
    '<td align="center" valign="top" >'.$prob.
    (scalar(@{$parts}) == 1 ? '' 
                                           : '<br />('.&mt('[quant,_1,&nbsp;part]',scalar(@{$parts}))
    .')').'</td>';
     $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';      $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
   
     my %newrecord=();      my %newrecord=();
Line 4304  sub updateGradeByPage { Line 4564  sub updateGradeByPage {
  }   }
  my $display_part=&get_display_part($partid,$curRes->symb());   my $display_part=&get_display_part($partid,$curRes->symb());
  my $oldstatus = $env{'form.solved'.$question.'_'.$partid};   my $oldstatus = $env{'form.solved'.$question.'_'.$partid};
  $displayPts[0].='&nbsp;<b>Part:</b> '.$display_part.' = '.   $displayPts[0].='&nbsp;<b>'.&mt('Part').':</b> '.$display_part.' = '.
     (($oldstatus eq 'excused') ? 'excused' : $oldpts).      (($oldstatus eq 'excused') ? 'excused' : $oldpts).
     '&nbsp;<br />';      '&nbsp;<br />';
  $displayPts[1].='&nbsp;<b>Part:</b> '.$display_part.' = '.   $displayPts[1].='&nbsp;<b>'.&mt('Part').':</b> '.$display_part.' = '.
      (($score eq 'excused') ? 'excused' : $newpts).       (($score eq 'excused') ? 'excused' : $newpts).
     '&nbsp;<br />';      '&nbsp;<br />';
  $question++;   $question++;
Line 4347  sub updateGradeByPage { Line 4607  sub updateGradeByPage {
   
     $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.      $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
  '<td valign="top">'.$displayPts[1].'</td>'.   '<td valign="top">'.$displayPts[1].'</td>'.
  '</tr>';   &Apache::loncommon::end_data_table_row();
   
     $prob++;      $prob++;
  }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
   
     $studentTable.='</td></tr></table></td></tr></table>';      $studentTable.=&Apache::loncommon::end_data_table();
     $studentTable.=&show_grading_menu_form($env{'form.symb'});      $studentTable.=&show_grading_menu_form($env{'form.symb'});
     my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :      my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') :
   'The scores were changed for '.    &mt('The scores were changed for [quant,_1,problem].',
   $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));    $changeflag));
     $request->print($grademsg.$studentTable);      $request->print($grademsg.$studentTable);
   
     return '';      return '';
Line 4392  one of the predefined configurations for Line 4652  one of the predefined configurations for
 like.  like.
   
 Next each scanline is checked for any errors of either 'missing  Next each scanline is checked for any errors of either 'missing
 bubbles' (it's an error because it may have been missed scanned  bubbles' (it's an error because it may have been mis-scanned
 because too light bubbling), 'double bubble' (each bubble line should  because too light bubbling), 'double bubble' (each bubble line should
 have no more that one letter picked), invalid or duplicated CODE,  have no more that one letter picked), invalid or duplicated CODE,
 invalid student ID  invalid student ID
Line 4403  username:domain. Line 4663  username:domain.
   
 During the validation phase the instructor can choose to skip scanlines.   During the validation phase the instructor can choose to skip scanlines. 
   
 After the validation phase, there is now 3 bubble sheet files  After the validation phase, there are now 3 bubble sheet files
   
   scantron_original_filename (unmodified original file)    scantron_original_filename (unmodified original file)
   scantron_corrected_filename (file where the corrected information has replaced the original information)    scantron_corrected_filename (file where the corrected information has replaced the original information)
Line 4420  the homework problem. Line 4680  the homework problem.
   
 =over 4  =over 4
   
 =cut  
   
   
 =pod   
   
 =item defaultFormData  =item defaultFormData
   
Line 4436  the homework problem. Line 4693  the homework problem.
   
 sub defaultFormData {  sub defaultFormData {
     my ($symb)=@_;      my ($symb)=@_;
     return '      return '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
       <input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".  
      '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".       '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
      '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";       '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
 }  }
   
   
 =pod   =pod 
   
 =item getSequenceDropDown  =item getSequenceDropDown
Line 4470  sub getSequenceDropDown { Line 4727  sub getSequenceDropDown {
     return $result;      return $result;
 }  }
   
   my %bubble_lines_per_response;     # no. bubble lines for each response.
                                      # index is "symb.part_id"
   
   my %first_bubble_line;             # First bubble line no. for each bubble.
   
   my %subdivided_bubble_lines;       # no. bubble lines for optionresponse, 
                                      # matchresponse or rankresponse, where 
                                      # an individual response can have multiple 
                                      # lines
   
   my %responsetype_per_response;     # responsetype for each response
   
   # Save and restore the bubble lines array to the form env.
   
   
   sub save_bubble_lines {
       foreach my $line (keys(%bubble_lines_per_response)) {
    $env{"form.scantron.bubblelines.$line"}  = $bubble_lines_per_response{$line};
    $env{"form.scantron.first_bubble_line.$line"} =
       $first_bubble_line{$line};
           $env{"form.scantron.sub_bubblelines.$line"} = 
               $subdivided_bubble_lines{$line};
           $env{"form.scantron.responsetype.$line"} =
               $responsetype_per_response{$line};
       }
   }
   
   
   sub restore_bubble_lines {
       my $line = 0;
       %bubble_lines_per_response = ();
       while ($env{"form.scantron.bubblelines.$line"}) {
    my $value = $env{"form.scantron.bubblelines.$line"};
    $bubble_lines_per_response{$line} = $value;
    $first_bubble_line{$line}  =
       $env{"form.scantron.first_bubble_line.$line"};
           $subdivided_bubble_lines{$line} =
               $env{"form.scantron.sub_bubblelines.$line"};
           $responsetype_per_response{$line} =
               $env{"form.scantron.responsetype.$line"};
    $line++;
       }
   
   }
   
   #  Given the parsed scanline, get the response for 
   #  'answer' number n:
   
   sub get_response_bubbles {
       my ($parsed_line, $response)  = @_;
   
   
       my $bubble_line = $first_bubble_line{$response-1} +1;
       my $bubble_lines= $bubble_lines_per_response{$response-1};
       
       my $selected = "";
   
       for (my $bline = 0; $bline < $bubble_lines; $bline++) {
    $selected .= $$parsed_line{"scantron.$bubble_line.answer"}.":";
    $bubble_line++;
       }
       return $selected;
   }
   
 =pod   =pod 
   
Line 4482  sub getSequenceDropDown { Line 4802  sub getSequenceDropDown {
 sub scantron_filenames {  sub scantron_filenames {
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $getpropath = 1;
     my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,      my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
     &propath($cdom,$cname));                                         $getpropath);
     my @possiblenames;      my @possiblenames;
     foreach my $filename (sort(@files)) {      foreach my $filename (sort(@files)) {
  ($filename)=split(/&/,$filename);   ($filename)=split(/&/,$filename);
Line 4526  sub scantron_uploads { Line 4847  sub scantron_uploads {
 =cut  =cut
   
 sub scantron_scantab {  sub scantron_scantab {
     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');  
     my $result='<select name="scantron_format">'."\n";      my $result='<select name="scantron_format">'."\n";
     $result.='<option></option>'."\n";      $result.='<option></option>'."\n";
     foreach my $line (<$fh>) {      my @lines = &get_scantronformat_file();
  my ($name,$descrip)=split(/:/,$line);      if (@lines > 0) {
  if ($name =~ /^\#/) { next; }          foreach my $line (@lines) {
  $result.='<option value="'.$name.'">'.$descrip.'</option>'."\n";              next if (($line =~ /^\#/) || ($line eq ''));
       my ($name,$descrip)=split(/:/,$line);
       $result.='<option value="'.$name.'">'.$descrip.'</option>'."\n";
           }
     }      }
     $result.='</select>'."\n";      $result.='</select>'."\n";
   
     return $result;      return $result;
 }  }
   
   =pod
   
   =item get_scantronformat_file
   
     Returns an array containing lines from the scantron format file for
     the domain of the course.
   
     If a url for a custom.tab file is listed in domain's configuration.db, 
     lines are from this file.
   
     Otherwise, if a default.tab has been published in RES space by the 
     domainconfig user, lines are from this file.
   
     Otherwise, fall back to getting lines from the legacy file on the
     local server:  /home/httpd/lonTabs/default_scantronformat.tab    
   
   =cut
   
   sub get_scantronformat_file {
       my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
       my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$cdom);
       my $gottab = 0;
       my @lines;
       if (ref($domconfig{'scantron'}) eq 'HASH') {
           if ($domconfig{'scantron'}{'scantronformat'} ne '') {
               my $formatfile = &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.$domconfig{'scantron'}{'scantronformat'});
               if ($formatfile ne '-1') {
                   @lines = split("\n",$formatfile,-1);
                   $gottab = 1;
               }
           }
       }
       if (!$gottab) {
           my $confname = $cdom.'-domainconfig';
           my $default = $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cdom.'/'.$confname.'/default.tab';
           my $formatfile =  &Apache::lonnet::getfile($default);
           if ($formatfile ne '-1') {
               @lines = split("\n",$formatfile,-1);
               $gottab = 1;
           }
       }
       if (!$gottab) {
           my @domains = &Apache::lonnet::current_machine_domains();
           if (grep(/^\Q$cdom\E$/,@domains)) {
               my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
               @lines = <$fh>;
               close($fh);
           } else {
               my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab');
               @lines = <$fh>;
               close($fh);
           }
       }
       return @lines;
   }
   
 =pod   =pod 
   
 =item scantron_CODElist  =item scantron_CODElist
Line 4571  sub scantron_CODElist { Line 4949  sub scantron_CODElist {
 =cut  =cut
   
 sub scantron_CODEunique {  sub scantron_CODEunique {
     my $result='<span style="white-space: nowrap;">      my $result='<span class="LC_nobreak">
                  <label><input type="radio" name="scantron_CODEunique"                   <label><input type="radio" name="scantron_CODEunique"
                         value="yes" checked="checked" />'.&mt('Yes').' </label>                          value="yes" checked="checked" />'.&mt('Yes').' </label>
                 </span>                  </span>
                 <span style="white-space: nowrap;">                  <span class="LC_nobreak">
                  <label><input type="radio" name="scantron_CODEunique"                   <label><input type="radio" name="scantron_CODEunique"
                         value="no" />'.&mt('No').' </label>                          value="no" />'.&mt('No').' </label>
                 </span>';                  </span>';
Line 4612  sub scantron_selectphase { Line 4990  sub scantron_selectphase {
     my $CODE_unique=&scantron_CODEunique();      my $CODE_unique=&scantron_CODEunique();
     my $result;      my $result;
   
       $ssi_error = 0;
   
     # Chunk of form to prompt for a file to grade and how:      # Chunk of form to prompt for a file to grade and how:
   
     $result.= <<SCANTRONFORM;      $result.= '
     <table width="100%" border="0">      <br />
     <tr>      <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process">
      <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process">      <input type="hidden" name="command" value="scantron_warning" />
       <td bgcolor="#777777">      '.$default_form_data.'
        <input type="hidden" name="command" value="scantron_warning" />      '.&Apache::loncommon::start_data_table('LC_scantron_action').'
         $default_form_data         '.&Apache::loncommon::start_data_table_header_row().'
         <table width="100%" border="0">              <th colspan="2">
           <tr bgcolor="#e6ffff">                &nbsp;'.&mt('Specify file and which Folder/Sequence to grade').'
             <td colspan="2">              </th>
               &nbsp;<b>Specify file and which Folder/Sequence to grade</b>         '.&Apache::loncommon::end_data_table_header_row().'
             </td>         '.&Apache::loncommon::start_data_table_row().'
           </tr>              <td> '.&mt('Sequence to grade:').' </td><td> '.$sequence_selector.' </td>
           <tr bgcolor="#ffffe6">         '.&Apache::loncommon::end_data_table_row().'
             <td> Sequence to grade: </td><td> $sequence_selector </td>         '.&Apache::loncommon::start_data_table_row().'
           </tr>              <td> '.&mt('Filename of scoring office file:').' </td><td> '.$file_selector.' </td>
           <tr bgcolor="#ffffe6">         '.&Apache::loncommon::end_data_table_row().'
             <td> Filename of scoring office file: </td><td> $file_selector </td>         '.&Apache::loncommon::start_data_table_row().'
           </tr>              <td> '.&mt('Format of data file:').' </td><td> '.$format_selector.' </td>
           <tr bgcolor="#ffffe6">         '.&Apache::loncommon::end_data_table_row().'
             <td> Format of data file: </td><td> $format_selector </td>         '.&Apache::loncommon::start_data_table_row().'
           </tr>              <td> '.&mt('Saved CODEs to validate against:').' </td><td> '.$CODE_selector.' </td>
           <tr bgcolor="#ffffe6">         '.&Apache::loncommon::end_data_table_row().'
             <td> Saved CODEs to validate against: </td><td> $CODE_selector</td>         '.&Apache::loncommon::start_data_table_row().'
           </tr>              <td> '.&mt('Each CODE is only to be used once:').'</td><td> '.$CODE_unique.' </td>
           <tr bgcolor="#ffffe6">         '.&Apache::loncommon::end_data_table_row().'
             <td> Each CODE is only to be used once:</td><td> $CODE_unique </td>         '.&Apache::loncommon::start_data_table_row().'
           </tr>      <td> '.&mt('Options:').' </td>
           <tr bgcolor="#ffffe6">  
     <td> Options: </td>  
             <td>              <td>
        <label><input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only previously skipped records</label> <br />         <label><input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> '.&mt('Do only previously skipped records').'</label> <br />
                <label><input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove all existing corrections</label> <br />                 <label><input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> '.&mt('Remove all existing corrections').'</label> <br />
                <label><input type="checkbox" name="scantron_options_hidden" value="ignore_hidden"/> Skip hidden resources when grading</label>                 <label><input type="checkbox" name="scantron_options_hidden" value="ignore_hidden"/> '.&mt('Skip hidden resources when grading').'</label>
     </td>      </td>
           </tr>         '.&Apache::loncommon::end_data_table_row().'
           <tr bgcolor="#ffffe6">         '.&Apache::loncommon::start_data_table_row().'
             <td colspan="2">              <td colspan="2">
               <input type="submit" value="Grading: Validate Scantron Records" />                <input type="submit" value="'.&mt('Grading: Validate Scantron Records').'" />
             </td>              </td>
           </tr>         '.&Apache::loncommon::end_data_table_row().'
         </table>      '.&Apache::loncommon::end_data_table().'
        </td>      </form>
      </form>  ';
     </tr>  
 SCANTRONFORM  
         
     $r->print($result);      $r->print($result);
   
Line 4668  SCANTRONFORM Line 5044  SCANTRONFORM
   
  # Chunk of form to prompt for a scantron file upload.   # Chunk of form to prompt for a scantron file upload.
   
         $r->print(<<SCANTRONFORM);          $r->print('
     <tr>      <br />
       <td bgcolor="#777777">      '.&Apache::loncommon::start_data_table('LC_scantron_action').'
         <table width="100%" border="0">         '.&Apache::loncommon::start_data_table_header_row().'
           <tr bgcolor="#e6ffff">              <th>
             <td>                &nbsp;'.&mt('Specify a Scantron data file to upload.').'
               &nbsp;<b>Specify a Scantron data file to upload.</b>              </th>
             </td>         '.&Apache::loncommon::end_data_table_header_row().'
           </tr>         '.&Apache::loncommon::start_data_table_row().'
           <tr bgcolor="#ffffe6">  
             <td>              <td>
 SCANTRONFORM  ');
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData(&get_symb($r,1));
     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
     $r->print(<<UPLOAD);      $r->print('
               <script type="text/javascript" language="javascript">                <script type="text/javascript" language="javascript">
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
     alert("Please use the browse button to select a file from your local directory.");      alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
     return false;      return false;
  }   }
  formname.submit();   formname.submit();
     }      }
               </script>                </script>
   
               <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'>                <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
                 $default_form_data                  '.$default_form_data.'
                 <input name='courseid' type='hidden' value='$cnum' />                  <input name="courseid" type="hidden" value="'.$cnum.'" />
                 <input name='domainid' type='hidden' value='$cdom' />                  <input name="domainid" type="hidden" value="'.$cdom.'" />
                 <input name='command' value='scantronupload_save' type='hidden' />                  <input name="command" value="scantronupload_save" type="hidden" />
                 File to upload:<input type="file" name="upfile" size="50" />                  '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
                 <br />                  <br />
                 <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" />                  <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
               </form>                </form>
 UPLOAD  ');
   
         $r->print(<<SCANTRONFORM);          $r->print('
             </td>              </td>
           </tr>         '.&Apache::loncommon::end_data_table_row().'
         </table>         '.&Apache::loncommon::end_data_table().'
       </td>  ');
     </tr>  
 SCANTRONFORM  
     }      }
   
     # Chunk of the form that prompts to view a scoring office file,      # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.      # corrected file, skipped records in a file.
   
     $r->print(<<SCANTRONFORM);      $r->print('
     <tr>     <br />
       <form action='/adm/grades' name='scantron_download'>     <form action="/adm/grades" name="scantron_download">
         <td bgcolor="#777777">       '.$default_form_data.'
   $default_form_data       <input type="hidden" name="command" value="scantron_download" />
           <input type="hidden" name="command" value="scantron_download" />       '.&Apache::loncommon::start_data_table('LC_scantron_action').'
           <table width="100%" border="0">         '.&Apache::loncommon::start_data_table_header_row().'
             <tr bgcolor="#e6ffff">                <th>
               <td colspan="2">                  &nbsp;'.&mt('Download a scoring office file').'
                 &nbsp;<b>Download a scoring office file</b>                </th>
               </td>         '.&Apache::loncommon::end_data_table_header_row().'
             </tr>         '.&Apache::loncommon::start_data_table_row().'
             <tr bgcolor="#ffffe6">                <td> '.&mt('Filename of scoring office file: [_1]',$file_selector).' 
               <td> Filename of scoring office file: </td><td> $file_selector </td>                  <br />
             </tr>                  <input type="submit" value="'.&mt('Download: Show List of Associated Files').'" />
             <tr bgcolor="#ffffe6">         '.&Apache::loncommon::end_data_table_row().'
               <td colspan="2">       '.&Apache::loncommon::end_data_table().'
                 <input type="submit" value="Download: Show List of Associated Files" />     </form>
               </td>     <br />
             </tr>  ');
           </table>  
         </td>      &Apache::lonpickcode::code_list($r,2);
       </form>  
     </tr>      $r->print('<br /><form method="post" name="checkscantron">'.
 SCANTRONFORM               $default_form_data."\n".
                &Apache::loncommon::start_data_table('LC_scantron_action')."\n".
     $r->print(<<SCANTRONFORM);               &Apache::loncommon::start_data_table_header_row()."\n".
   </table>               '<th colspan="2">
 $grading_menu_button                &nbsp;'.&mt('Review scantron data and submissions for a previously graded folder/sequence')."\n".
 SCANTRONFORM               '</th>'."\n".
                 &Apache::loncommon::end_data_table_header_row()."\n".
     return                &Apache::loncommon::start_data_table_row()."\n".
                 '<td> '.&mt('Graded folder/sequence:').' </td>'."\n".
                 '<td> '.$sequence_selector.' </td>'.
                 &Apache::loncommon::end_data_table_row()."\n".
                 &Apache::loncommon::start_data_table_row()."\n".
                 '<td> '.&mt('Filename of scoring office file:').' </td>'."\n".
                 '<td> '.$file_selector.' </td>'."\n".
                 &Apache::loncommon::end_data_table_row()."\n".
                 &Apache::loncommon::start_data_table_row()."\n".
                 '<td> '.&mt('Format of data file:').' </td>'."\n".
                 '<td> '.$format_selector.' </td>'."\n".
                 &Apache::loncommon::end_data_table_row()."\n".
                 &Apache::loncommon::start_data_table_row()."\n".
                 '<td colspan="2">'."\n".
                 '<input type="hidden" name="command" value="checksubmissions" />'."\n".
                 '<input type="submit" value="'.&mt('Review Scantron Data and Submission Records').'" />'."\n".
                 '</td>'."\n".
                 &Apache::loncommon::end_data_table_row()."\n".
                 &Apache::loncommon::end_data_table()."\n".
                 '</form><br />');
       $r->print($grading_menu_button);
       return;
 }  }
   
 =pod  =pod
Line 4810  SCANTRONFORM Line 5204  SCANTRONFORM
   
 sub get_scantron_config {  sub get_scantron_config {
     my ($which) = @_;      my ($which) = @_;
     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');      my @lines = &get_scantronformat_file();
     my %config;      my %config;
     #FIXME probably should move to XML it has already gotten a bit much now      #FIXME probably should move to XML it has already gotten a bit much now
     foreach my $line (<$fh>) {      foreach my $line (@lines) {
  my ($name,$descrip)=split(/:/,$line);   my ($name,$descrip)=split(/:/,$line);
  if ($name ne $which ) { next; }   if ($name ne $which ) { next; }
  chomp($line);   chomp($line);
Line 4826  sub get_scantron_config { Line 5220  sub get_scantron_config {
  $config{'IDstart'}=$config[5];   $config{'IDstart'}=$config[5];
  $config{'IDlength'}=$config[6];   $config{'IDlength'}=$config[6];
  $config{'Qstart'}=$config[7];   $config{'Qstart'}=$config[7];
  $config{'Qlength'}=$config[8];    $config{'Qlength'}=$config[8];
  $config{'Qoff'}=$config[9];   $config{'Qoff'}=$config[9];
  $config{'Qon'}=$config[10];   $config{'Qon'}=$config[10];
  $config{'PaperID'}=$config[11];   $config{'PaperID'}=$config[11];
Line 4900  sub username_to_idmap { Line 5294  sub username_to_idmap {
                           - 'answer'                            - 'answer'
                                'response' - new answer or 'none' if blank                                 'response' - new answer or 'none' if blank
                                'question' - the bubble line to change                                 'question' - the bubble line to change
                                  'questionnum' - the question identifier,
                                                  may include subquestion. 
   
   Returns:    Returns:
     $line - the modified scanline      $line - the modified scanline
Line 4912  sub username_to_idmap { Line 5308  sub username_to_idmap {
   
 sub scantron_fixup_scanline {  sub scantron_fixup_scanline {
     my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_;      my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_;
   
     if ($field eq 'ID') {      if ($field eq 'ID') {
  if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) {   if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) {
     return ($line,1,'New value too large');      return ($line,1,'New value too large');
Line 4949  sub scantron_fixup_scanline { Line 5344  sub scantron_fixup_scanline {
  my $answer=${off}x$length;   my $answer=${off}x$length;
  if ($args->{'response'} eq 'none') {   if ($args->{'response'} eq 'none') {
     &scan_data($scan_data,      &scan_data($scan_data,
        "$whichline.no_bubble.".$args->{'question'},'1');         "$whichline.no_bubble.".$args->{'questionnum'},'1');
  } else {   } else {
     if ($on eq 'letter') {      if ($on eq 'letter') {
  my @alphabet=('A'..'Z');   my @alphabet=('A'..'Z');
Line 4961  sub scantron_fixup_scanline { Line 5356  sub scantron_fixup_scanline {
  substr($answer,$args->{'response'},1)=$on;   substr($answer,$args->{'response'},1)=$on;
     }      }
     &scan_data($scan_data,      &scan_data($scan_data,
        "$whichline.no_bubble.".$args->{'question'},undef,'1');         "$whichline.no_bubble.".$args->{'questionnum'},undef,'1');
  }   }
  my $where=$length*($args->{'question'}-1)+$scantron_config->{'Qstart'};   my $where=$length*($args->{'question'}-1)+$scantron_config->{'Qstart'};
  substr($line,$where-1,$length)=$answer;   substr($line,$where-1,$length)=$answer;
Line 4998  sub scan_data { Line 5393  sub scan_data {
     return $scan_data->{$filename.'_'.$key};      return $scan_data->{$filename.'_'.$key};
 }  }
   
   # ----- These first few routines are general use routines.----
   
   # Return the number of occurences of a pattern in a string.
   
   sub occurence_count {
       my ($string, $pattern) = @_;
   
       my @matches = ($string =~ /$pattern/g);
   
       return scalar(@matches);
   }
   
   
   # Take a string known to have digits and convert all the
   # digits into letters in the range J,A..I.
   
   sub digits_to_letters {
       my ($input) = @_;
   
       my @alphabet = ('J', 'A'..'I');
   
       my @input    = split(//, $input);
       my $output ='';
       for (my $i = 0; $i < scalar(@input); $i++) {
    if ($input[$i] =~ /\d/) {
       $output .= $alphabet[$input[$i]];
    } else {
       $output .= $input[$i];
    }
       }
       return $output;
   }
   
 =pod   =pod 
   
 =item scantron_parse_scanline  =item scantron_parse_scanline
Line 5031  sub scan_data { Line 5459  sub scan_data {
   
      if just_header was not true these key may also exist       if just_header was not true these key may also exist
   
        missingerror - a list of bubbled line numbers that had a blank bubble         missingerror - a list of bubble ranges that are considered to be answers
                       that is considered an error (if the operator had already                        to a single question that don't have any bubbles filled in.
                       okayed a blank bubble line as really being blank then                        Of the form questionnumber:firstbubblenumber:count.
                       that bubble line number won't appear here.         doubleerror  - a list of bubble ranges that are considered to be answers
        doubleerror  - a list of bubbled line numbers that had more than one                        to a single question that have more than one bubble filled in.
                       bubble filled in and has not been corrected by the                        Of the form questionnumber::firstbubblenumber:count
                       operator     
                   In the above, count is the number of bubble responses in the
                   input line needed to represent the possible answers to the question.
                   e.g. a radioresponse with 15 choices in an answer sheet with 10 choices
                   per line would have count = 2.
   
        maxquest     - the number of the last bubble line that was parsed         maxquest     - the number of the last bubble line that was parsed
   
        (<number> starts at 1)         (<number> starts at 1)
Line 5052  sub scan_data { Line 5485  sub scan_data {
   
 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)=@_;
   
     my %record;      my %record;
     my $questions=substr($line,$$scantron_config{'Qstart'}-1);  # Answers      my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};
       my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff      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')) {
Line 5088  sub scantron_parse_scanline { Line 5523  sub scantron_parse_scanline {
   
     my @alphabet=('A'..'Z');      my @alphabet=('A'..'Z');
     my $questnum=0;      my $questnum=0;
     while ($questions) {      my $ansnum  =1; # Multiple 'answer lines'/question.
  $questnum++;  
  my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});      chomp($questions); # Get rid of any trailing \n.
  substr($questions,0,$$scantron_config{'Qlength'})='';      $questions =~ s/\r$//;      # Get rid of trailing \r too (MAC or Win uploads).
  if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }      while (length($questions)) {
  if ($$scantron_config{'Qon'} eq 'letter') {   my $answers_needed = $bubble_lines_per_response{$questnum};
     if ($currentquest eq '?'          my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)
  || $currentquest eq '*') {                               || 1;
  push(@{$record{'scantron.doubleerror'}},$questnum);          $questnum++;
  $record{"scantron.$questnum.answer"}='';          my $quest_id = $questnum;
     } elsif (!defined($currentquest)          my $currentquest = substr($questions,0,$answer_length);
      || $currentquest eq $$scantron_config{'Qoff'}          $questions       = substr($questions,$answer_length);
      || $currentquest !~ /^[A-Z]$/) {          if (length($currentquest) < $answer_length) { next; }
  $record{"scantron.$questnum.answer"}='';  
  if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {          if ($subdivided_bubble_lines{$questnum-1} =~ /,/) {
     push(@{$record{"scantron.missingerror"}},$questnum);              my $subquestnum = 1;
  }              my $subquestions = $currentquest;
     } else {              my @subanswers_needed = 
  $record{"scantron.$questnum.answer"}=$currentquest;                  split(/,/,$subdivided_bubble_lines{$questnum-1});  
     }              foreach my $subans (@subanswers_needed) {
  } elsif ($$scantron_config{'Qon'} eq 'number') {                  my $subans_length =
     if ($currentquest eq '?'                      ($$scantron_config{'Qlength'} * $subans)  || 1;
  || $currentquest eq '*') {                  my $currsubquest = substr($subquestions,0,$subans_length);
  push(@{$record{'scantron.doubleerror'}},$questnum);                  $subquestions   = substr($subquestions,$subans_length);
  $record{"scantron.$questnum.answer"}='';                  $quest_id = "$questnum.$subquestnum";
     } elsif (!defined($currentquest)                  if (($$scantron_config{'Qon'} eq 'letter') ||
      || $currentquest eq $$scantron_config{'Qoff'}                       ($$scantron_config{'Qon'} eq 'number')) {
      || $currentquest !~ /^\d$/) {                      $ansnum = &scantron_validator_lettnum($ansnum, 
  $record{"scantron.$questnum.answer"}='';                          $questnum,$quest_id,$subans,$currsubquest,$whichline,
  if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {                          \@alphabet,\%record,$scantron_config,$scan_data);
     push(@{$record{"scantron.missingerror"}},$questnum);                  } else {
  }                      $ansnum = &scantron_validator_positional($ansnum,
     } else {                          $questnum,$quest_id,$subans,$currsubquest,$whichline,                        \@alphabet,\%record,$scantron_config,$scan_data);
  # wrap zero back to J                  }
  if ($currentquest eq '0') {                  $subquestnum ++;
     $record{"scantron.$questnum.answer"}=              }
  $alphabet[9];          } else {
  } else {              if (($$scantron_config{'Qon'} eq 'letter') ||
     $record{"scantron.$questnum.answer"}=                  ($$scantron_config{'Qon'} eq 'number')) {
  $alphabet[$currentquest-1];                  $ansnum = &scantron_validator_lettnum($ansnum,$questnum,
  }                      $quest_id,$answers_needed,$currentquest,$whichline,
     }                      \@alphabet,\%record,$scantron_config,$scan_data);
  } else {              } else {
     my @array=split($$scantron_config{'Qon'},$currentquest,-1);                  $ansnum = &scantron_validator_positional($ansnum,$questnum,
     if (length($array[0]) eq $$scantron_config{'Qlength'}) {                      $quest_id,$answers_needed,$currentquest,$whichline,
  $record{"scantron.$questnum.answer"}='';                      \@alphabet,\%record,$scantron_config,$scan_data);
  if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {              }
     push(@{$record{"scantron.missingerror"}},$questnum);          }
  }  
     } else {  
  $record{"scantron.$questnum.answer"}=  
     $alphabet[length($array[0])];  
     }  
     if (scalar(@array) gt 2) {  
  push(@{$record{'scantron.doubleerror'}},$questnum);  
  my @ans=@array;  
  my $i=length($ans[0]);shift(@ans);  
  while ($#ans) {  
     $i+=length($ans[0])+1;  
     $record{"scantron.$questnum.answer"}.=$alphabet[$i];  
     shift(@ans);  
  }  
     }  
  }  
     }      }
     $record{'scantron.maxquest'}=$questnum;      $record{'scantron.maxquest'}=$questnum;
     return \%record;      return \%record;
 }  }
   
   sub scantron_validator_lettnum {
       my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,$whichline,
           $alphabet,$record,$scantron_config,$scan_data) = @_;
   
       # Qon 'letter' implies for each slot in currquest we have:
       #    ? or * for doubles, a letter in A-Z for a bubble, and
       #    about anything else (esp. a value of Qoff) for missing
       #    bubbles.
       #
       # Qon 'number' implies each slot gives a digit that indexes the
       #    bubbles filled, or Qoff, or a non-number for unbubbled lines,
       #    and * or ? for double bubbles on a single line.
       #
   
       my $matchon;
       if ($$scantron_config{'Qon'} eq 'letter') {
           $matchon = '[A-Z]';
       } elsif ($$scantron_config{'Qon'} eq 'number') {
           $matchon = '\d';
       }
       my $occurrences = 0;
       if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||
           ($responsetype_per_response{$questnum-1} eq 'formularesponse') ||
           ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||
           ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||
           ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||
           ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {
           my @singlelines = split('',$currquest);
           foreach my $entry (@singlelines) {
               $occurrences = &occurence_count($entry,$matchon);
               if ($occurrences > 1) {
                   last;
               }
           } 
       } else {
           $occurrences = &occurence_count($currquest,$matchon); 
       }
       if (($currquest =~ /\?/ || $currquest =~ /\*/) || ($occurrences > 1)) {
           push(@{$record->{'scantron.doubleerror'}},$quest_id);
           for (my $ans=0; $ans<$answers_needed; $ans++) {
               my $bubble = substr($currquest,$ans,1);
               if ($bubble =~ /$matchon/ ) {
                   if ($$scantron_config{'Qon'} eq 'number') {
                       if ($bubble == 0) {
                           $bubble = 10; 
                       }
                       $record->{"scantron.$ansnum.answer"} = 
                           $alphabet->[$bubble-1];
                   } else {
                       $record->{"scantron.$ansnum.answer"} = $bubble;
                   }
               } else {
                   $record->{"scantron.$ansnum.answer"}='';
               }
               $ansnum++;
           }
       } elsif (!defined($currquest)
               || (&occurence_count($currquest, $$scantron_config{'Qoff'}) == length($currquest))
               || (&occurence_count($currquest,$matchon) == 0)) {
           for (my $ans=0; $ans<$answers_needed; $ans++ ) {
               $record->{"scantron.$ansnum.answer"}='';
               $ansnum++;
           }
           if (!&scan_data($scan_data,"$whichline.no_bubble.$quest_id")) {
               push(@{$record->{'scantron.missingerror'}},$quest_id);
           }
       } else {
           if ($$scantron_config{'Qon'} eq 'number') {
               $currquest = &digits_to_letters($currquest);            
           }
           for (my $ans=0; $ans<$answers_needed; $ans++) {
               my $bubble = substr($currquest,$ans,1);
               $record->{"scantron.$ansnum.answer"} = $bubble;
               $ansnum++;
           }
       }
       return $ansnum;
   }
   
   sub scantron_validator_positional {
       my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,
           $whichline,$alphabet,$record,$scantron_config,$scan_data) = @_;
   
       # Otherwise there's a positional notation;
       # each bubble line requires Qlength items, and there are filled in
       # bubbles for each case where there 'Qon' characters.
       #
   
       my @array=split($$scantron_config{'Qon'},$currquest,-1);
   
       # If the split only gives us one element.. the full length of the
       # answer string, no bubbles are filled in:
   
       if ($answers_needed eq '') {
           return;
       }
   
       if (length($array[0]) eq $$scantron_config{'Qlength'}*$answers_needed) {
           for (my $ans=0; $ans<$answers_needed; $ans++ ) {
               $record->{"scantron.$ansnum.answer"}='';
               $ansnum++;
           }
           if (!&scan_data($scan_data,"$whichline.no_bubble.$quest_id")) {
               push(@{$record->{"scantron.missingerror"}},$quest_id);
           }
       } elsif (scalar(@array) == 2) {
           my $location = length($array[0]);
           my $line_num = int($location / $$scantron_config{'Qlength'});
           my $bubble   = $alphabet->[$location % $$scantron_config{'Qlength'}];
           for (my $ans=0; $ans<$answers_needed; $ans++) {
               if ($ans eq $line_num) {
                   $record->{"scantron.$ansnum.answer"} = $bubble;
               } else {
                   $record->{"scantron.$ansnum.answer"} = ' ';
               }
               $ansnum++;
            }
       } else {
           #  If there's more than one instance of a bubble character
           #  That's a double bubble; with positional notation we can
           #  record all the bubbles filled in as well as the
           #  fact this response consists of multiple bubbles.
           #
           if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||
               ($responsetype_per_response{$questnum-1} eq 'formularesponse') ||
               ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||
               ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||
               ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||
               ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {
               my $doubleerror = 0;
               while (($currquest >= $$scantron_config{'Qlength'}) && 
                      (!$doubleerror)) {
                  my $currline = substr($currquest,0,$$scantron_config{'Qlength'});
                  $currquest = substr($currquest,$$scantron_config{'Qlength'});
                  my @currarray = split($$scantron_config{'Qon'},$currline,-1);
                  if (length(@currarray) > 2) {
                      $doubleerror = 1;
                  } 
               }
               if ($doubleerror) {
                   push(@{$record->{'scantron.doubleerror'}},$quest_id);
               }
           } else {
               push(@{$record->{'scantron.doubleerror'}},$quest_id);
           }
           my $item = $ansnum;
           for (my $ans=0; $ans<$answers_needed; $ans++) {
               $record->{"scantron.$item.answer"} = '';
               $item ++;
           }
   
           my @ans=@array;
           my $i=0;
           my $increment = 0;
           while ($#ans) {
               $i+=length($ans[0]) + $increment;
               my $line   = int($i/$$scantron_config{'Qlength'} + $ansnum);
               my $bubble = $i%$$scantron_config{'Qlength'};
               $record->{"scantron.$line.answer"}.=$alphabet->[$bubble];
               shift(@ans);
               $increment = 1;
           }
           $ansnum += $answers_needed;
       }
       return $ansnum;
   }
   
 =pod  =pod
   
 =item scantron_add_delay  =item scantron_add_delay
Line 5294  sub scantron_process_corrections { Line 5880  sub scantron_process_corrections {
  &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,   &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,
  $which,'answer',   $which,'answer',
  { 'question'=>$question,   { 'question'=>$question,
        'response'=>$env{"form.scantron_correct_Q_$question"}});           'response'=>$env{"form.scantron_correct_Q_$question"},
                                      'questionnum'=>$env{"form.scantron_questionnum_Q_$question"}});
     if ($err) { last; }      if ($err) { last; }
  }   }
     }      }
Line 5399  sub remember_current_skipped { Line 5986  sub remember_current_skipped {
 sub check_for_error {  sub check_for_error {
     my ($r,$result)=@_;      my ($r,$result)=@_;
     if ($result ne 'ok' && $result ne 'not_found' ) {      if ($result ne 'ok' && $result ne 'not_found' ) {
  $r->print("An error occurred ($result) when trying to Remove the existing corrections.");   $r->print(&mt("An error occurred ([_1]) when trying to remove the existing corrections.",$result));
     }      }
 }  }
   
Line 5423  sub scantron_warning_screen { Line 6010  sub scantron_warning_screen {
  $CODElist=$env{'form.scantron_CODElist'};   $CODElist=$env{'form.scantron_CODElist'};
  if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">None</span>'; }   if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">None</span>'; }
  $CODElist=   $CODElist=
     '<tr><td><b>List of CODES to validate against:</b></td><td><tt>'.      '<tr><td><b>'.&mt('List of CODES to validate against:').'</b></td><td><tt>'.
     $env{'form.scantron_CODElist'}.'</tt></td></tr>';      $env{'form.scantron_CODElist'}.'</tt></td></tr>';
     }      }
     return (<<STUFF);      return ('
 <p>  <p>
 <span class="LC_warning">Please double check the information  <span class="LC_warning">
                  below before clicking on '$button_text'</span>  '.&mt('Please double check the information below before clicking on \'[_1]\'',&mt($button_text)).'</span>
 </p>  </p>
 <table>  <table>
 <tr><td><b>Sequence to be Graded:</b></td><td>$title</td></tr>  <tr><td><b>'.&mt('Sequence to be Graded:').'</b></td><td>'.$title.'</td></tr>
 <tr><td><b>Data File that will be used:</b></td><td><tt>$env{'form.scantron_selectfile'}</tt></td></tr>  <tr><td><b>'.&mt('Data File that will be used:').'</b></td><td><tt>'.$env{'form.scantron_selectfile'}.'</tt></td></tr>
 $CODElist  '.$CODElist.'
 </table>  </table>
 <br />  <br />
 <p> If this information is correct, please click on '$button_text'.</p>  <p> '.&mt('If this information is correct, please click on \'[_1]\'.',&mt($button_text)).'</p>
 <p> If something is incorrect, please click the 'Grading Menu' button to start over.</p>  <p> '.&mt('If something is incorrect, please click the \'Grading Menu\' button to start over.').'</p>
   
 <br />  <br />
 STUFF  ');
 }  }
   
 =pod  =pod
Line 5462  sub scantron_do_warning { Line 6049  sub scantron_do_warning {
     if ( $env{'form.selectpage'} eq '' ||      if ( $env{'form.selectpage'} eq '' ||
  $env{'form.scantron_selectfile'} eq '' ||   $env{'form.scantron_selectfile'} eq '' ||
  $env{'form.scantron_format'} eq '' ) {   $env{'form.scantron_format'} eq '' ) {
  $r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>");   $r->print("<p>".&mt('You have forgetten to specify some information. Please go Back and try again.')."</p>");
  if ( $env{'form.selectpage'} eq '') {   if ( $env{'form.selectpage'} eq '') {
     $r->print('<p><span class="LC_error">You have not selected a Sequence to grade</span></p>');      $r->print('<p><span class="LC_error">'.&mt('You have not selected a Sequence to grade').'</span></p>');
  }    } 
  if ( $env{'form.scantron_selectfile'} eq '') {   if ( $env{'form.scantron_selectfile'} eq '') {
     $r->print('<p><span class="LC_error">You have not selected a file that contains the student\'s response data.</span></p>');      $r->print('<p><span class="LC_error">'.&mt('You have not selected a file that contains the student\'s response data.').'</span></p>');
  }    } 
  if ( $env{'form.scantron_format'} eq '') {   if ( $env{'form.scantron_format'} eq '') {
     $r->print('<p><span class="LC_error">You have not selected a the format of the student\'s response data.</span></p>');      $r->print('<p><span class="LC_error">'.&mt('You have not selected a the format of the student\'s response data.').'</span></p>');
  }    } 
     } else {      } else {
  my $warning=&scantron_warning_screen('Grading: Validate Records');   my $warning=&scantron_warning_screen('Grading: Validate Records');
  $r->print(<<STUFF);   $r->print('
 $warning  '.$warning.'
 <input type="submit" name="submit" value="Grading: Validate Records" />  <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />
 <input type="hidden" name="command" value="scantron_validate" />  <input type="hidden" name="command" value="scantron_validate" />
 STUFF  ');
     }      }
     $r->print("</form><br />".&show_grading_menu_form($symb));      $r->print("</form><br />".&show_grading_menu_form($symb));
     return '';      return '';
Line 5506  sub scantron_form_start { Line 6093  sub scantron_form_start {
   <input type="hidden" name="scantron_options_ignore" value="$env{'form.scantron_options_ignore'}" />    <input type="hidden" name="scantron_options_ignore" value="$env{'form.scantron_options_ignore'}" />
   <input type="hidden" name="scantron_options_hidden" value="$env{'form.scantron_options_hidden'}" />    <input type="hidden" name="scantron_options_hidden" value="$env{'form.scantron_options_hidden'}" />
 SCANTRONFORM  SCANTRONFORM
   
     my $line = 0;
       while (defined($env{"form.scantron.bubblelines.$line"})) {
          my $chunk =
      '<input type="hidden" name="scantron.bubblelines.'.$line.'" value="'.$env{"form.scantron.bubblelines.$line"}.'" />'."\n";
          $chunk .=
      '<input type="hidden" name="scantron.first_bubble_line.'.$line.'" value="'.$env{"form.scantron.first_bubble_line.$line"}.'" />'."\n";
          $chunk .= 
              '<input type="hidden" name="scantron.sub_bubblelines.'.$line.'" value="'.$env{"form.scantron.sub_bubblelines.$line"}.'" />'."\n";
          $chunk .=
              '<input type="hidden" name="scantron.responsetype.'.$line.'" value="'.$env{"form.scantron.responsetype.$line"}.'" />'."\n";
          $result .= $chunk;
          $line++;
      }
     return $result;      return $result;
 }  }
   
Line 5547  sub scantron_validate_file { Line 6148  sub scantron_validate_file {
     if ($env{'form.scantron_corrections'}) {      if ($env{'form.scantron_corrections'}) {
  &scantron_process_corrections($r);   &scantron_process_corrections($r);
     }      }
     $r->print("<p>Gathering necessary info.</p>");$r->rflush();      $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $max_bubble=&scantron_get_maxbubble();      my $max_bubble=&scantron_get_maxbubble();
Line 5564  sub scantron_validate_file { Line 6165  sub scantron_validate_file {
     }      }
     my $currentphase=$env{'form.validatepass'};      my $currentphase=$env{'form.validatepass'};
   
   
     my $stop=0;      my $stop=0;
     while (!$stop && $currentphase < scalar(@validate_phases)) {      while (!$stop && $currentphase < scalar(@validate_phases)) {
  $r->print("<p> Validating ".$validate_phases[$currentphase]."</p>");   $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];
  {   {
Line 5576  sub scantron_validate_file { Line 6178  sub scantron_validate_file {
     }      }
     if (!$stop) {      if (!$stop) {
  my $warning=&scantron_warning_screen('Start Grading');   my $warning=&scantron_warning_screen('Start Grading');
  $r->print(<<STUFF);   $r->print(&mt('Validation process complete.').'<br />'.
 Validation process complete.<br />                    $warning.
 $warning                    &mt('Perform verification for each student after storage of submissions?').
 <input type="submit" name="submit" value="Start Grading" />                    '&nbsp;<span class="LC_nobreak"><label>'.
 <input type="hidden" name="command" value="scantron_process" />                    '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.
 STUFF                    ('&nbsp;'x3).'<label>'.
                     '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').
                     '</label></span><br />'.
                     &mt('Grading will take longer if you use verification.').'<br />'.
                     &mt("Alternatively, the 'Review scantron data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.
                     '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.
                     '<input type="hidden" name="command" value="scantron_process" />'."\n");
     } else {      } else {
  $r->print('<input type="hidden" name="command" value="scantron_validate" />');   $r->print('<input type="hidden" name="command" value="scantron_validate" />');
  $r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />");   $r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />");
     }      }
     if ($stop) {      if ($stop) {
  if ($validate_phases[$currentphase] eq 'sequence') {   if ($validate_phases[$currentphase] eq 'sequence') {
     $r->print('<input type="submit" name="submit" value="Ignore -> " />');      $r->print('<input type="submit" name="submit" value="'.&mt('Ignore').' &rarr; " />');
     $r->print(' this error <br />');      $r->print(' '.&mt('this error').' <br />');
   
     $r->print(" <p>Or click the 'Grading Menu' button to start over.</p>");      $r->print(" <p>".&mt("Or click the 'Grading Menu' button to start over.")."</p>");
  } else {   } else {
     $r->print('<input type="submit" name="submit" value="Continue ->" />');              if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {
     $r->print(' using corrected info <br />');          $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue').' &rarr;" onclick="javascript:verify_bubble_radio(this.form)" />');
     $r->print("<input type='submit' value='Skip' name='scantron_skip_record' />");              } else {
     $r->print(" this scanline saving it for later.");                  $r->print('<input type="submit" name="submit" value="'.&mt('Continue').' &rarr;" />');
               }
       $r->print(' '.&mt('using corrected info').' <br />');
       $r->print("<input type='submit' value='".&mt("Skip")."' name='scantron_skip_record' />");
       $r->print(" ".&mt("this scanline saving it for later."));
  }   }
     }      }
     $r->print(" </form><br />".&show_grading_menu_form($symb));      $r->print(" </form><br />".&show_grading_menu_form($symb));
Line 5658  sub scantron_remove_scan_data { Line 6269  sub scantron_remove_scan_data {
     }      }
     my $result;      my $result;
     if (@todelete) {      if (@todelete) {
  $result=&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname);   $result = &Apache::lonnet::del('nohist_scantrondata',
          \@todelete,$cdom,$cname);
       } else {
    $result = 'ok';
     }      }
     return $result;      return $result;
 }  }
Line 5968  sub scantron_validate_sequence { Line 6582  sub scantron_validate_sequence {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
 =pod  
   
 =item scantron_validate_ID  
   
    Validates all scanlines in the selected file to not have any  
    invalid or underspecified student IDs  
   
 =cut  
   
 sub scantron_validate_ID {  sub scantron_validate_ID {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
Line 5987  sub scantron_validate_ID { Line 6594  sub scantron_validate_ID {
     #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();
       
       &scantron_get_maxbubble(); # parse needs the bubble_lines.. array.
   
     my %found=('ids'=>{},'usernames'=>{});      my %found=('ids'=>{},'usernames'=>{});
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
Line 6039  sub scantron_validate_ID { Line 6648  sub scantron_validate_ID {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
 =pod  
   
 =item scantron_get_correction  
   
    Builds the interface screen to interact with the operator to fix a  
    specific error condition in a specific scanline  
   
  Arguments:  
     $r           - Apache request object  
     $i           - number of the current scanline  
     $scan_record - hash ref as returned from &scantron_parse_scanline()  
     $scan_config - hash ref as returned from &get_scantron_config()  
     $line        - full contents of the current scanline  
     $error       - error condition, valid values are  
                    'incorrectCODE', 'duplicateCODE',  
                    'doublebubble', 'missingbubble',  
                    'duplicateID', 'incorrectID'  
     $arg         - extra information needed  
        For errors:  
          - duplicateID   - paper number that this studentID was seen before on  
          - duplicateCODE - array ref of the paper numbers this CODE was  
                            seen on before  
          - incorrectCODE - current incorrect CODE   
          - doublebubble  - array ref of the bubble lines that have double  
                            bubble errors  
          - missingbubble - array ref of the bubble lines that have missing  
                            bubble errors  
   
 =cut  
   
 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)=@_;
   #FIXME in the case of a duplicated ID the previous line, probably need
 #FIXME in the case of a duplicated ID the previous line, probaly 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
   
     $r->print("<p><b>An error was detected ($error)</b>");  
     if ( $$scan_record{'scantron.PaperID'} =~ /\S/) {      if ( $$scan_record{'scantron.PaperID'} =~ /\S/) {
  $r->print(" for PaperID <tt>".   $r->print("<p>".&mt("<b>An error was detected ($error)</b>".
   $$scan_record{'scantron.PaperID'}."</tt> \n");      " for PaperID <tt>[_1]</tt>",
       $$scan_record{'scantron.PaperID'})."</p> \n");
     } else {      } else {
  $r->print(" in scanline $i <pre>".   $r->print("<p>".&mt("<b>An error was detected ($error)</b>".
   $line."</pre> \n");      " in scanline [_1] <pre>[_2]</pre>",
     }      $i,$line)."</p> \n");
     my $message="<p>The ID on the form is  <tt>".      }
  $$scan_record{'scantron.ID'}."</tt><br />\n".      my $message="<p>".&mt("The ID on the form is  <tt>[_1]</tt><br />".
  "The name on the paper is ".    "The name on the paper is [_2],[_3]",
  $$scan_record{'scantron.LastName'}.",".    $$scan_record{'scantron.ID'},
  $$scan_record{'scantron.FirstName'}."</p>";    $$scan_record{'scantron.LastName'},
     $$scan_record{'scantron.FirstName'})."</p>";
   
     $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n");      $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n");
     $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n");      $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n");
                              # Array populated for doublebubble or
       my @lines_to_correct;  # missingbubble errors to build javascript
                              # to validate radio button checking   
   
     if ($error =~ /ID$/) {      if ($error =~ /ID$/) {
  if ($error eq 'incorrectID') {   if ($error eq 'incorrectID') {
     $r->print("The encoded ID is not in the classlist</p>\n");      $r->print("<p>".&mt("The encoded ID is not in the classlist").
         "</p>\n");
  } elsif ($error eq 'duplicateID') {   } elsif ($error eq 'duplicateID') {
     $r->print("The encoded ID has also been used by a previous paper $arg</p>\n");      $r->print("<p>".&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."</p>\n");
  }   }
  $r->print($message);   $r->print($message);
  $r->print("<p>How should I handle this? <br /> \n");   $r->print("<p>".&mt("How should I handle this?")." <br /> \n");
  $r->print("\n<ul><li> ");   $r->print("\n<ul><li> ");
  #FIXME it would be nice if this sent back the user ID and   #FIXME it would be nice if this sent back the user ID and
  #could do partial userID matches   #could do partial userID matches
Line 6112  sub scantron_get_correction { Line 6697  sub scantron_get_correction {
  $r->print('</li>');   $r->print('</li>');
     } elsif ($error =~ /CODE$/) {      } elsif ($error =~ /CODE$/) {
  if ($error eq 'incorrectCODE') {   if ($error eq 'incorrectCODE') {
     $r->print("</p><p>The encoded CODE is not in the list of possible CODEs</p>\n");      $r->print("<p>".&mt("The encoded CODE is not in the list of possible CODEs.")."</p>\n");
  } elsif ($error eq 'duplicateCODE') {   } elsif ($error eq 'duplicateCODE') {
     $r->print("</p><p>The encoded CODE has also been used by a previous paper ".join(', ',@{$arg}).", and CODEs are supposed to be unique</p>\n");      $r->print("<p>".&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."</p>\n");
  }   }
  $r->print("<p>The CODE on the form is  <tt>'".   $r->print("<p>".&mt("The CODE on the form is  <tt>'[_1]'</tt>",
   $$scan_record{'scantron.CODE'}."'</tt><br />\n");      $$scan_record{'scantron.CODE'})."<br />\n");
  $r->print($message);   $r->print($message);
  $r->print("<p>How should I handle this? <br /> \n");   $r->print("<p>".&mt("How should I handle this?")." <br /> \n");
  $r->print("\n<br /> ");   $r->print("\n<br /> ");
  my $i=0;   my $i=0;
  if ($error eq 'incorrectCODE'    if ($error eq 'incorrectCODE' 
Line 6129  sub scantron_get_correction { Line 6714  sub scantron_get_correction {
  foreach my $testcode (@{$closest}) {   foreach my $testcode (@{$closest}) {
     my $checked='';      my $checked='';
     if (!$i) { $checked=' checked="checked" '; }      if (!$i) { $checked=' checked="checked" '; }
     $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> Use the similar CODE <b><tt>".$testcode."</tt></b> instead.</label><input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />");      $r->print("
      <label>
          <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked />
          ".&mt("Use the similar CODE [_1] instead.",
       "<b><tt>".$testcode."</tt></b>")."
       </label>
       <input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />");
     $r->print("\n<br />");      $r->print("\n<br />");
     $i++;      $i++;
  }   }
Line 6137  sub scantron_get_correction { Line 6728  sub scantron_get_correction {
  }   }
  if ($$scan_record{'scantron.CODE'}=~/\S/ ) {   if ($$scan_record{'scantron.CODE'}=~/\S/ ) {
     my $checked; if (!$i) { $checked=' checked="checked" '; }      my $checked; if (!$i) { $checked=' checked="checked" '; }
     $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> Use the CODE <b><tt>".$$scan_record{'scantron.CODE'}."</tt></b> that is was on the paper, ignoring the error.</label>");      $r->print("
       <label>
           <input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked />
          ".&mt("Use the CODE [_1] that is was on the paper, ignoring the error.",
        "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."
       </label>");
     $r->print("\n<br />");      $r->print("\n<br />");
  }   }
   
Line 6159  ENDSCRIPT Line 6755  ENDSCRIPT
    "&curCODE=".&escape($$scan_record{'scantron.CODE'}).     "&curCODE=".&escape($$scan_record{'scantron.CODE'}).
    "&scantron_selectfile=".&escape($env{'form.scantron_selectfile'});     "&scantron_selectfile=".&escape($env{'form.scantron_selectfile'});
  if ($env{'form.scantron_CODElist'} =~ /\S/) {    if ($env{'form.scantron_CODElist'} =~ /\S/) { 
     $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it.</label> Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />");      $r->print("
       <label>
          <input type='radio' name='scantron_CODE_resolution' value='use_found' />
          ".&mt("[_1]Select[_2] a CODE from the list of all CODEs and use it.",
        "<a target='_blank' href='$href'>","</a>")."
       </label> 
       ".&mt("Selected CODE is [_1]","<input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />"));
     $r->print("\n<br />");      $r->print("\n<br />");
  }   }
  $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_typed' /> Use </label><input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" /> as the CODE.");   $r->print("
       <label>
          <input type='radio' name='scantron_CODE_resolution' value='use_typed' />
          ".&mt("Use [_1] as the CODE.",
        "</label><input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" />"));
  $r->print("\n<br /><br />");   $r->print("\n<br /><br />");
     } elsif ($error eq 'doublebubble') {      } elsif ($error eq 'doublebubble') {
  $r->print("<p>There have been multiple bubbles scanned for a some question(s)</p>\n");   $r->print("<p>".&mt("There have been multiple bubbles scanned for some question(s)")."</p>\n");
   
    # The form field scantron_questions is acutally a list of line numbers.
    # represented by this form so:
   
    my $line_list = &questions_to_line_list($arg);
   
  $r->print('<input type="hidden" name="scantron_questions" value="'.   $r->print('<input type="hidden" name="scantron_questions" value="'.
   join(',',@{$arg}).'" />');    $line_list.'" />');
  $r->print($message);   $r->print($message);
  $r->print("<p>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 $selected=$$scan_record{"scantron.$question.answer"};      my @linenums = &prompt_for_corrections($r,$question,$scan_config,
     &scantron_bubble_selector($r,$scan_config,$question,                                                     $scan_record, $error);
       split('',$selected));              push(@lines_to_correct,@linenums);
  }   }
           $r->print(&verify_bubbles_checked(@lines_to_correct));
     } elsif ($error eq 'missingbubble') {      } elsif ($error eq 'missingbubble') {
  $r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n");   $r->print("<p>".&mt("There have been <b>no</b> bubbles scanned for some question(s)")."</p>\n");
  $r->print($message);   $r->print($message);
  $r->print("<p>Please indicate which bubble should be used for grading</p>");   $r->print("<p>".&mt("Please indicate which bubble should be used for grading.")."</p>");
  $r->print("Some questions have no scanned bubbles\n");   $r->print(&mt("Some questions have no scanned bubbles.")."\n");
   
    # The form field scantron_questions is actually a list of line numbers not
    # a list of question numbers. Therefore:
    #
   
    my $line_list = &questions_to_line_list($arg);
   
  $r->print('<input type="hidden" name="scantron_questions" value="'.   $r->print('<input type="hidden" name="scantron_questions" value="'.
   join(',',@{$arg}).'" />');    $line_list.'" />');
  foreach my $question (@{$arg}) {   foreach my $question (@{$arg}) {
     my $selected=$$scan_record{"scantron.$question.answer"};      my @linenums = &prompt_for_corrections($r,$question,$scan_config,
     &scantron_bubble_selector($r,$scan_config,$question);                                                     $scan_record, $error);
               push(@lines_to_correct,@linenums);
  }   }
           $r->print(&verify_bubbles_checked(@lines_to_correct));
     } else {      } else {
  $r->print("\n<ul>");   $r->print("\n<ul>");
     }      }
     $r->print("\n</li></ul>");      $r->print("\n</li></ul>");
   }
   
   sub verify_bubbles_checked {
       my (@ansnums) = @_;
       my $ansnumstr = join('","',@ansnums);
       my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
       my $output = (<<ENDSCRIPT);
   <script type="text/javascript">
   function verify_bubble_radio(form) {
       var ansnumArray = new Array ("$ansnumstr");
       var need_bubble_count = 0;
       for (var i=0; i<ansnumArray.length; i++) {
           if (form.elements["scantron_correct_Q_"+ansnumArray[i]].length > 1) {
               var bubble_picked = 0; 
               for (var j=0; j<form.elements["scantron_correct_Q_"+ansnumArray[i]].length; j++) {
                   if (form.elements["scantron_correct_Q_"+ansnumArray[i]][j].checked == true) {
                       bubble_picked = 1;
                   }
               }
               if (bubble_picked == 0) {
                   need_bubble_count ++;
               }
           }
       }
       if (need_bubble_count) {
           alert("$warning");
           return;
       }
       form.submit(); 
   }
   </script>
   ENDSCRIPT
       return $output;
   }
   
   =pod
   
   =item  questions_to_line_list
   
   Converts a list of questions into a string of comma separated
   line numbers in the answer sheet used by the questions.  This is
   used to fill in the scantron_questions form field.
   
     Arguments:
        questions    - Reference to an array of questions.
   
   =cut
   
   
   sub questions_to_line_list {
       my ($questions) = @_;
       my @lines;
   
       foreach my $item (@{$questions}) {
           my $question = $item;
           my ($first,$count,$last);
           if ($item =~ /^(\d+)\.(\d+)$/) {
               $question = $1;
               my $subquestion = $2;
               $first = $first_bubble_line{$question-1} + 1;
               my @subans = split(/,/,$subdivided_bubble_lines{$question-1});
               my $subcount = 1;
               while ($subcount<$subquestion) {
                   $first += $subans[$subcount-1];
                   $subcount ++;
               }
               $count = $subans[$subquestion-1];
           } else {
       $first   = $first_bubble_line{$question-1} + 1;
       $count   = $bubble_lines_per_response{$question-1};
           }
           $last = $first+$count-1;
           push(@lines, ($first..$last));
       }
       return join(',', @lines);
   }
   
   =pod 
   
   =item prompt_for_corrections
   
   Prompts for a potentially multiline correction to the
   user's bubbling (factors out common code from scantron_get_correction
   for multi and missing bubble cases).
   
    Arguments:
      $r           - Apache request object.
      $question    - The question number to prompt for.
      $scan_config - The scantron file configuration hash.
      $scan_record - Reference to the hash that has the the parsed scanlines.
      $error       - Type of error
   
    Implicit inputs:
      %bubble_lines_per_response   - Starting line numbers for each question.
                                     Numbered from 0 (but question numbers are from
                                     1.
      %first_bubble_line           - Starting bubble line for each question.
      %subdivided_bubble_lines     - optionresponse, matchresponse and rankresponse 
                                     type problems render as separate sub-questions, 
                                     in exam mode. This hash contains a 
                                     comma-separated list of the lines per 
                                     sub-question.
      %responsetype_per_response   - essayresponse, formularesponse,
                                     stringresponse, imageresponse, reactionresponse,
                                     and organicresponse type problem parts can have
                                     multiple lines per response if the weight
                                     assigned exceeds 10.  In this case, only
                                     one bubble per line is permitted, but more 
                                     than one line might contain bubbles, e.g.
                                     bubbling of: line 1 - J, line 2 - J, 
                                     line 3 - B would assign 22 points.  
   
   =cut
   
   sub prompt_for_corrections {
       my ($r, $question, $scan_config, $scan_record, $error) = @_;
       my ($current_line,$lines);
       my @linenums;
       my $questionnum = $question;
       if ($question =~ /^(\d+)\.(\d+)$/) {
           $question = $1;
           $current_line = $first_bubble_line{$question-1} + 1 ;
           my $subquestion = $2;
           my @subans = split(/,/,$subdivided_bubble_lines{$question-1});
           my $subcount = 1;
           while ($subcount<$subquestion) {
               $current_line += $subans[$subcount-1];
               $subcount ++;
           }
           $lines = $subans[$subquestion-1];
       } else {
           $current_line = $first_bubble_line{$question-1} + 1 ;
           $lines        = $bubble_lines_per_response{$question-1};
       }
       if ($lines > 1) {
           $r->print(&mt('The group of bubble lines below responds to a single question.').'<br />');
           if (($responsetype_per_response{$question-1} eq 'essayresponse') ||
               ($responsetype_per_response{$question-1} eq 'formularesponse') ||
               ($responsetype_per_response{$question-1} eq 'stringresponse') ||
               ($responsetype_per_response{$question-1} eq 'imageresponse') ||
               ($responsetype_per_response{$question-1} eq 'reactionresponse') ||
               ($responsetype_per_response{$question-1} eq 'organicresponse')) {
               $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their scantron sheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during scantron grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');
           } else {
               $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");
           }
       }
       for (my $i =0; $i < $lines; $i++) {
           my $selected = $$scan_record{"scantron.$current_line.answer"};
    &scantron_bubble_selector($r,$scan_config,$current_line, 
             $questionnum,$error,split('', $selected));
           push(@linenums,$current_line);
    $current_line++;
       }
       if ($lines > 1) {
    $r->print("<hr /><br />");
       }
       return @linenums;
 }  }
   
 =pod  =pod
Line 6203  ENDSCRIPT Line 6982  ENDSCRIPT
  Arguments:   Arguments:
     $r           - Apache request object      $r           - Apache request object
     $scan_config - hash from &get_scantron_config()      $scan_config - hash from &get_scantron_config()
     $quest       - number of the bubble line to make a corrector for      $line        - Number of the line being displayed.
     $selected    - array of letters of previously selected bubbles      $questionnum - Question number (may include subquestion)
     $lines       - if present, number of bubble lines to show      $error       - Type of error.
       @selected    - Array of bubbles picked on this line.
   
 =cut  =cut
   
 sub scantron_bubble_selector {  sub scantron_bubble_selector {
     my ($r,$scan_config,$quest,@selected, $lines)=@_;      my ($r,$scan_config,$line,$questionnum,$error,@selected)=@_;
     my $max=$$scan_config{'Qlength'};      my $max=$$scan_config{'Qlength'};
   
     my $scmode=$$scan_config{'Qon'};      my $scmode=$$scan_config{'Qon'};
     if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; }           if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; }     
   
   
     if (!defined($lines)) {  
  $lines = 1;  
     }  
     my $total_lines = $lines*2;  
     my @alphabet=('A'..'Z');      my @alphabet=('A'..'Z');
     $r->print("<table border='1'><tr><td rowspan='".$total_lines."'>$quest</td>");      $r->print(&Apache::loncommon::start_data_table().
                 &Apache::loncommon::start_data_table_row());
     for (my $l = 0; $l < $lines; $l++) {      $r->print('<td rowspan="2" class="LC_leftcol_header">'.$line.'</td>');
  if ($l != 0) {      for (my $i=0;$i<$max+1;$i++) {
     $r->print('<tr>');   $r->print("\n".'<td align="center">');
  }   if ($selected[0] eq $alphabet[$i]) { $r->print('X'); shift(@selected) }
    else { $r->print('&nbsp;'); }
  # FIXME:  This loop probably has to be considerably more clever for   $r->print('</td>');
  #  multiline bubbles: User can multibubble by having bubbles in      }
  #  several lines.  User can skip lines legitimately etc. etc.      $r->print(&Apache::loncommon::end_data_table_row().
                 &Apache::loncommon::start_data_table_row());
  for (my $i=0;$i<$max;$i++) {      for (my $i=0;$i<$max;$i++) {
     $r->print("\n".'<td align="center">');   $r->print("\n".
     if ($selected[0] eq $alphabet[$i]) {     '<td><label><input type="radio" name="scantron_correct_Q_'.
  $r->print('X');     $line.'" value="'.$i.'" />'.$alphabet[$i]."</label></td>");
  shift(@selected) ;      }
     } else {       my $nobub_checked = ' ';
  $r->print('&nbsp;');       if ($error eq 'missingbubble') {
     }          $nobub_checked = ' checked = "checked" ';
     $r->print('</td>');      }
           $r->print("\n".'<td><label><input type="radio" name="scantron_correct_Q_'.
  }        $line.'" value="none"'.$nobub_checked.'/>'.&mt('No bubble').
                 '</label>'."\n".'<input type="hidden" name="scantron_questionnum_Q_'.
  if ($l == 0) {                $line.'" value="'.$questionnum.'" /></td>');
     my $lspan = $total_lines * 2;   #  2 table rows per bubble line.      $r->print(&Apache::loncommon::end_data_table_row().
                 &Apache::loncommon::end_data_table());
     $r->print('<td rowspan='.$lspan.'><label><input type="radio" name="scantron_correct_Q_'.  
       $quest.'" value="none" /> No bubble </label></td>');  
   
  }  
   
  $r->print('</tr><tr>');  
   
  # FIXME: This may have to be a bit more clever for  
  #        multiline questions (different values e.g..).  
   
  for (my $i=0;$i<$max;$i++) {  
     $r->print("\n".  
       '<td><label><input type="radio" name="scantron_correct_Q_'.  
       $quest.'" value="'.$i.'" />'.$alphabet[$i]."</label></td>");  
  }  
  $r->print('</tr>');  
   
       
     }  
     $r->print('</table>');  
 }  }
   
 =pod  =pod
Line 6393  sub scantron_validate_CODE { Line 7148  sub scantron_validate_CODE {
   
     my %allcodes=&get_codes();      my %allcodes=&get_codes();
   
       &scantron_get_maxbubble(); # parse needs the lines per response array.
   
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     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);
Line 6422  sub scantron_validate_CODE { Line 7179  sub scantron_validate_CODE {
      $line,'duplicateCODE',$usedCODEs{$CODE});       $line,'duplicateCODE',$usedCODEs{$CODE});
     return(1,$currentphase);      return(1,$currentphase);
  }   }
  push (@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'});   push(@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'});
     }      }
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
Line 6445  sub scantron_validate_doublebubble { Line 7202  sub scantron_validate_doublebubble {
     #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();
       &scantron_get_maxbubble(); # parse needs the bubble line array.
   
     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; }
Line 6459  sub scantron_validate_doublebubble { Line 7218  sub scantron_validate_doublebubble {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
 =pod  
   
 =item scantron_get_maxbubble  
   
    Returns the maximum number of bubble lines that are expected to  sub scantron_get_maxbubble {
    occur. Does this by walking the selected sequence rendering the  
    resource and then checking &Apache::lonxml::get_problem_counter()  
    for what the current value of the problem counter is.  
   
    Caches the result to $env{'form.scantron_maxbubble'}  
   
 =cut  
   
 sub scantron_get_maxbubble {      
     if (defined($env{'form.scantron_maxbubble'}) &&      if (defined($env{'form.scantron_maxbubble'}) &&
  $env{'form.scantron_maxbubble'}) {   $env{'form.scantron_maxbubble'}) {
    &restore_bubble_lines();
  return $env{'form.scantron_maxbubble'};   return $env{'form.scantron_maxbubble'};
     }      }
   
     my $navmap=Apache::lonnavmaps::navmap->new();      my (undef, undef, $sequence) =
     my (undef,undef,$sequence)=  
  &Apache::lonnet::decode_symb($env{'form.selectpage'});   &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
       my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
   
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
   
       my $uname       = $env{'form.student'};
       my $udom        = $env{'form.userdom'};
       my $cid         = $env{'request.course.id'};
       my $total_lines = 0;
       %bubble_lines_per_response = ();
       %first_bubble_line         = ();
       %subdivided_bubble_lines   = ();
       %responsetype_per_response = ();
     
       my $response_number = 0;
       my $bubble_line     = 0;
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
  my $result=&Apache::lonnet::ssi($resource->src(),          my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,$udom);
  ('symb' => $resource->symb()));          if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
       foreach my $part_id (@{$parts}) {
                   my $lines;
   
           # TODO - make this a persistent hash not an array.
   
                   # optionresponse, matchresponse and rankresponse type items 
                   # render as separate sub-questions in exam mode.
                   if (($analysis->{$part_id.'.type'} eq 'optionresponse') ||
                       ($analysis->{$part_id.'.type'} eq 'matchresponse') ||
                       ($analysis->{$part_id.'.type'} eq 'rankresponse')) {
                       my ($numbub,$numshown);
                       if ($analysis->{$part_id.'.type'} eq 'optionresponse') {
                           if (ref($analysis->{$part_id.'.options'}) eq 'ARRAY') {
                               $numbub = scalar(@{$analysis->{$part_id.'.options'}});
                           }
                       } elsif ($analysis->{$part_id.'.type'} eq 'matchresponse') {
                           if (ref($analysis->{$part_id.'.items'}) eq 'ARRAY') {
                               $numbub = scalar(@{$analysis->{$part_id.'.items'}});
                           }
                       } elsif ($analysis->{$part_id.'.type'} eq 'rankresponse') {
                           if (ref($analysis->{$part_id.'.foils'}) eq 'ARRAY') {
                               $numbub = scalar(@{$analysis->{$part_id.'.foils'}});
                           }
                       }
                       if (ref($analysis->{$part_id.'.shown'}) eq 'ARRAY') {
                           $numshown = scalar(@{$analysis->{$part_id.'.shown'}});
                       }
                       my $bubbles_per_line = 10;
                       my $inner_bubble_lines = int($numbub/$bubbles_per_line);
                       if (($numbub % $bubbles_per_line) != 0) {
                           $inner_bubble_lines++;
                       }
                       for (my $i=0; $i<$numshown; $i++) {
                           $subdivided_bubble_lines{$response_number} .= 
                               $inner_bubble_lines.',';
                       }
                       $subdivided_bubble_lines{$response_number} =~ s/,$//;
                       $lines = $numshown * $inner_bubble_lines;
                   } else {
                       $lines = $analysis->{"$part_id.bubble_lines"};
                   } 
   
                   $first_bubble_line{$response_number} = $bubble_line;
           $bubble_lines_per_response{$response_number} = $lines;
                   $responsetype_per_response{$response_number} = 
                       $analysis->{$part_id.'.type'};
           $response_number++;
   
           $bubble_line +=  $lines;
           $total_lines +=  $lines;
       }
           }
     }      }
     &Apache::lonnet::delenv('scantron\.');      &Apache::lonnet::delenv('scantron\.');
     $env{'form.scantron_maxbubble'} =  
  &Apache::lonxml::get_problem_counter()-1;  
   
       &save_bubble_lines();
       $env{'form.scantron_maxbubble'} =
    $total_lines;
     return $env{'form.scantron_maxbubble'};      return $env{'form.scantron_maxbubble'};
 }  }
   
 =pod  sub scantron_partids_tograde {
       my ($resource,$cid,$uname,$udom) = @_;
 =item scantron_validate_missingbubbles      my (%analysis,@parts); 
   
    Validates all scanlines in the selected file to not have any      if (ref($resource)) {
    bubble lines with missing bubbles that haven't been verified as missing.          my $symb = $resource->symb();
           my $result=&ssi_with_retries($resource->src(), $ssi_retries,
 =cut                                          ('symb' => $symb,
                                            'grade_target' => 'analyze',
                                            'grade_courseid' => $cid,
                                            'grade_domain' => $udom,
                                            'grade_username' => $uname));
           my (undef, $an) = split(/_HASH_REF__/,$result, 2);
           %analysis = &Apache::lonnet::str2hash($an);
   
           if (ref($analysis{'parts'}) eq 'ARRAY') {
               foreach my $part (@{$analysis{'parts'}}) {
                   my ($id,$respid) = split(/\./,$part);
                   if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
                       push(@parts,$part);
                   }
               }
           }
       }
       return (\%analysis,\@parts);
   }
   
 sub scantron_validate_missingbubbles {  sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
Line 6525  sub scantron_validate_missingbubbles { Line 7355  sub scantron_validate_missingbubbles {
  $scan_data);   $scan_data);
  if (!defined($$scan_record{'scantron.missingerror'})) { next; }   if (!defined($$scan_record{'scantron.missingerror'})) { next; }
  my @to_correct;   my @to_correct;
   
    # Probably here's where the error is...
   
  foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {   foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {
     if ($missing > $max_bubble) { next; }              my $lastbubble;
               if ($missing =~ /^(\d+)\.(\d+)$/) {
                  my $question = $1;
                  my $subquestion = $2;
                  if (!defined($first_bubble_line{$question -1})) { next; }
                  my $first = $first_bubble_line{$question-1};
                  my @subans = split(/,/,$subdivided_bubble_lines{$question-1});
                  my $subcount = 1;
                  while ($subcount<$subquestion) {
                      $first += $subans[$subcount-1];
                      $subcount ++;
                  }
                  my $count = $subans[$subquestion-1];
                  $lastbubble = $first + $count;
               } else {
                   if (!defined($first_bubble_line{$missing - 1})) { next; }
                   $lastbubble = $first_bubble_line{$missing - 1} + $bubble_lines_per_response{$missing - 1};
               }
               if ($lastbubble > $max_bubble) { next; }
     push(@to_correct,$missing);      push(@to_correct,$missing);
  }   }
  if (@to_correct) {   if (@to_correct) {
Line 6539  sub scantron_validate_missingbubbles { Line 7390  sub scantron_validate_missingbubbles {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
 =pod  
   
 =item scantron_process_students  
   
    Routine that does the actual grading of the bubble sheet information.  
   
    The parsed scanline hash is added to %env   
   
    Then foreach unskipped scanline it does an &Apache::lonnet::ssi()  
    foreach resource , with the form data of  
   
  'submitted'     =>'scantron'   
  'grade_target'  =>'grade',  
  'grade_username'=> username of student  
  'grade_domain'  => domain of student  
  'grade_courseid'=> of course  
  'grade_symb'    => symb of resource to grade  
   
     This triggers a grading pass. The problem grading code takes care  
     of converting the bubbled letter information (now in %env) into a  
     valid submission.  
   
 =cut  
   
 sub scantron_process_students {  sub scantron_process_students {
     my ($r) = @_;      my ($r) = @_;
   
     my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});      my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
     my ($symb)=&get_symb($r);      my ($symb)=&get_symb($r);
     if (!$symb) {return '';}      if (!$symb) {
    return '';
       }
     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'});
Line 6577  sub scantron_process_students { Line 7408  sub scantron_process_students {
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
   
       my ($uname,$udom,%partids_by_symb);
       foreach my $resource (@resources) {
           my $ressymb = $resource->symb(); 
           my ($analysis,$parts) = 
               &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
           $partids_by_symb{$ressymb} = $parts;
       }
 #    $r->print("geto ".scalar(@resources)."<br />");  #    $r->print("geto ".scalar(@resources)."<br />");
     my $result= <<SCANTRONFORM;      my $result= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
Line 6586  SCANTRONFORM Line 7425  SCANTRONFORM
     $r->print($result);      $r->print($result);
   
     my @delayqueue;      my @delayqueue;
     my %completedstudents;      my (%completedstudents,%scandata);
           
       my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
     my $count=&get_todo_count($scanlines,$scan_data);      my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
      'Scantron Progress',$count,       'Scantron Progress',$count,
     'inline',undef,'scantronupload');      'inline',undef,'scantronupload');
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
   'Processing first student');    'Processing first student');
       $r->print('<br />');
     my $start=&Time::HiRes::time();      my $start=&Time::HiRes::time();
     my $i=-1;      my $i=-1;
     my ($uname,$udom,$started);      my $started;
   
       &scantron_get_maxbubble(); # Need the bubble lines array to parse.
       
   
       # If an ssi failed in scantron_get_maxbubble, put an error message out to
       # the user and return.
   
       if ($ssi_error) {
    $r->print("</form>");
    &ssi_print_error($r);
    $r->print(&show_grading_menu_form($symb));
           &Apache::lonnet::remove_lock($lock);
    return ''; # Dunno why the other returns return '' rather than just returning.
       }
   
       my %lettdig = &letter_to_digits();
       my $numletts = scalar(keys(%lettdig));
   
     while ($i<$scanlines->{'count'}) {      while ($i<$scanlines->{'count'}) {
   ($uname,$udom)=('','');    ($uname,$udom)=('','');
   $i++;    $i++;
Line 6623  SCANTRONFORM Line 7482  SCANTRONFORM
   ($uname,$udom)=split(/:/,$uname);    ($uname,$udom)=split(/:/,$uname);
   
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
   &Apache::lonnet::appenv(%$scan_record);    &Apache::lonnet::appenv($scan_record);
   
  if (&scantron_clear_skip($scanlines,$scan_data,$i)) {   if (&scantron_clear_skip($scanlines,$scan_data,$i)) {
     &scantron_putfile($scanlines,$scan_data);      &scantron_putfile($scanlines,$scan_data);
  }   }
   
  my $i=0;          my $scancode;
  foreach my $resource (@resources) {          if ((exists($scan_record->{'scantron.CODE'})) &&
     $i++;              (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) {
     my %form=('submitted'     =>'scantron',              $scancode = $scan_record->{'scantron.CODE'};
       'grade_target'  =>'grade',          } else {
       'grade_username'=>$uname,              $scancode = '';
       'grade_domain'  =>$udom,          }
       'grade_courseid'=>$env{'request.course.id'},  
       'grade_symb'    =>$resource->symb());          if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
     if (exists($scan_record->{'scantron.CODE'})                                     @resources) eq 'ssi_error') {
  &&               $ssi_error = 0; # So end of handler error message does not trigger.
  &Apache::lonnet::validCODE($scan_record->{'scantron.CODE'})) {              $r->print("</form>");
  $form{'CODE'}=$scan_record->{'scantron.CODE'};              &ssi_print_error($r);
     } else {              $r->print(&show_grading_menu_form($symb));
  $form{'CODE'}='';              &Apache::lonnet::remove_lock($lock);
     }              return '';      # Why return ''?  Beats me.
     my $result=&Apache::lonnet::ssi($resource->src(),%form);          }
     if ($result ne '') {  
  &Apache::lonnet::logthis("scantron grading error -> $result");  
  &Apache::lonnet::logthis("scantron grading error info name $uname domain $udom course $env{'request.course.id'} url ".$resource->src());  
     }  
     if (&Apache::loncommon::connection_aborted($r)) { last; }  
  }  
  $completedstudents{$uname}={'line'=>$line};   $completedstudents{$uname}={'line'=>$line};
  if (&Apache::loncommon::connection_aborted($r)) { last; }          if ($env{'form.verifyrecord'}) {
               my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};
               my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos);
               chomp($studentdata);
               $studentdata =~ s/\r$//;
               my $studentrecord = '';
               my $counter = -1;
               foreach my $resource (@resources) {
                   ($counter,my $recording) =
                       &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                                $counter,$studentdata,\%partids_by_symb,
                                                \%scantron_config,\%lettdig,$numletts);
                   $studentrecord .= $recording;
               }
               if ($studentrecord ne $studentdata) {
                   $counter = -1;
                   $studentrecord = '';
                   foreach my $resource (@resources) {
                       ($counter,my $recording) =
                           &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                                    $counter,$studentdata,\%partids_by_symb,
                                                    \%scantron_config,\%lettdig,$numletts);
                       $studentrecord .= $recording;
                   }
                   if ($studentrecord ne $studentdata) {
                       $r->print('<p><span class="LC_error">');
                       if ($scancode eq '') {
                           $r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2].',
                                     $uname.':'.$udom,$scan_record->{'scantron.ID'}));
                       } else {
                           $r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2] and CODE: [_3].',
                                     $uname.':'.$udom,$scan_record->{'scantron.ID'},$scancode));
                       }
                       $r->print('</span><br />'.&Apache::loncommon::start_data_table()."\n".
                                 &Apache::loncommon::start_data_table_header_row()."\n".
                                 '<th>'.&mt('Source').'</th><th>'.&mt('Bubbled responses').'</th>'.
                                 &Apache::loncommon::end_data_table_header_row()."\n".
                                 &Apache::loncommon::start_data_table_row().
                                 '<td>'.&mt('Bubble Sheet').'</td>'.
                                 '<td><span class="LC_nobreak">'.$studentdata.'</span></td>'.
                                 &Apache::loncommon::end_data_table_row().
                                 &Apache::loncommon::start_data_table_row().
                                 '<td>Stored submissions</td>'.
                                 '<td><span class="LC_nobreak">'.$studentrecord.'</span></td>'."\n".
                                 &Apache::loncommon::end_data_table_row().
                                 &Apache::loncommon::end_data_table().'</p>');
                   } else {
                       $r->print('<br /><span class="LC_warning">'.
                                &mt('A second grading pass was needed for user: [_1] with ID: [_2], because a mismatch was seen on the first pass.',$uname.':'.$udom,$scan_record->{'scantron.ID'}).'<br />'.
                                &mt("As a consequence, this user's submission history records two tries.").
                                    '</span><br />');
                   }
               }
           }
           if (&Apache::loncommon::connection_aborted($r)) { last; }
     } continue {      } continue {
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
  &Apache::lonnet::delenv('scantron\.');   &Apache::lonnet::delenv('scantron\.');
     }      }
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);      &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
       &Apache::lonnet::remove_lock($lock);
 #    my $lasttime = &Time::HiRes::time()-$start;  #    my $lasttime = &Time::HiRes::time()-$start;
 #    $r->print("<p>took $lasttime</p>");  #    $r->print("<p>took $lasttime</p>");
   
Line 6667  SCANTRONFORM Line 7576  SCANTRONFORM
     return '';      return '';
 }  }
   
 =pod  sub grade_student_bubbles {
       my ($r,$uname,$udom,$scan_record,$scancode,@resources) = @_;
 =item scantron_upload_scantron_data      foreach my $resource (@resources) {
           my %form = ('submitted'     => 'scantron',
     Creates the screen for adding a new bubble sheet data file to a course.                      'grade_target'  => 'grade',
                       'grade_username'=> $uname,
 =cut                      'grade_domain'  => $udom,
                       'grade_courseid'=> $env{'request.course.id'},
                       'grade_symb'    => $resource->symb(),
                       'CODE'          => $scancode);
           my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
           return 'ssi_error' if ($ssi_error);
           last if (&Apache::loncommon::connection_aborted($r));
       }
       return;
   }
   
 sub scantron_upload_scantron_data {  sub scantron_upload_scantron_data {
     my ($r)=@_;      my ($r)=@_;
Line 6684  sub scantron_upload_scantron_data { Line 7602  sub scantron_upload_scantron_data {
     my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'},      my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'},
    'domainid');     'domainid');
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData(&get_symb($r,1));
     $r->print(<<UPLOAD);      $r->print('
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
     alert("Please use the browse button to select a file from your local directory.");      alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
     return false;      return false;
  }   }
  formname.submit();   formname.submit();
     }      }
 </script>  </script>
   
 <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'>  <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
 $default_form_data  '.$default_form_data.'
 <table>  <table>
 <tr><td>$select_link </td></tr>  <tr><td>'.$select_link.'                             </td></tr>
 <tr><td>Course ID:   </td><td><input name='courseid' type='text' />  </td></tr>  <tr><td>'.&mt('Course ID:').'     </td>
 <tr><td>Course Name: </td><td><input name='coursename' type='text' /></td></tr>      <td><input name="courseid"   type="text" />      </td></tr>
 <tr><td>Domain:      </td><td>$domsel                                </td></tr>  <tr><td>'.&mt('Course Name:').'   </td>
 <tr><td>File to upload:</td><td><input type="file" name="upfile" size="50" /></td></tr>      <td><input name="coursename" type="text" />      </td></tr>
   <tr><td>'.&mt('Domain:').'        </td>
       <td>'.$domsel.'                                  </td></tr>
   <tr><td>'.&mt('File to upload:').'</td>
       <td><input type="file" name="upfile" size="50" /></td></tr>
 </table>  </table>
 <input name='command' value='scantronupload_save' type='hidden' />  <input name="command" value="scantronupload_save" type="hidden" />
 <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" />  <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
 </form>  </form>
 UPLOAD  ');
     return '';      return '';
 }  }
   
 =pod  
   
 =item scantron_upload_scantron_data_save  
   
    Adds a provided bubble information data file to the course if user  
    has the correct privileges to do so.    
   
 =cut  
   
 sub scantron_upload_scantron_data_save {  sub scantron_upload_scantron_data_save {
     my($r)=@_;      my($r)=@_;
Line 6726  sub scantron_upload_scantron_data_save { Line 7640  sub scantron_upload_scantron_data_save {
     my $doanotherupload=      my $doanotherupload=
  '<br /><form action="/adm/grades" method="post">'."\n".   '<br /><form action="/adm/grades" method="post">'."\n".
  '<input type="hidden" name="command" value="scantronupload" />'."\n".   '<input type="hidden" name="command" value="scantronupload" />'."\n".
  '<input type="submit" name="submit" value="Do Another Upload" />'."\n".   '<input type="submit" name="submit" value="'.&mt('Do Another Upload').'" />'."\n".
  '</form>'."\n";   '</form>'."\n";
     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&      if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
  !&Apache::lonnet::allowed('usc',   !&Apache::lonnet::allowed('usc',
     $env{'form.domainid'}.'_'.$env{'form.courseid'})) {      $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
  $r->print("You are not allowed to upload Scantron data to the requested course.<br />");   $r->print(&mt("You are not allowed to upload Scantron data to the requested course.")."<br />");
  if ($symb) {   if ($symb) {
     $r->print(&show_grading_menu_form($symb));      $r->print(&show_grading_menu_form($symb));
  } else {   } else {
Line 6740  sub scantron_upload_scantron_data_save { Line 7654  sub scantron_upload_scantron_data_save {
  return '';   return '';
     }      }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});      my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
     $r->print("Doing upload to ".$coursedata{'description'}." <br />");      $r->print(&mt("Doing upload to [_1]",$coursedata{'description'})." <br />");
     my $fname=$env{'form.upfile.filename'};      my $fname=$env{'form.upfile.filename'};
     #FIXME      #FIXME
     #copied from lonnet::userfileupload()      #copied from lonnet::userfileupload()
Line 6758  sub scantron_upload_scantron_data_save { Line 7672  sub scantron_upload_scantron_data_save {
     my $uploadedfile=$fname;      my $uploadedfile=$fname;
     $fname='scantron_orig_'.$fname;      $fname='scantron_orig_'.$fname;
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
  $r->print("<span class=\"LC_error\">Error:</span> The file you attempted to upload, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename.");   $r->print(&mt("<span class=\"LC_error\">Error:</span> The file you attempted to upload, [_1]  contained no information. Please check that you entered the correct filename.",'<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));
     } else {      } else {
  my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);   my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);
  if ($result =~ m|^/uploaded/|) {   if ($result =~ m|^/uploaded/|) {
     $r->print("<span class=\"LC_success\">Success:</span> Successfully uploaded ".(length($env{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>");      $r->print(&mt("<span class=\"LC_success\">Success:</span> Successfully uploaded [_1] bytes of data into location [_2]",
     (length($env{'form.upfile'})-1),
     '<span class="LC_filename">'.$result."</span>"));
  } else {   } else {
     $r->print("<span class=\"LC_error\">Error:</span> An error (".$result.") occurred when attempting to upload the file, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>");      $r->print(&mt("<span class=\"LC_error\">Error:</span> An error ([_1]) occurred when attempting to upload the file, [_2]",
     $result,
     '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));
   
  }   }
     }      }
     if ($symb) {      if ($symb) {
Line 6775  sub scantron_upload_scantron_data_save { Line 7694  sub scantron_upload_scantron_data_save {
     return '';      return '';
 }  }
   
 =pod  
   
 =item valid_file  
   
    Validates that the requested bubble data file exists in the course.  
   
 =cut  
   
 sub valid_file {  sub valid_file {
     my ($requested_file)=@_;      my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {      foreach my $filename (sort(&scantron_filenames())) {
Line 6791  sub valid_file { Line 7702  sub valid_file {
     return 0;      return 0;
 }  }
   
 =pod  
   
 =item scantron_download_scantron_data  
   
    Shows a list of the three internal files (original, corrected,  
    skipped) for a specific bubble sheet data file that exists in the  
    course.  
   
 =cut  
   
 sub scantron_download_scantron_data {  sub scantron_download_scantron_data {
     my ($r)=@_;      my ($r)=@_;
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData(&get_symb($r,1));
Line 6808  sub scantron_download_scantron_data { Line 7709  sub scantron_download_scantron_data {
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
     my $file=$env{'form.scantron_selectfile'};      my $file=$env{'form.scantron_selectfile'};
     if (! &valid_file($file)) {      if (! &valid_file($file)) {
  $r->print(<<ERROR);   $r->print('
  <p>   <p>
     The requested file name was invalid.      '.&mt('The requested file name was invalid.').'
         </p>          </p>
 ERROR  ');
  $r->print(&show_grading_menu_form(&get_symb($r,1)));   $r->print(&show_grading_menu_form(&get_symb($r,1)));
  return;   return;
     }      }
Line 6822  ERROR Line 7723  ERROR
     &Apache::lonnet::allowuploaded('/adm/grades',$orig);      &Apache::lonnet::allowuploaded('/adm/grades',$orig);
     &Apache::lonnet::allowuploaded('/adm/grades',$corrected);      &Apache::lonnet::allowuploaded('/adm/grades',$corrected);
     &Apache::lonnet::allowuploaded('/adm/grades',$skipped);      &Apache::lonnet::allowuploaded('/adm/grades',$skipped);
     $r->print(<<DOWNLOAD);      $r->print('
     <p>      <p>
  <a href="$orig">Original</a> file as uploaded by the scantron office.   '.&mt('[_1]Original[_2] file as uploaded by the scantron office.',
         '<a href="'.$orig.'">','</a>').'
     </p>      </p>
     <p>      <p>
  <a href="$corrected">Corrections</a>, a file of corrected records that were used in grading.   '.&mt('[_1]Corrections[_2], a file of corrected records that were used in grading.',
         '<a href="'.$corrected.'">','</a>').'
     </p>      </p>
     <p>      <p>
  <a href="$skipped">Skipped</a>, a file of records that were skipped.   '.&mt('[_1]Skipped[_2], a file of records that were skipped.',
         '<a href="'.$skipped.'">','</a>').'
     </p>      </p>
 DOWNLOAD  ');
     $r->print(&show_grading_menu_form(&get_symb($r,1)));      $r->print(&show_grading_menu_form(&get_symb($r,1)));
     return '';      return '';
 }  }
   
 =pod  sub checkscantron_results {
       my ($r) = @_;
       my ($symb)=&get_symb($r);
       if (!$symb) {return '';}
       my $grading_menu_button=&show_grading_menu_form($symb);
       my $cid = $env{'request.course.id'};
       my %lettdig = &letter_to_digits();
       my $numletts = scalar(keys(%lettdig));
       my $cnum = $env{'course.'.$cid.'.num'};
       my $cdom = $env{'course.'.$cid.'.domain'};
       my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});
       my %record;
       my %scantron_config =
           &Apache::grades::get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();
       my $classlist=&Apache::loncoursedata::get_classlist();
       my %idmap=&Apache::grades::username_to_idmap($classlist);
       my $navmap=Apache::lonnavmaps::navmap->new();
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,undef,1,0);
       my ($uname,$udom,%partids_by_symb);
       foreach my $resource (@resources) {
           my $ressymb = $resource->symb();
           my ($analysis,$parts) =
               &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
           $partids_by_symb{$ressymb} = $parts;
       }
       my (%scandata,%lastname,%bylast);
       $r->print('
   <form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n");
   
 =back      my @delayqueue;
       my %completedstudents;
   
       my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);
       my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status',
                                       'Progress of Scantron Data/Submission Records Comparison',$count,
                                       'inline',undef,'checkscantron');
       my ($username,$domain,$started);
   
       &Apache::grades::scantron_get_maxbubble();  # Need the bubble lines array to parse.
   
       &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
                                             'Processing first student');
       my $start=&Time::HiRes::time();
       my $i=-1;
   
       while ($i<$scanlines->{'count'}) {
           ($username,$domain,$uname)=('','','');
           $i++;
           my $line=&Apache::grades::scantron_get_line($scanlines,$scan_data,$i);
           if ($line=~/^[\s\cz]*$/) { next; }
           if ($started) {
               &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
                                                        'last student');
           }
           $started=1;
           my $scan_record=
               &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,
                                                        $scan_data);
           unless ($uname=&Apache::grades::scantron_find_student($scan_record,$scan_data,
                                                                 \%idmap,$i)) {
               &Apache::grades::scantron_add_delay(\@delayqueue,$line,
                                   'Unable to find a student that matches',1);
               next;
           }
           if (exists $completedstudents{$uname}) {
               &Apache::grades::scantron_add_delay(\@delayqueue,$line,
                                   'Student '.$uname.' has multiple sheets',2);
               next;
           }
           my $pid = $scan_record->{'scantron.ID'};
           $lastname{$pid} = $scan_record->{'scantron.LastName'};
           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$//;
           ($username,$domain)=split(/:/,$uname);
           my $counter = -1;
           foreach my $resource (@resources) {
               ($counter,my $recording) =
                   &verify_scantron_grading($resource,$domain,$username,$cid,$counter,
                                            $scandata{$pid},\%partids_by_symb,
                                            \%scantron_config,\%lettdig,$numletts);
               $record{$pid} .= $recording;
           }
       }
       &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
       $r->print('<br />');
       my ($okstudents,$badstudents,$numstudents,$passed,$failed);
       $passed = 0;
       $failed = 0;
       $numstudents = 0;
       foreach my $last (sort(keys(%bylast))) {
           if (ref($bylast{$last}) eq 'ARRAY') {
               foreach my $pid (sort(@{$bylast{$last}})) {
                   my $showscandata = $scandata{$pid};
                   my $showrecord = $record{$pid};
                   $showscandata =~ s/\s/&nbsp;/g;
                   $showrecord =~ s/\s/&nbsp;/g;
                   if ($scandata{$pid} eq $record{$pid}) {
                       my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row';
                       $okstudents .= '<tr class="'.$css_class.'">'.
   '<td>'.&mt('Scantron').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
   '</tr>'."\n".
   '<tr class="'.$css_class.'">'."\n".
   '<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n";
                       $passed ++;
                   } else {
                       my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row';
                       $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Scantron').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
   '</tr>'."\n".
   '<tr class="'.$css_class.'">'."\n".
   '<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".
   '</tr>'."\n";
                       $failed ++;
                   }
                   $numstudents ++;
               }
           }
       }
       $r->print('<p>'.&mt('Comparison of scantron data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');
       $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');
       if ($passed) {
           $r->print(&mt('Students with exact correspondence between scantron data and submissions are as follows:').'<br /><br />');
           $r->print(&Apache::loncommon::start_data_table()."\n".
                    &Apache::loncommon::start_data_table_header_row()."\n".
                    '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
                    &Apache::loncommon::end_data_table_header_row()."\n".
                    $okstudents."\n".
                    &Apache::loncommon::end_data_table().'<br />');
       }
       if ($failed) {
           $r->print(&mt('Students with differences between scantron data and submissions are as follows:').'<br /><br />');
           $r->print(&Apache::loncommon::start_data_table()."\n".
                    &Apache::loncommon::start_data_table_header_row()."\n".
                    '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
                    &Apache::loncommon::end_data_table_header_row()."\n".
                    $badstudents."\n".
                    &Apache::loncommon::end_data_table()).'<br />'.
                    &mt('Differences can occur if submissions were modified using manual grading after a scantron grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original scantron sheets.');  
       }
       $r->print('</form><br />'.$grading_menu_button);
       return;
   }
   
   sub verify_scantron_grading {
       my ($resource,$domain,$username,$cid,$counter,$scandata,$partids_by_symb,
           $scantron_config,$lettdig,$numletts) = @_;
       my ($record,%expected,%startpos);
       return ($counter,$record) if (!ref($resource));
       return ($counter,$record) if (!$resource->is_problem());
       my $symb = $resource->symb();
       return ($counter,$record) if (ref($partids_by_symb) ne 'HASH');
       return ($counter,$record) if (ref($partids_by_symb->{$symb}) ne 'ARRAY');
       foreach my $part_id (@{$partids_by_symb->{$symb}}) {
           $counter ++;
           $expected{$part_id} = 0;
           if ($env{"form.scantron.sub_bubblelines.$counter"}) {
               my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"});
               foreach my $item (@sub_lines) {
                   $expected{$part_id} += $item;
               }
           } else {
               $expected{$part_id} = $env{"form.scantron.bubblelines.$counter"};
           }
           $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};
       }
       if ($symb) {
           my %recorded;
           my (%returnhash) = &Apache::lonnet::restore($symb,$cid,$domain,$username);
           if ($returnhash{'version'}) {
               my %lasthash=();
               my $version;
               for ($version=1;$version<=$returnhash{'version'};$version++) {
                   foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
                       $lasthash{$key}=$returnhash{$version.':'.$key};
                   }
               }
               foreach my $key (keys(%lasthash)) {
                   if ($key =~ /\.scantron$/) {
                       my $value = &unescape($lasthash{$key});
                       my ($part_id) = ($key =~ /^resource\.(.+)\.scantron$/);
                       if ($value eq '') {
                           for (my $i=0; $i<$expected{$part_id}; $i++) {
                               for (my $j=0; $j<$scantron_config->{'length'}; $j++) {
                                   $recorded{$part_id} .= $scantron_config->{'Qoff'};
                               }
                           }
                       } else {
                           my @tocheck;
                           my @items = split(//,$value);
                           if (($scantron_config->{'Qon'} eq 'letter') ||
                               ($scantron_config->{'Qon'} eq 'number')) {
                               if (@items < $expected{$part_id}) {
                                   my $fragment = substr($scandata,$startpos{$part_id},$expected{$part_id});
                                   my @singles = split(//,$fragment);
                                   foreach my $pos (@singles) {
                                       if ($pos eq ' ') {
                                           push(@tocheck,$pos);
                                       } else {
                                           my $next = shift(@items);
                                           push(@tocheck,$next);
                                       }
                                   }
                               } else {
                                   @tocheck = @items;
                               }
                               foreach my $letter (@tocheck) {
                                   if ($scantron_config->{'Qon'} eq 'letter') {
                                       if ($letter !~ /^[A-J]$/) {
                                           $letter = $scantron_config->{'Qoff'};
                                       }
                                       $recorded{$part_id} .= $letter;
                                   } elsif ($scantron_config->{'Qon'} eq 'number') {
                                       my $digit;
                                       if ($letter !~ /^[A-J]$/) {
                                           $digit = $scantron_config->{'Qoff'};
                                       } else {
                                           $digit = $lettdig->{$letter};
                                       }
                                       $recorded{$part_id} .= $digit;
                                   }
                               }
                           } else {
                               @tocheck = @items;
                               for (my $i=0; $i<$expected{$part_id}; $i++) {
                                   my $curr_sub = shift(@tocheck);
                                   my $digit;
                                   if ($curr_sub =~ /^[A-J]$/) {
                                       $digit = $lettdig->{$curr_sub}-1;
                                   }
                                   if ($curr_sub eq 'J') {
                                       $digit += scalar($numletts);
                                   }
                                   for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
                                       if ($j == $digit) {
                                           $recorded{$part_id} .= $scantron_config->{'Qon'};
                                       } else {
                                           $recorded{$part_id} .= $scantron_config->{'Qoff'};
                                       }
                                   }
                               }
                           }
                       }
                   }
               }
           }
           foreach my $part_id (@{$partids_by_symb->{$symb}}) {
               if ($recorded{$part_id} eq '') {
                   for (my $i=0; $i<$expected{$part_id}; $i++) {
                       for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
                           $recorded{$part_id} .= $scantron_config->{'Qoff'};
                       }
                   }
               }
               $record .= $recorded{$part_id};
           }
       }
       return ($counter,$record);
   }
   
   sub letter_to_digits { 
       my %lettdig = (
                       A => 1,
                       B => 2,
                       C => 3,
                       D => 4,
                       E => 5,
                       F => 6,
                       G => 7,
                       H => 8,
                       I => 9,
                       J => 0,
                     );
       return %lettdig;
   }
   
 =cut  
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
Line 6856  sub show_grading_menu_form { Line 8034  sub show_grading_menu_form {
  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="command" value="gradingmenu" />'."\n".   '<input type="hidden" name="command" value="gradingmenu" />'."\n".
  '<input type="submit" name="submit" value="Grading Menu" />'."\n".   '<input type="submit" name="submit" value="'.&mt('Grading Menu').'" />'."\n".
  '</form>'."\n";   '</form>'."\n";
     return $result;      return $result;
 }  }
Line 6873  sub savedState { Line 8051  sub savedState {
     return \%savedState;      return \%savedState;
 }  }
   
 #--- Displays the main menu page -------  sub grading_menu {
 sub gradingmenu {  
     my ($request) = @_;      my ($request) = @_;
     my ($symb)=&get_symb($request);      my ($symb)=&get_symb($request);
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $probTitle = &Apache::lonnet::gettitle($symb);      my $probTitle = &Apache::lonnet::gettitle($symb);
       my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);
   
       $request->print($table);
       my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),
                     'handgrade'=>$hdgrade,
                     'probTitle'=>$probTitle,
                     'command'=>'submit_options',
                     'saveState'=>"",
                     'gradingMenu'=>1,
                     'showgrading'=>"yes");
       
       my $url1 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
       $fields{'command'} = 'csvform';
       my $url2 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
       $fields{'command'} = 'processclicker';
       my $url3 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
       $fields{'command'} = 'scantron_selectphase';
       my $url4 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
       my @menu = ({ categorytitle=>'Course Grading',
               items =>[
                           { linktext => 'Manual Grading/View Submissions',
                       url => $url1,
                       permission => 'F',
                       icon => 'edit-find-replace.png',
                       linktitle => 'Start the process of hand grading submissions.'
                           },
                      { linktext => 'Upload Scores',
                       url => $url2,
                       permission => 'F',
                       icon => 'uploadscores.png',
                       linktitle => 'Specify a file containing the class scores for current resource.'
                      },
                      { linktext => 'Process Clicker',
                       url => $url3,
                       permission => 'F',
                       icon => 'addClickerInfoFile.png',
                       linktitle => 'Specify a file containing the clicker information for this resource.'
                      },
                      { linktext => 'Grade/Manage/Review Scantron Forms',
                       url => $url4,
                       permission => 'F',
                       icon => 'stat.png',
                       linktitle => 'Grade scantron exams, upload/download scantron data files, and review previously graded scantron exams.'
                      }
                       ]
               });
   
       #$fields{'command'} = 'verify';
       #$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       #
       # Create the menu
       my $Str;
       # $Str .= '<h2>'.&mt('Please select a grading task').'</h2>';
       $Str .= '<form method="post" action="" name="gradingMenu">';
       $Str .= '<input type="hidden" name="command" value="" />'.
       '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
    '<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".
    '<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".
    '<input type="hidden" name="saveState"   value="" />'."\n".
    '<input type="hidden" name="gradingMenu" value="1" />'."\n".
    '<input type="hidden" name="showgrading" value="yes" />'."\n";
   
       $Str .= Apache::lonhtmlcommon::generate_menu(@menu);
       #$menudata->{'jscript'}
       $Str .='<hr /><input type="button" value="'.&mt('Verify Receipt').'" '.
           ' onClick="javascript:checkChoice(document.forms.gradingMenu,\'5\',\'verify\')" '.
           ' /> '.
           &Apache::lonnet::recprefix($env{'request.course.id'}).
           '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')" />';
   
       $Str .="</form>\n";
       my $receiptalert = &mt("Please enter a receipt number given by a student in the receipt box.");
       $request->print(<<GRADINGMENUJS);
   <script type="text/javascript" language="javascript">
       function checkChoice(formname,val,cmdx) {
    if (val <= 2) {
       var cmd = radioSelection(formname.radioChoice);
       var cmdsave = cmd;
    } else {
       cmd = cmdx;
       cmdsave = 'submission';
    }
    formname.command.value = cmd;
    if (val < 5) formname.submit();
    if (val == 5) {
       if (!checkReceiptNo(formname,'notOK')) { 
           return false;
       } else {
           formname.submit();
       }
    }
       }
   
       function checkReceiptNo(formname,nospace) {
    var receiptNo = formname.receipt.value;
    var checkOpt = false;
    if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
    if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
    if (checkOpt) {
       alert("$receiptalert");
       formname.receipt.value = "";
       formname.receipt.focus();
       return false;
    }
    return true;
       }
   </script>
   GRADINGMENUJS
       &commonJSfunctions($request);
       return $Str;    
   }
   
   
   #--- Displays the submissions first page -------
   sub submit_options {
       my ($request) = @_;
       my ($symb)=&get_symb($request);
       if (!$symb) {return '';}
       my $probTitle = &Apache::lonnet::gettitle($symb);
   
       my $receiptalert = &mt("Please enter a receipt number given by a student in the receipt box."); 
     $request->print(<<GRADINGMENUJS);      $request->print(<<GRADINGMENUJS);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkChoice(formname,val,cmdx) {      function checkChoice(formname,val,cmdx) {
Line 6907  sub gradingmenu { Line 8208  sub gradingmenu {
  if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}   if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
  if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}   if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
  if (checkOpt) {   if (checkOpt) {
     alert("Please enter a receipt number given by a student in the receipt box.");      alert("$receiptalert");
     formname.receipt.value = "";      formname.receipt.value = "";
     formname.receipt.focus();      formname.receipt.focus();
     return false;      return false;
Line 6917  sub gradingmenu { Line 8218  sub gradingmenu {
 </script>  </script>
 GRADINGMENUJS  GRADINGMENUJS
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my $result='<h3>&nbsp;<span class="LC_info">Manual Grading/View Submission</span></h3>';  
     my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);      my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);
     $result.=$table;      my $result;
     my (undef,$sections) = &getclasslist('all','0');      my (undef,$sections) = &getclasslist('all','0');
     my $savedState = &savedState();      my $savedState = &savedState();
     my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'});      my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'});
Line 6927  GRADINGMENUJS Line 8227  GRADINGMENUJS
     my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'});      my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'});
     my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});      my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});
   
       # Preselect sections
       my $selsec="";
       if (ref($sections)) {
           foreach my $section (sort(@$sections)) {
               $selsec.='<option value="'.$section.'" '.
                   ($saveSec eq $section ? 'selected="selected"':'').'>'.$section.'</option>'."\n";
           }
       }
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
  '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".   '<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".
Line 6936  GRADINGMENUJS Line 8245  GRADINGMENUJS
  '<input type="hidden" name="gradingMenu" value="1" />'."\n".   '<input type="hidden" name="gradingMenu" value="1" />'."\n".
  '<input type="hidden" name="showgrading" value="yes" />'."\n";   '<input type="hidden" name="showgrading" value="yes" />'."\n";
   
     $result.='<table width="100%" border="0"><tr><td bgcolor=#777777>'."\n".      $result.='
  '<table width="100%" border="0"><tr bgcolor="#e6ffff"><td colspan="2">'."\n".  <h2>
  '&nbsp;<b>Select a Grading/Viewing Option</b></td></tr>'."\n".    '.&mt('Grade Current Resource').'
  '<tr bgcolor="#ffffe6" valign="top"><td>'."\n";  </h2>
   <div>
     $result.='<table width="100%" border="0">';    '.$table.'
     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n".  </div>
  '&nbsp;'.&mt('Select Section').': <select name="section" multiple="multiple" size="3">'."\n";  
     if (ref($sections)) {  
  foreach (sort (@$sections)) {  
     $result.='<option value="'.$_.'" '.  
  ($saveSec eq $_ ? 'selected="selected"':'').'>'.$_.'</option>'."\n";  
  }  
     }  
     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="selected"' : ''). '>all</option></select> &nbsp; ';  
   
     $result.=&mt('Student Status').':'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);  <div class="LC_columnSection">
     
     $result.='</td></tr>';      <fieldset>
         <legend>
          '.&mt('Sections').'
         </legend>
         <select name="section" multiple="multiple" size="5">'."\n";
       $result.= $selsec;
       $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="selected"' : ''). '>all</option></select> &nbsp; ';
       $result.='
       </fieldset>
     
       <fieldset>
         <legend>
           '.&mt('Groups').'
         </legend>
         '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'
       </fieldset>
     
       <fieldset>
         <legend>
           '.&mt('Access Status').'
         </legend>
         '.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,5,undef,'mult').'
       </fieldset>
     
       <fieldset>
         <legend>
           '.&mt('Submission Status').'
         </legend>
         <select name="submitonly" size="5">
            <option value="yes" '.      ($saveSub eq 'yes'       ? 'selected="selected"' : '').'>'.&mt('with submissions').'</option>
            <option value="queued" '.   ($saveSub eq 'queued'    ? 'selected="selected"' : '').'>'.&mt('in grading queue').'</option>
            <option value="graded" '.   ($saveSub eq 'graded'    ? 'selected="selected"' : '').'>'.&mt('with ungraded submissions').'</option>
            <option value="incorrect" '.($saveSub eq 'incorrect' ? 'selected="selected"' : '').'>'.&mt('with incorrect submissions').'</option>
                    <option value="all" '.      ($saveSub eq 'all'       ? 'selected="selected"' : '').'>'.&mt('with any status').'</option>
         </select>
       </fieldset>
     
   </div>
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td><label>'.  <br />
  '<input type="radio" name="radioChoice" value="submission" '.            <div>
  ($saveCmd eq 'submission' ? 'checked="checked"' : '').' /> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').              <div>
  '</label> <select name="submitonly">'.                <label>
  '<option value="yes" '.                  <input type="radio" name="radioChoice" value="submission" '.
  ($saveSub eq 'yes' ? 'selected="selected"' : '').'>'.&mt('with submissions').'</option>'.                    ($saveCmd eq 'submission' ? 'checked="checked"' : '').' /> '.
  '<option value="queued" '.               &mt('Select individual students to grade and view submissions.').'
  ($saveSub eq 'queued' ? 'selected="selected"' : '').'>'.&mt('in grading queue').'</option>'.        </label> 
  '<option value="graded" '.              </div>
  ($saveSub eq 'graded' ? 'selected="selected"' : '').'>'.&mt('with ungraded submissions').'</option>'.              <div>
  '<option value="incorrect" '.        <label>
  ($saveSub eq 'incorrect' ? 'selected="selected"' : '').'>'.&mt('with incorrect submissions').'</option>'.                  <input type="radio" name="radioChoice" value="viewgrades" '.
  '<option value="all" '.                    ($saveCmd eq 'viewgrades' ? 'checked="checked"' : '').' /> '.
  ($saveSub eq 'all' ? 'selected="selected"' : '').'>'.&mt('with any status').'</option></select></td></tr>'."\n";                      &mt('Grade all selected students in a grading table.').'
                 </label>
     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.              </div>
  '<label><input type="radio" name="radioChoice" value="viewgrades" '.              <div>
  ($saveCmd eq 'viewgrades' ? 'checked="checked"' : '').' /> '.        <input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next').' &rarr;" />
  '<b>Current Resource:</b> For all students in selected section or course</label></td></tr>'."\n";              </div>
             </div>
     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'.  
  '<label><input type="radio" name="radioChoice" value="pickStudentPage" '.  
  ($saveCmd eq 'pickStudentPage' ? 'checked="checked"' : '').' /> '.          <h2>
  'The <b>complete</b> set/page/sequence: For one student</label></td></tr>'."\n";           '.&mt('Grade Complete Folder for One Student').'
           </h2>
     $result.='<tr bgcolor="#ffffe6"><td><br />'.          <div>
  '<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="Next->" />'.              <div>
  '</td></tr></table>'."\n";                <label>
                   <input type="radio" name="radioChoice" value="pickStudentPage" '.
     $result.='</td><td valign="top">';    ($saveCmd eq 'pickStudentPage' ? 'checked="checked"' : '').' /> '.
     &mt('The <b>complete</b> page/sequence/folder: For one student').'
     $result.='<table width="100%" border="0">';                </label>
     $result.='<tr bgcolor="#ffffe6"><td>'.              </div>
  '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.              <div>
  ' '.&mt('scores from file').' </td></tr>'."\n";        <input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next').' &rarr;" />
               </div>
     $result.='<tr bgcolor="#ffffe6"><td>'.          </div>
         '<input type="button" onClick="javascript:checkChoice(this.form,\'6\',\'processclicker\');" value="'.&mt('Process').'" />'.    </form>';
         ' '.&mt('clicker file').' </td></tr>'."\n";      $result .= &show_grading_menu_form($symb);
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.  
  '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.  
  '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";  
   
     if ((&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) && ($symb)) {  
  $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.  
     '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="'.&mt('Verify').'" />'.  
     ' '.&mt('receipt').': '.  
     &Apache::lonnet::recprefix($env{'request.course.id'}).  
     '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')" />'.  
     '</td></tr>'."\n";  
     }   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.  
  '<input type="button" onClick="javascript:this.form.action=\'/adm/helper/resettimes.helper\';this.form.submit();'.  
  '" value="'.&mt('Manage').'" /> access times.</td></tr>'."\n";  
     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.  
  '<input type="button" onClick="javascript:this.form.command.value=\'codelist\';this.form.action=\'/adm/pickcode\';this.form.submit();'.  
  '" value="'.&mt('View').'" /> saved CODEs.</td></tr>'."\n";  
   
     $result.='</table>'."\n".  
  '</td></tr></table>'."\n".  
  '</td></tr></table></form>'."\n";  
     return $result;      return $result;
 }  }
   
Line 7051  sub gather_clicker_ids { Line 8366  sub gather_clicker_ids {
     # Set up a couple variables.      # Set up a couple variables.
     my $username_idx = &Apache::loncoursedata::CL_SNAME();      my $username_idx = &Apache::loncoursedata::CL_SNAME();
     my $domain_idx   = &Apache::loncoursedata::CL_SDOM();      my $domain_idx   = &Apache::loncoursedata::CL_SDOM();
       my $status_idx   = &Apache::loncoursedata::CL_STATUS();
   
     foreach my $student (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
           if ($classlist->{$student}->[$status_idx] ne 'Active') { next; }
         my $username = $classlist->{$student}->[$username_idx];          my $username = $classlist->{$student}->[$username_idx];
         my $domain   = $classlist->{$student}->[$domain_idx];          my $domain   = $classlist->{$student}->[$domain_idx];
         my $clickers =          my $clickers =
Line 7113  sub process_clicker { Line 8429  sub process_clicker {
     $result.=$table;      $result.=$table;
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";      $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";      $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
     $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource').      $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource.').
         '.</b></td></tr>'."\n";          '</b></td></tr>'."\n";
     $result.='<tr bgcolor=#ffffe6><td>'."\n";      $result.='<tr bgcolor=#ffffe6><td>'."\n";
 # Attempt to restore parameters from last session, set defaults if not present  # Attempt to restore parameters from last session, set defaults if not present
     my %Saveable_Parameters=&clicker_grading_parameters();      my %Saveable_Parameters=&clicker_grading_parameters();
Line 7126  sub process_clicker { Line 8442  sub process_clicker {
     if (!$env{'form.upfiletype'}) { $env{'form.upfiletype'}='iclicker'; }      if (!$env{'form.upfiletype'}) { $env{'form.upfiletype'}='iclicker'; }
   
     my %checked;      my %checked;
     foreach my $gradingmechanism ('attendance','personnel','specific') {      foreach my $gradingmechanism ('attendance','personnel','specific','given') {
        if ($env{'form.gradingmechanism'} eq $gradingmechanism) {         if ($env{'form.gradingmechanism'} eq $gradingmechanism) {
           $checked{$gradingmechanism}="checked='checked'";            $checked{$gradingmechanism}="checked='checked'";
        }         }
Line 7137  sub process_clicker { Line 8453  sub process_clicker {
     my $attendance=&mt("Award points just for participation");      my $attendance=&mt("Award points just for participation");
     my $personnel=&mt("Correctness determined from response by course personnel");      my $personnel=&mt("Correctness determined from response by course personnel");
     my $specific=&mt("Correctness determined from response with clicker ID(s)");       my $specific=&mt("Correctness determined from response with clicker ID(s)"); 
       my $given=&mt("Correctness determined from given list of answers").' '.
                 '<font size="-2"><tt>('.&mt("Provide comma-separated list. Use '*' for any answer correct, '-' for skip").')</tt></font>';
     my $pcorrect=&mt("Percentage points for correct solution");      my $pcorrect=&mt("Percentage points for correct solution");
     my $pincorrect=&mt("Percentage points for incorrect solution");      my $pincorrect=&mt("Percentage points for incorrect solution");
     my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',      my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',
Line 7190  function sanitycheck() { Line 8508  function sanitycheck() {
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 <input type="file" name="upfile" size="50" />  <input type="file" name="upfile" size="50" />
 <br /><label>$type: $selectform</label>  <br /><label>$type: $selectform</label>
 <br /><label>$attendance: <input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" /></label>  <br /><label><input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" />$attendance </label>
 <br /><label>$personnel: <input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" /></label>  <br /><label><input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" />$personnel</label>
 <br /><label>$specific: <input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" /></label>  <br /><label><input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" />$specific </label>
 <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />  <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />
   <br /><label><input type="radio" name="gradingmechanism" value="given" $checked{'given'} onClick="sanitycheck()" />$given </label>
   <br />&nbsp;&nbsp;&nbsp;
   <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />  <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
 <br /><label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onChange="sanitycheck()" /></label>  <br /><label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onChange="sanitycheck()" /></label>
 <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onChange="sanitycheck()" /></label>  <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onChange="sanitycheck()" /></label>
Line 7220  sub process_clicker_file { Line 8541  sub process_clicker_file {
  $result.='<span class="LC_error">'.&mt('You need to specify a clicker ID for the correct answer').'</span>';   $result.='<span class="LC_error">'.&mt('You need to specify a clicker ID for the correct answer').'</span>';
  return $result.&show_grading_menu_form($symb);   return $result.&show_grading_menu_form($symb);
     }      }
       if (($env{'form.gradingmechanism'} eq 'given') && ($env{'form.givenanswer'}!~/\S/)) {
           $result.='<span class="LC_error">'.&mt('You need to specify the correct answer').'</span>';
           return $result.&show_grading_menu_form($symb);
       }
       my $foundgiven=0;
       if ($env{'form.gradingmechanism'} eq 'given') {
           $env{'form.givenanswer'}=~s/^\s*//gs;
           $env{'form.givenanswer'}=~s/\s*$//gs;
           $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-]+/\,/g;
           $env{'form.givenanswer'}=uc($env{'form.givenanswer'});
           my @answers=split(/\,/,$env{'form.givenanswer'});
           $foundgiven=$#answers+1;
       }
     my %clicker_ids=&gather_clicker_ids();      my %clicker_ids=&gather_clicker_ids();
     my %correct_ids;      my %correct_ids;
     if ($env{'form.gradingmechanism'} eq 'personnel') {      if ($env{'form.gradingmechanism'} eq 'personnel') {
Line 7238  sub process_clicker_file { Line 8572  sub process_clicker_file {
     }      }
     if ($env{'form.gradingmechanism'} eq 'attendance') {      if ($env{'form.gradingmechanism'} eq 'attendance') {
  $result.=&mt('Score based on attendance only');   $result.=&mt('Score based on attendance only');
       } elsif ($env{'form.gradingmechanism'} eq 'given') {
           $result.=&mt('Score based on [_1] ([_2] answers)','<tt>'.$env{'form.givenanswer'}.'</tt>',$foundgiven);
     } else {      } else {
  my $number=0;   my $number=0;
  $result.='<p><b>'.&mt('Correctness determined by the following IDs').'</b>';   $result.='<p><b>'.&mt('Correctness determined by the following IDs').'</b>';
Line 7283  sub process_clicker_file { Line 8619  sub process_clicker_file {
 <input type="hidden" name="pcorrect" value="$env{'form.pcorrect'}" />  <input type="hidden" name="pcorrect" value="$env{'form.pcorrect'}" />
 <input type="hidden" name="pincorrect" value="$env{'form.pincorrect'}" />  <input type="hidden" name="pincorrect" value="$env{'form.pincorrect'}" />
 ENDHEADER  ENDHEADER
       if ($env{'form.gradingmechanism'} eq 'given') {
          $result.='<input type="hidden" name="correct:given" value="'.$env{'form.givenanswer'}.'" />';
       } 
     my %responses;      my %responses;
     my @questiontitles;      my @questiontitles;
     my $errormsg='';      my $errormsg='';
Line 7298  ENDHEADER Line 8637  ENDHEADER
              &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',               &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',
                  $env{'form.pcorrect'},$env{'form.pincorrect'}).                   $env{'form.pcorrect'},$env{'form.pincorrect'}).
              '<br />';               '<br />';
       if (($env{'form.gradingmechanism'} eq 'given') && ($number!=$foundgiven)) {
          $result.='<span class="LC_error">'.&mt('Number of given answers does not agree with number of questions in file.').'</span>';
          return $result.&show_grading_menu_form($symb);
       } 
 # Remember Question Titles  # Remember Question Titles
 # FIXME: Possibly need delimiter other than ":"  # FIXME: Possibly need delimiter other than ":"
     for (my $i=0;$i<$number;$i++) {      for (my $i=0;$i<$number;$i++) {
Line 7314  ENDHEADER Line 8657  ENDHEADER
           $result.="\n".'<input type="hidden" name="correct:'.$correct_count.':'.$correct_ids{$id}.'" value="'.$responses{$id}.'" />';            $result.="\n".'<input type="hidden" name="correct:'.$correct_count.':'.$correct_ids{$id}.'" value="'.$responses{$id}.'" />';
           $correct_count++;            $correct_count++;
        } elsif ($clicker_ids{$id}) {         } elsif ($clicker_ids{$id}) {
           $result.="\n".'<input type="hidden" name="student:'.$clicker_ids{$id}.'" value="'.$responses{$id}.'" />';            if ($clicker_ids{$id}=~/\,/) {
           $student_count++;  # More than one user with the same clicker!
                $result.="\n<hr />".&mt('Clicker registered more than once').": <tt>".$id."</tt><br />";
                $result.="\n".'<input type="hidden" name="unknown:'.$id.'" value="'.$responses{$id}.'" />'.
                              "<select name='multi".$id."'>";
                foreach my $reguser (sort(split(/\,/,$clicker_ids{$id}))) {
                    $result.="<option value='".$reguser."'>".&Apache::loncommon::plainname(split(/\:/,$reguser)).' ('.$reguser.')</option>';
                }
                $result.='</select>';
                $unknown_count++;
             } else {
   # Good: found one and only one user with the right clicker
                $result.="\n".'<input type="hidden" name="student:'.$clicker_ids{$id}.'" value="'.$responses{$id}.'" />';
                $student_count++;
             }
        } else {         } else {
           $result.="\n<hr />".&mt('Unregistered Clicker')." <tt>".$id."</tt><br />";            $result.="\n<hr />".&mt('Unregistered Clicker')." <tt>".$id."</tt><br />";
           $result.="\n".'<input type="hidden" name="unknown:'.$id.'" value="'.$responses{$id}.'" />'.            $result.="\n".'<input type="hidden" name="unknown:'.$id.'" value="'.$responses{$id}.'" />'.
Line 7328  ENDHEADER Line 8684  ENDHEADER
     }      }
     $result.='<hr />'.      $result.='<hr />'.
              &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') {      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 answers for grading!";
        } elsif ($correct_count>1) {         } elsif ($correct_count>1) {
Line 7399  sub interwrite_eval { Line 8755  sub interwrite_eval {
         $id=~s/[\-\:]//g;          $id=~s/[\-\:]//g;
         $idresponses{$id}[$number]=$entries[6];          $idresponses{$id}[$number]=$entries[6];
     }      }
     foreach my $id (keys %idresponses) {      foreach my $id (keys(%idresponses)) {
        $$responses{$id}=join(',',@{$idresponses{$id}});         $$responses{$id}=join(',',@{$idresponses{$id}});
        $$responses{$id}=~s/^\s*\,//;         $$responses{$id}=~s/^\s*\,//;
     }      }
Line 7466  ENDHEADER Line 8822  ENDHEADER
           my $id=$1;            my $id=$1;
           if (($env{'form.uname'.$id}) && ($env{'form.udom'.$id})) {            if (($env{'form.uname'.$id}) && ($env{'form.udom'.$id})) {
              $user=$env{'form.uname'.$id}.':'.$env{'form.udom'.$id};               $user=$env{'form.uname'.$id}.':'.$env{'form.udom'.$id};
             } elsif ($env{'form.multi'.$id}) {
                $user=$env{'form.multi'.$id};
           }            }
        }         }
        if ($user) {          if ($user) { 
           my @answer=split(/\,/,$env{$key});            my @answer=split(/\,/,$env{$key});
           my $sum=0;            my $sum=0;
             my $realnumber=$number;
           for (my $i=0;$i<$number;$i++) {            for (my $i=0;$i<$number;$i++) {
              if ($answer[$i]) {               if ($answer[$i]) {
                 if ($gradingmechanism eq 'attendance') {                  if ($gradingmechanism eq 'attendance') {
                    $sum+=$pcorrect;                     $sum+=$pcorrect;
                   } elsif ($answer[$i] eq '*') {
                      $sum+=$pcorrect;
                   } elsif ($answer[$i] eq '-') {
                      $realnumber--;
                 } else {                  } else {
                    if ($answer[$i] eq $correct[$i]) {                     if ($answer[$i] eq $correct[$i]) {
                       $sum+=$pcorrect;                        $sum+=$pcorrect;
Line 7484  ENDHEADER Line 8847  ENDHEADER
                 }                  }
              }               }
           }            }
           my $ave=$sum/(100*$number);            my $ave=$sum/(100*$realnumber);
 # Store  # Store
           my ($username,$domain)=split(/\:/,$user);            my ($username,$domain)=split(/\:/,$user);
           my %grades=();            my %grades=();
Line 7502  ENDHEADER Line 8865  ENDHEADER
        }         }
     }      }
 # We are done  # We are done
     $result.='<br />'.&mt('Successfully stored grades for [_1] student(s).',$storecount).      $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).
              '</td></tr></table>'."\n".               '</td></tr></table>'."\n".
              '</td></tr></table><br /><br />'."\n";               '</td></tr></table><br /><br />'."\n";
     return $result.&show_grading_menu_form($symb);      return $result.&show_grading_menu_form($symb);
Line 7510  ENDHEADER Line 8873  ENDHEADER
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
       &reset_caches();
     &reset_perm();  
     if ($env{'browser.mathml'}) {      if ($env{'browser.mathml'}) {
  &Apache::loncommon::content_type($request,'text/xml');   &Apache::loncommon::content_type($request,'text/xml');
     } else {      } else {
Line 7523  sub handler { Line 8885  sub handler {
     my $symb=&get_symb($request,1);      my $symb=&get_symb($request,1);
     my @commands=&Apache::loncommon::get_env_multiple('form.command');      my @commands=&Apache::loncommon::get_env_multiple('form.command');
     my $command=$commands[0];      my $command=$commands[0];
   
     if ($#commands > 0) {      if ($#commands > 0) {
  &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));   &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
     }      }
     $request->print(&Apache::loncommon::start_page('Grading'));  
       $ssi_error = 0;
       my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];
       $request->print(&Apache::loncommon::start_page('Grading',undef,
                                             {'bread_crumbs' => $brcrum}));
     if ($symb eq '' && $command eq '') {      if ($symb eq '' && $command eq '') {
  if ($env{'user.adv'}) {   if ($env{'user.adv'}) {
     if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&      if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&
Line 7538  sub handler { Line 8905  sub handler {
  if ($tsymb) {   if ($tsymb) {
     my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb);      my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb);
     if (&Apache::lonnet::allowed('mgr',$tcrsid)) {      if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
  $request->print(&Apache::lonnet::ssi_body('/res/'.$url,   $request->print(&ssi_with_retries('/res/'.$url, $ssi_retries,
   ('grade_username' => $tuname,    ('grade_username' => $tuname,
    'grade_domain' => $tudom,     'grade_domain' => $tudom,
    'grade_courseid' => $tcrsid,     'grade_courseid' => $tcrsid,
Line 7566  sub handler { Line 8933  sub handler {
  } elsif ($command eq 'processGroup' && $perm{'vgr'}) {   } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
     &processGroup($request);      &processGroup($request);
  } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {   } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
     $request->print(&gradingmenu($request));      $request->print(&grading_menu($request));
    } elsif ($command eq 'submit_options' && $perm{'vgr'}) {
       $request->print(&submit_options($request));
  } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {   } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {
     $request->print(&viewgrades($request));      $request->print(&viewgrades($request));
  } elsif ($command eq 'handgrade' && $perm{'mgr'}) {   } elsif ($command eq 'handgrade' && $perm{'mgr'}) {
Line 7619  sub handler { Line 8988  sub handler {
   } elsif ($command eq 'scantron_download' &&    } elsif ($command eq 'scantron_download' &&
  &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {   &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
      $request->print(&scantron_download_scantron_data($request));       $request->print(&scantron_download_scantron_data($request));
           } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
               $request->print(&checkscantron_results($request));     
  } elsif ($command) {   } elsif ($command) {
     $request->print("Access Denied ($command)");      $request->print("Access Denied ($command)");
  }   }
     }      }
       if ($ssi_error) {
    &ssi_print_error($request);
       }
     $request->print(&Apache::loncommon::end_page());      $request->print(&Apache::loncommon::end_page());
       &reset_caches();
     return '';      return '';
 }  }
   
 1;  1;
   
 __END__;  __END__;
   
   
   =head1 NAME
   
   Apache::grades
   
   =head1 SYNOPSIS
   
   Handles the viewing of grades.
   
   This is part of the LearningOnline Network with CAPA project
   described at http://www.lon-capa.org.
   
   =head1 OVERVIEW
   
   Do an ssi with retries:
   While I'd love to factor out this with the vesrion in lonprintout,
   that would either require a data coupling between modules, which I refuse to perpetuate (there's quite enough of that already), or would require the invention of another infrastructure
   I'm not quite ready to invent (e.g. an ssi_with_retry object).
   
   At least the logic that drives this has been pulled out into loncommon.
   
   
   
   ssi_with_retries - Does the server side include of a resource.
                        if the ssi call returns an error we'll retry it up to
                        the number of times requested by the caller.
                        If we still have a proble, no text is appended to the
                        output and we set some global variables.
                        to indicate to the caller an SSI error occurred.  
                        All of this is supposed to deal with the issues described
                        in LonCAPA BZ 5631 see:
                        http://bugs.lon-capa.org/show_bug.cgi?id=5631
                        by informing the user that this happened.
   
   Parameters:
     resource   - The resource to include.  This is passed directly, without
                  interpretation to lonnet::ssi.
     form       - The form hash parameters that guide the interpretation of the resource
                  
     retries    - Number of retries allowed before giving up completely.
   Returns:
     On success, returns the rendered resource identified by the resource parameter.
   Side Effects:
     The following global variables can be set:
      ssi_error                - If an unrecoverable error occurred this becomes true.
                                 It is up to the caller to initialize this to false
                                 if desired.
      ssi_error_resource  - If an unrecoverable error occurred, this is the value
                                 of the resource that could not be rendered by the ssi
                                 call.
      ssi_error_message   - The error string fetched from the ssi response
                                 in the event of an error.
   
   
   =head1 HANDLER SUBROUTINE
   
   ssi_with_retries()
   
   =head1 SUBROUTINES
   
   =over
   
   =item scantron_get_correction() : 
   
      Builds the interface screen to interact with the operator to fix a
      specific error condition in a specific scanline
   
    Arguments:
       $r           - Apache request object
       $i           - number of the current scanline
       $scan_record - hash ref as returned from &scantron_parse_scanline()
       $scan_config - hash ref as returned from &get_scantron_config()
       $line        - full contents of the current scanline
       $error       - error condition, valid values are
                      'incorrectCODE', 'duplicateCODE',
                      'doublebubble', 'missingbubble',
                      'duplicateID', 'incorrectID'
       $arg         - extra information needed
          For errors:
            - duplicateID   - paper number that this studentID was seen before on
            - duplicateCODE - array ref of the paper numbers this CODE was
                              seen on before
            - incorrectCODE - current incorrect CODE 
            - doublebubble  - array ref of the bubble lines that have double
                              bubble errors
            - missingbubble - array ref of the bubble lines that have missing
                              bubble errors
   
   =item  scantron_get_maxbubble() : 
   
      Returns the maximum number of bubble lines that are expected to
      occur. Does this by walking the selected sequence rendering the
      resource and then checking &Apache::lonxml::get_problem_counter()
      for what the current value of the problem counter is.
   
      Caches the results to $env{'form.scantron_maxbubble'},
      $env{'form.scantron.bubble_lines.n'}, 
      $env{'form.scantron.first_bubble_line.n'} and
      $env{"form.scantron.sub_bubblelines.n"}
      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,
      and a comma separated list of numbers of bubble lines for sub-questions
      (for optionresponse, matchresponse, and rankresponse items), for response n.  
   
   
   =item  scantron_validate_missingbubbles() : 
   
      Validates all scanlines in the selected file to not have any
       answers that don't have bubbles that have not been verified
       to be bubble free.
   
   =item  scantron_process_students() : 
   
      Routine that does the actual grading of the bubble sheet information.
   
      The parsed scanline hash is added to %env 
   
      Then foreach unskipped scanline it does an &Apache::lonnet::ssi()
      foreach resource , with the form data of
   
    'submitted'     =>'scantron' 
    'grade_target'  =>'grade',
    'grade_username'=> username of student
    'grade_domain'  => domain of student
    'grade_courseid'=> of course
    'grade_symb'    => symb of resource to grade
   
       This triggers a grading pass. The problem grading code takes care
       of converting the bubbled letter information (now in %env) into a
       valid submission.
   
   =item  scantron_upload_scantron_data() :
   
       Creates the screen for adding a new bubble sheet data file to a course.
   
   =item  scantron_upload_scantron_data_save() : 
   
      Adds a provided bubble information data file to the course if user
      has the correct privileges to do so. 
   
   =item  valid_file() :
   
      Validates that the requested bubble data file exists in the course.
   
   =item  scantron_download_scantron_data() : 
   
      Shows a list of the three internal files (original, corrected,
      skipped) for a specific bubble sheet data file that exists in the
      course.
   
   =item  scantron_validate_ID() : 
   
      Validates all scanlines in the selected file to not have any
      invalid or underspecified student IDs
   
   =back
   
   =cut

Removed from v.1.432  
changed lines
  Added in v.1.551


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