Diff for /loncom/homework/grades.pm between versions 1.617 and 1.749

version 1.617, 2010/04/13 16:12:54 version 1.749, 2017/12/31 14:00:41
Line 40  use Apache::lonhomework; Line 40  use Apache::lonhomework;
 use Apache::lonpickcode;  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 :http);
 use Apache::lonlocal;  use Apache::lonlocal;
 use Apache::lonenc;  use Apache::lonenc;
   use Apache::lonstathelpers;
   use Apache::lonquickgrades;
   use Apache::bridgetask();
 use String::Similarity;  use String::Similarity;
 use LONCAPA;  use LONCAPA;
   
Line 51  use POSIX qw(floor); Line 54  use POSIX qw(floor);
   
   
 my %perm=();  my %perm=();
   my %old_essays=();
   
 #  These variables are used to recover from ssi errors  #  These variables are used to recover from ssi errors
   
Line 112  sub getpartlist { Line 116  sub getpartlist {
     my $res      = $navmap->getBySymb($symb);      my $res      = $navmap->getBySymb($symb);
     my $partlist = $res->parts();      my $partlist = $res->parts();
     my $url      = $res->src();      my $url      = $res->src();
     my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));      my $toolsymb;
       if ($url =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
       my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys',$toolsymb));
   
     my @stores;      my @stores;
     foreach my $part (@{ $partlist }) {      foreach my $part (@{ $partlist }) {
Line 137  sub nameUserString { Line 145  sub nameUserString {
   
 #--- Get the partlist and the response type for a given problem. ---  #--- Get the partlist and the response type for a given problem. ---
 #--- Indicate if a response type is coded handgraded or not. ---  #--- Indicate if a response type is coded handgraded or not. ---
   #--- Sets response_error pointer to "1" if navmaps object broken ---
 sub response_type {  sub response_type {
     my ($symb,$response_error) = @_;      my ($symb,$response_error) = @_;
   
Line 198  sub get_display_part { Line 207  sub get_display_part {
 sub reset_caches {  sub reset_caches {
     &reset_analyze_cache();      &reset_analyze_cache();
     &reset_perm();      &reset_perm();
       &reset_old_essays();
 }  }
   
 {  {
Line 210  sub reset_caches { Line 220  sub reset_caches {
     }      }
   
     sub get_analyze {      sub get_analyze {
  my ($symb,$uname,$udom,$no_increment,$add_to_hash)=@_;   my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed,$bubbles_per_row)=@_;
  my $key = "$symb\0$uname\0$udom";   my $key = "$symb\0$uname\0$udom";
           if ($type eq 'randomizetry') {
               if ($trial ne '') {
                   $key .= "\0".$trial;
               }
           }
  if (exists($analyze_cache{$key})) {   if (exists($analyze_cache{$key})) {
             my $getupdate = 0;              my $getupdate = 0;
             if (ref($add_to_hash) eq 'HASH') {              if (ref($add_to_hash) eq 'HASH') {
Line 239  sub reset_caches { Line 254  sub reset_caches {
                     'grade_courseid'    =>  $env{'request.course.id'},                      'grade_courseid'    =>  $env{'request.course.id'},
                     'grade_username'    => $uname,                      'grade_username'    => $uname,
                     'grade_noincrement' => $no_increment);                      'grade_noincrement' => $no_increment);
           if ($bubbles_per_row ne '') {
               $form{'bubbles_per_row'} = $bubbles_per_row;
           }
           if ($type eq 'randomizetry') {
               $form{'grade_questiontype'} = $type;
               if ($rndseed ne '') {
                   $form{'grade_rndseed'} = $rndseed;
               }
           }
         if (ref($add_to_hash)) {          if (ref($add_to_hash)) {
             %form = (%form,%{$add_to_hash});              %form = (%form,%{$add_to_hash});
         }           }
  my $subresult=&ssi_with_retries($url, $ssi_retries,%form);   my $subresult=&ssi_with_retries($url, $ssi_retries,%form);
  (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);   (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
  my %analyze=&Apache::lonnet::str2hash($subresult);   my %analyze=&Apache::lonnet::str2hash($subresult);
Line 254  sub reset_caches { Line 278  sub reset_caches {
     }      }
   
     sub get_order {      sub get_order {
  my ($partid,$respid,$symb,$uname,$udom,$no_increment)=@_;   my ($partid,$respid,$symb,$uname,$udom,$no_increment,$type,$trial,$rndseed)=@_;
  my $analyze = &get_analyze($symb,$uname,$udom,$no_increment);   my $analyze = &get_analyze($symb,$uname,$udom,$no_increment,undef,$type,$trial,$rndseed);
  return $analyze->{"$partid.$respid.shown"};   return $analyze->{"$partid.$respid.shown"};
     }      }
   
     sub get_radiobutton_correct_foil {      sub get_radiobutton_correct_foil {
  my ($partid,$respid,$symb,$uname,$udom)=@_;   my ($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed)=@_;
  my $analyze = &get_analyze($symb,$uname,$udom);   my $analyze = &get_analyze($symb,$uname,$udom,undef,undef,$type,$trial,$rndseed);
         my $foils = &get_order($partid,$respid,$symb,$uname,$udom);          my $foils = &get_order($partid,$respid,$symb,$uname,$udom,undef,$type,$trial,$rndseed);
         if (ref($foils) eq 'ARRAY') {          if (ref($foils) eq 'ARRAY') {
     foreach my $foil (@{$foils}) {      foreach my $foil (@{$foils}) {
         if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {          if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
Line 273  sub reset_caches { Line 297  sub reset_caches {
     }      }
   
     sub scantron_partids_tograde {      sub scantron_partids_tograde {
         my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_;          my ($resource,$cid,$uname,$udom,$check_for_randomlist,$bubbles_per_row,$scancode) = @_;
         my (%analysis,@parts);          my (%analysis,@parts);
         if (ref($resource)) {          if (ref($resource)) {
             my $symb = $resource->symb();              my $symb = $resource->symb();
Line 281  sub reset_caches { Line 305  sub reset_caches {
             if ($check_for_randomlist) {              if ($check_for_randomlist) {
                 $add_to_form = { 'check_parts_withrandomlist' => 1,};                  $add_to_form = { 'check_parts_withrandomlist' => 1,};
             }              }
             my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form);              if ($scancode) {
                   if (ref($add_to_form) eq 'HASH') {
                       $add_to_form->{'code_for_randomlist'} = $scancode;
                   } else {
                       $add_to_form = { 'code_for_randomlist' => $scancode,};
                   }
               }
               my $analyze = 
                   &get_analyze($symb,$uname,$udom,undef,$add_to_form,
                                undef,undef,undef,$bubbles_per_row);
             if (ref($analyze) eq 'HASH') {              if (ref($analyze) eq 'HASH') {
                 %analysis = %{$analyze};                  %analysis = %{$analyze};
             }              }
Line 304  sub reset_caches { Line 337  sub reset_caches {
 #        response types only.  #        response types only.
 sub cleanRecord {  sub cleanRecord {
     my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,      my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,
  $uname,$udom) = @_;   $uname,$udom,$type,$trial,$rndseed) = @_;
     my $grayFont = '<span class="LC_internal_info">';      my $grayFont = '<span class="LC_internal_info">';
     if ($response =~ /^(option|rank)$/) {      if ($response =~ /^(option|rank)$/) {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
           my @answer = %answer;
           %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
  my ($toprow,$bottomrow);   my ($toprow,$bottomrow);
  foreach my $foil (@$order) {   foreach my $foil (@$order) {
Line 321  sub cleanRecord { Line 356  sub cleanRecord {
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.      '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
     $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';      $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'match') {      } elsif ($response eq 'match') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
           my @answer = %answer;
           %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
  my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});   my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
  my ($toprow,$middlerow,$bottomrow);   my ($toprow,$middlerow,$bottomrow);
Line 343  sub cleanRecord { Line 380  sub cleanRecord {
     '<tr valign="top"><td>'.$grayFont.&mt('Item ID').'</span></td>'.      '<tr valign="top"><td>'.$grayFont.&mt('Item ID').'</span></td>'.
     $middlerow.'</tr>'.      $middlerow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.&mt('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 @answer = %answer;
           %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;
  my ($toprow,$bottomrow);   my ($toprow,$bottomrow);
  my $correct =    my $correct = 
     &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom);      &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed);
  foreach my $foil (@$order) {   foreach my $foil (@$order) {
     if (exists($answer{$foil})) {      if (exists($answer{$foil})) {
  if ($foil eq $correct) {   if ($foil eq $correct) {
Line 364  sub cleanRecord { Line 403  sub cleanRecord {
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.&mt('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 'essay') {      } elsif ($response eq 'essay') {
  if (! exists ($env{'form.'.$symb})) {   if (! exists ($env{'form.'.$symb})) {
     my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',      my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
Line 378  sub cleanRecord { Line 417  sub cleanRecord {
     $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';      $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
     $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.      $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
  }   }
  $answer =~ s-\n-<br />-g;  
  return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';   return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
   
     } elsif ( $response eq 'organic') {      } elsif ( $response eq 'organic') {
  my $result='Smile representation: "<tt>'.$answer.'</tt>"';          my $result=&mt('Smile representation: [_1]',
                              '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');
  my $jme=$record->{$version."resource.$partid.$respid.molecule"};   my $jme=$record->{$version."resource.$partid.$respid.molecule"};
  $result.=&Apache::chemresponse::jme_img($jme,$answer,400);   $result.=&Apache::chemresponse::jme_img($jme,$answer,400);
  return $result;   return $result;
Line 415  sub cleanRecord { Line 455  sub cleanRecord {
     $result.='</ul>';      $result.='</ul>';
     return $result;      return $result;
  }   }
     } elsif ( $response =~ m/(?:numerical|formula)/) {      } elsif ( $response =~ m/(?:numerical|formula|custom)/) {
           # Respect multiple input fields, see Bug #5409
  $answer =    $answer = 
     &Apache::loncommon::format_previous_attempt_value('submission',      &Apache::loncommon::format_previous_attempt_value('submission',
       $answer);        $answer);
    return $answer;
     }      }
     return $answer;      return &HTML::Entities::encode($answer, '"<>&');
 }  }
   
 #-- A couple of common js functions  #-- A couple of common js functions
Line 661  sub compute_points { Line 703  sub compute_points {
 #  #
   
 sub most_similar {  sub most_similar {
     my ($uname,$udom,$uessay,$old_essays)=@_;      my ($uname,$udom,$symb,$uessay)=@_;
   
       unless ($symb) { return ''; }
   
       unless (ref($old_essays{$symb}) eq 'HASH') { return ''; }
   
 # ignore spaces and punctuation  # ignore spaces and punctuation
   
Line 678  sub most_similar { Line 724  sub most_similar {
     my $scrsid='';      my $scrsid='';
     my $sessay='';      my $sessay='';
 # go through all essays ...  # go through all essays ...
     foreach my $tkey (keys(%$old_essays)) {      foreach my $tkey (keys(%{$old_essays{$symb}})) {
  my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey));   my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey));
 # ... except the same student  # ... except the same student
         next if (($tname eq $uname) && ($tdom eq $udom));          next if (($tname eq $uname) && ($tdom eq $udom));
  my $tessay=$old_essays->{$tkey};   my $tessay=$old_essays{$symb}{$tkey};
  $tessay=~s/\W+/ /gs;   $tessay=~s/\W+/ /gs;
 # String similarity gives up if not even limit  # String similarity gives up if not even limit
  my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);   my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
Line 692  sub most_similar { Line 738  sub most_similar {
     $sname=$tname;      $sname=$tname;
     $sdom=$tdom;      $sdom=$tdom;
     $scrsid=$tcrsid;      $scrsid=$tcrsid;
     $sessay=$old_essays->{$tkey};      $sessay=$old_essays{$symb}{$tkey};
  }   }
     }      }
     if ($limit>0.6) {      if ($limit>0.6) {
Line 710  sub most_similar { Line 756  sub most_similar {
 sub initialverifyreceipt {  sub initialverifyreceipt {
    my ($request,$symb) = @_;     my ($request,$symb) = @_;
    &commonJSfunctions($request);     &commonJSfunctions($request);
    return '<form name="gradingMenu"><input type="submit" value="'.&mt('Verify Receipt Number.').'" />'.     return '<form name="gradingMenu" action=""><input type="submit" value="'.&mt('Verify Receipt Number.').'" />'.
         &Apache::lonnet::recprefix($env{'request.course.id'}).          &Apache::lonnet::recprefix($env{'request.course.id'}).
         '-<input type="text" name="receipt" size="4" />'.          '-<input type="text" name="receipt" size="4" />'.
         '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".          '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
Line 810  sub verifyreceipt { Line 856  sub verifyreceipt {
 sub listStudents {  sub listStudents {
     my ($request,$symb,$submitonly) = @_;      my ($request,$symb,$submitonly) = @_;
   
       my $is_tool   = ($symb =~ /ext\.tool$/);
     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'};
Line 817  sub listStudents { Line 864  sub listStudents {
     unless ($submitonly) {      unless ($submitonly) {
        $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};         $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
     }      }
     my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';  
   
     my $result='<h3><span class="LC_info">&nbsp;'      my $result='';
  .&mt("$viewgrade Submissions for a Student or a Group of Students")      my $res_error;
  .'</span></h3>';      my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
   
     my ($partlist,$handgrade,$responseType) = &response_type($symb  
 #,$res_error  
     );  
   
     my %lt = &Apache::lonlocal::texthash (      my %js_lt = &Apache::lonlocal::texthash (
  'multiple' => 'Please select a student or group of students before clicking on the Next button.',   'multiple' => 'Please select a student or group of students before clicking on the Next button.',
  'single'   => 'Please select the student before clicking on the Next button.',   'single'   => 'Please select the student before clicking on the Next button.',
      );       );
       &js_escape(\%js_lt);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));
     function checkSelect(checkBox) {      function checkSelect(checkBox) {
  var ctr=0;   var ctr=0;
Line 841  sub listStudents { Line 884  sub listStudents {
     ctr++;      ctr++;
  }   }
     }      }
     sense = '$lt{'multiple'}';      sense = '$js_lt{'multiple'}';
  } else {   } else {
     if (checkBox.checked) {      if (checkBox.checked) {
  ctr = 1;   ctr = 1;
     }      }
     sense = '$lt{'single'}';      sense = '$js_lt{'single'}';
  }   }
  if (ctr == 0) {   if (ctr == 0) {
     alert(sense);      alert(sense);
Line 865  LISTJAVASCRIPT Line 908  LISTJAVASCRIPT
     &commonJSfunctions($request);      &commonJSfunctions($request);
     $request->print($result);      $request->print($result);
   
     my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? '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";   "\n";
   
     $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();      $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))      unless ($is_tool) {
                   .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
                   .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"                        .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
                   .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"                        .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                        .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))                        .&Apache::lonhtmlcommon::row_closure();
                   .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
                   .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"                        .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
                   .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"                        .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                        .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
                         .&Apache::lonhtmlcommon::row_closure();
       }
   
     my $submission_options;      my $submission_options;
     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {  
  $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 $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;      my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;
     $env{'form.Status'} = $saveStatus;      $env{'form.Status'} = $saveStatus;
       my %optiontext;
       if ($is_tool) {
           %optiontext = &Apache::lonlocal::texthash (
                             lastonly => 'last transaction',
                             last     => 'last transaction with details',
                             datesub  => 'all transactions',
                             all      => 'all transactions with details',
                         );
       } else {
           %optiontext = &Apache::lonlocal::texthash (
                             lastonly => 'last submission',
                             last     => 'last submission with details',
                             datesub  => 'all submissions',
                             all      => 'all submissions with details',
                         );
       }
     $submission_options.=      $submission_options.=
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> '.          '<label><input type="radio" name="lastSub" value="lastonly" /> '.
         &mt('last submission only').' </label></span>'."\n".          $optiontext{'lastonly'}.' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="last" /> '.          '<label><input type="radio" name="lastSub" value="last" /> '.
         &mt('last submission &amp; parts info').' </label></span>'."\n".          $optiontext{'last'}.' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="datesub" /> '.          '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.
         &mt('by dates and submissions').'</label></span>'."\n".          $optiontext{'datesub'}.'</label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="all" /> '.          '<label><input type="radio" name="lastSub" value="all" /> '.
         &mt('all details').'</label></span>';          $optiontext{'all'}.'</label></span>';
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))      my $viewtitle;
       if ($is_tool) {
           $viewtitle = &mt('View Transactions');
       } else {
           $viewtitle = &mt('View Submissions');
       }
       $gradeTable .= &Apache::lonhtmlcommon::row_title($viewtitle)
                   .$submission_options                    .$submission_options
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure();
   
       my $closure;
       if (($is_tool) && (exists($env{'form.Status'}))) {
           $closure = 1;
       }
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
                   .'<select name="increment">'                    .'<select name="increment">'
                   .'<option value="1">'.&mt('Whole Points').'</option>'                    .'<option value="1">'.&mt('Whole Points').'</option>'
Line 914  LISTJAVASCRIPT Line 979  LISTJAVASCRIPT
                   .'<option value=".25">'.&mt('Quarter Points').'</option>'                    .'<option value=".25">'.&mt('Quarter Points').'</option>'
                   .'<option value=".1">'.&mt('Tenths of a Point').'</option>'                    .'<option value=".1">'.&mt('Tenths of a Point').'</option>'
                   .'</select>'                    .'</select>'
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure($closure);
   
     $gradeTable .=       $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="showgrading" value="'.$env{'form.showgrading'}.'" /><br />'."\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="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.Status'})) {
  $gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";   $gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
     } else {      } else {
           if ($is_tool) {
               $closure = 1;
           }
         $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))
                       .&Apache::lonhtmlcommon::StatusOptions(                        .&Apache::lonhtmlcommon::StatusOptions(
                            $saveStatus,undef,1,'javascript:reLoadList(this.form);')                             $saveStatus,undef,1,'javascript:reLoadList(this.form);')
                       .&Apache::lonhtmlcommon::row_closure();                        .&Apache::lonhtmlcommon::row_closure($closure);
     }      }
   
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))      unless ($is_tool) {
                   .'<input type="checkbox" name="checkPlag" checked="checked" />'          $closure = 1;
                   .&Apache::lonhtmlcommon::row_closure(1)          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
                   .&Apache::lonhtmlcommon::end_pick_box();                        .'<input type="checkbox" name="checkPlag" checked="checked" />'
                         .&Apache::lonhtmlcommon::row_closure($closure);
       }
       $gradeTable .= &Apache::lonhtmlcommon::end_pick_box();
       my $regrademsg;
       if ($is_tool) {
           $regrademsg =&mt("To view/grade/regrade, click on the check box(es) next to the student's name(s). Then click on the Next button.");
       } else {
           $regrademsg = &mt("To view/grade/regrade 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.");
       }
     $gradeTable .= '<p>'      $gradeTable .= '<p>'
                   .&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.")."\n"                    .$regrademsg."\n"
                   .'<input type="hidden" name="command" value="processGroup" />'                    .'<input type="hidden" name="command" value="processGroup" />'
                   .'</p>';                    .'</p>';
   
Line 956  LISTJAVASCRIPT Line 1030  LISTJAVASCRIPT
     while ($loop < 2) {      while ($loop < 2) {
  $gradeTable.='<th>'.&mt('No.').'</th><th>'.&mt('Select').'</th>'.   $gradeTable.='<th>'.&mt('No.').'</th><th>'.&mt('Select').'</th>'.
     '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>';      '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>';
  if ($env{'form.showgrading'} eq 'yes'    if (($submitonly ne 'queued') && ($submitonly ne 'all')) {
     && $submitonly ne 'queued'  
     && $submitonly ne 'all') {  
     foreach my $part (sort(@$partlist)) {      foreach my $part (sort(@$partlist)) {
  my $display_part=   my $display_part=
     &get_display_part((split(/_/,$part))[0],$symb);      &get_display_part((split(/_/,$part))[0],$symb);
Line 994  LISTJAVASCRIPT Line 1066  LISTJAVASCRIPT
     $status{'gradingqueue'} = $queue_status{'gradingqueue'};      $status{'gradingqueue'} = $queue_status{'gradingqueue'};
  }   }
   
  if ($env{'form.showgrading'} eq 'yes'    if (($submitonly ne 'queued') && ($submitonly ne 'all')) {
     && $submitonly ne 'queued'  
     && $submitonly ne 'all') {  
     (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);      (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);
     my $submitted = 0;      my $submitted = 0;
     my $graded = 0;      my $graded = 0;
Line 1037  LISTJAVASCRIPT Line 1107  LISTJAVASCRIPT
        &nameUserString(undef,$$fullname{$student},$uname,$udom).         &nameUserString(undef,$$fullname{$student},$uname,$udom).
        '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'."\n";         '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'."\n";
   
     if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {      if ($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;'.&mt($status{$_}).'&nbsp;</td>'."\n";      $gradeTable.='<td align="center">&nbsp;'.&mt($status{$_}).'&nbsp;</td>'."\n";
Line 1051  LISTJAVASCRIPT Line 1121  LISTJAVASCRIPT
     }      }
     if ($ctr%2 ==1) {      if ($ctr%2 ==1) {
  $gradeTable.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>';   $gradeTable.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>';
     if ($env{'form.showgrading'} eq 'yes'       if (($submitonly ne 'queued') && ($submitonly ne 'all')) {
  && $submitonly ne 'queued'  
  && $submitonly ne 'all') {  
  foreach (@$partlist) {   foreach (@$partlist) {
     $gradeTable.='<td>&nbsp;</td>';      $gradeTable.='<td>&nbsp;</td>';
  }   }
Line 1077  LISTJAVASCRIPT Line 1145  LISTJAVASCRIPT
     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">'.
  &mt('No '.$submissions.' found for this resource for any students. ([_1] students checked for '.$submissions.')',   &mt('No '.$submissions.' found for this resource for any students. ([quant,_1,student] checked for '.$submissions.')',
     $num_students).      $num_students).
  '</span><br />';   '</span><br />';
  }   }
Line 1137  sub check_buttons { Line 1205  sub check_buttons {
   
 #     Displays the submissions for one student or a group of students  #     Displays the submissions for one student or a group of students
 sub processGroup {  sub processGroup {
     my ($request)  = shift;      my ($request,$symb)  = @_;
     my $ctr        = 0;      my $ctr        = 0;
     my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
     my $total      = scalar(@stuchecked)-1;      my $total      = scalar(@stuchecked)-1;
Line 1147  sub processGroup { Line 1215  sub processGroup {
  $env{'form.student'}        = $uname;   $env{'form.student'}        = $uname;
  $env{'form.userdom'}        = $udom;   $env{'form.userdom'}        = $udom;
  $env{'form.fullname'}       = $fullname;   $env{'form.fullname'}       = $fullname;
  &submission($request,$ctr,$total);   &submission($request,$ctr,$total,$symb);
  $ctr++;   $ctr++;
     }      }
     return '';      return '';
Line 1161  sub processGroup { Line 1229  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 = ');      my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
       &js_escape(\$alertmsg);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
     function updateRadio(formname,id,weight) {      function updateRadio(formname,id,weight) {
  var gradeBox = formname["GD_BOX"+id];   var gradeBox = formname["GD_BOX"+id];
Line 1279  sub sub_page_js { Line 1348  sub sub_page_js {
     }      }
  }   }
     }      }
       
  }   }
     }      }
       
  }  
  if (val == "Grade Student") {  
     formname.showgrading.value = "yes";  
     if (formname.Status.value == "") {  
  formname.Status.value = "Active";  
     }  
     formname.studentNo.value = total;  
  }   }
  formname.submit();   formname.submit();
     }      }
Line 1339  sub sub_page_kw_js { Line 1399  sub sub_page_kw_js {
     my $iconpath = $request->dir_config('lonIconsURL');      my $iconpath = $request->dir_config('lonIconsURL');
     &commonJSfunctions($request);      &commonJSfunctions($request);
   
     my $inner_js_msg_central= &Apache::lonhtmlcommon::scripttag(<<INNERJS);      my $inner_js_msg_central= (<<INNERJS);
   <script type="text/javascript">
     function checkInput() {      function checkInput() {
       opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);        opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);
       var nmsg   = opener.document.SCORE.savemsgN.value;        var nmsg   = opener.document.SCORE.savemsgN.value;
Line 1376  sub sub_page_kw_js { Line 1437  sub sub_page_kw_js {
       self.close()        self.close()
   
     }      }
   </script>
 INNERJS  INNERJS
   
     my $inner_js_highlight_central= &Apache::lonhtmlcommon::scripttag(<<INNERJS);      my $inner_js_highlight_central= (<<INNERJS);
   <script type="text/javascript">
     function updateChoice(flag) {      function updateChoice(flag) {
       opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);        opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
       opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);        opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
Line 1389  INNERJS Line 1452  INNERJS
       }        }
       self.close()        self.close()
     }      }
   </script>
 INNERJS  INNERJS
   
     my $start_page_msg_central =       my $start_page_msg_central = 
Line 1411  INNERJS Line 1475  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.');      my %js_lt = &Apache::lonlocal::texthash(
                   keyw => 'Keywords list, separated by a space. Add/delete to list if desired.',
                   plse => 'Please select a word or group of words from document and then click this link.',
                   adds => 'Add selection to keyword list? Edit if desired.',
                   col1 => 'red',
                   col2 => 'green',
                   col3 => 'blue',
                   siz1 => 'normal',
                   siz2 => '+1',
                   siz3 => '+2',
                   sty1 => 'normal',
                   sty2 => 'italic',
                   sty3 => 'bold',
                );
       my %html_js_lt = &Apache::lonlocal::texthash(
                   comp => 'Compose Message for: ',
                   incl => 'Include',
                   type => 'Type',
                   subj => 'Subject',
                   mesa => 'Message',
                   new  => 'New',
                   save => 'Save',
                   canc => 'Cancel',
                   kehi => 'Keyword Highlight Options',
                   txtc => 'Text Color',
                   font => 'Font Size',
                   fnst => 'Font Style',
                );
       &js_escape(\%js_lt);
       &html_escape(\%html_js_lt);
       &js_escape(\%html_js_lt);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
   
 //===================== Show list of keywords ====================  //===================== Show list of keywords ====================
   function keywords(formname) {    function keywords(formname) {
     var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);      var nret = prompt("$js_lt{'keyw'}",formname.keywords.value);
     if (nret==null) return;      if (nret==null) return;
     formname.keywords.value = nret;      formname.keywords.value = nret;
   
Line 1443  INNERJS Line 1537  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("$alertmsg");   alert("$js_lt{'plse'}");
  return;   return;
     }      }
     var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);      var nret = prompt("$js_lt{'adds'}",cleantxt);
     if (nret==null) return;      if (nret==null) return;
     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;      document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
     if (document.SCORE.keywords.value != "") {      if (document.SCORE.keywords.value != "") {
Line 1510  INNERJS Line 1604  INNERJS
   
   function savedMsgHeader(Nmsg,usrctr,fullname) {    function savedMsgHeader(Nmsg,usrctr,fullname) {
     var height = 70*Nmsg+250;      var height = 70*Nmsg+250;
     var scrollbar = "no";  
     if (height > 600) {      if (height > 600) {
  height = 600;   height = 600;
  scrollbar = "yes";  
     }      }
     var xpos = (screen.width-600)/2;      var xpos = (screen.width-600)/2;
     xpos = (xpos < 0) ? '0' : xpos;      xpos = (xpos < 0) ? '0' : xpos;
     var ypos = (screen.height-height)/2-30;      var ypos = (screen.height-height)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;      ypos = (ypos < 0) ? '0' : ypos;
   
     pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);      pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars=yes,screenx='+xpos+',screeny='+ypos+',width=700,height='+height);
     pWin.focus();      pWin.focus();
     pDoc = pWin.document;      pDoc = pWin.document;
     pDoc.$docopen;      pDoc.$docopen;
Line 1528  INNERJS Line 1620  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("<h1>&nbsp;$html_js_lt{'comp'}\"+fullname+\"<\\/h1>");
   
     pDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');      pDoc.write('<table style="border:1px solid black;"><tr>');
     pDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');      pDoc.write("<td><b>$html_js_lt{'incl'}<\\/b><\\/td><td><b>$html_js_lt{'type'}<\\/b><\\/td><td><b>$html_js_lt{'mesa'}<\\/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>");
     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>$html_js_lt{'subj'}<\\/td>");
       pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"40\\" 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>");
     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 align=\\"center\\">"+ctr+"<\\/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>");
     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 align=\\"center\\">$html_js_lt{'new'}<\\/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=\\"$html_js_lt{'save'}\\" onclick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onclick=\\"self.close()\\"><br /><br />");      pDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'canc'}\\" 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 1577  INNERJS Line 1668  INNERJS
     var redsel = "";      var redsel = "";
     var grnsel = "";      var grnsel = "";
     var blusel = "";      var blusel = "";
     if (kwclr=="red")   {var redsel="checked"};      var txtcol1 = "$js_lt{'col1'}";
     if (kwclr=="green") {var grnsel="checked"};      var txtcol2 = "$js_lt{'col2'}";
     if (kwclr=="blue")  {var blusel="checked"};      var txtcol3 = "$js_lt{'col3'}";
       var txtsiz1 = "$js_lt{'siz1'}";
       var txtsiz2 = "$js_lt{'siz2'}";
       var txtsiz3 = "$js_lt{'siz3'}";
       var txtsty1 = "$js_lt{'sty1'}";
       var txtsty2 = "$js_lt{'sty2'}";
       var txtsty3 = "$js_lt{'sty3'}";
       if (kwclr=="red")   {var redsel="checked='checked'"};
       if (kwclr=="green") {var grnsel="checked='checked'"};
       if (kwclr=="blue")  {var blusel="checked='checked'"};
     var sznsel = "";      var sznsel = "";
     var sz1sel = "";      var sz1sel = "";
     var sz2sel = "";      var sz2sel = "";
     if (kwsize=="0")  {var sznsel="checked"};      if (kwsize=="0")  {var sznsel="checked='checked'"};
     if (kwsize=="+1") {var sz1sel="checked"};      if (kwsize=="+1") {var sz1sel="checked='checked'"};
     if (kwsize=="+2") {var sz2sel="checked"};      if (kwsize=="+2") {var sz2sel="checked='checked'"};
     var synsel = "";      var synsel = "";
     var syisel = "";      var syisel = "";
     var sybsel = "";      var sybsel = "";
     if (kwstyle=="")    {var synsel="checked"};      if (kwstyle=="")    {var synsel="checked='checked'"};
     if (kwstyle=="<i>") {var syisel="checked"};      if (kwstyle=="<i>") {var syisel="checked='checked'"};
     if (kwstyle=="<b>") {var sybsel="checked"};      if (kwstyle=="<b>") {var sybsel="checked='checked'"};
     highlightCentral();      highlightCentral();
     highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel);      highlightbody('red',txtcol1,redsel,'0',txtsiz1,sznsel,'',txtsty1,synsel);
     highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel);      highlightbody('green',txtcol2,grnsel,'+1',txtsiz2,sz1sel,'<i>',txtsty2,syisel);
     highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel);      highlightbody('blue',txtcol3,blusel,'+2',txtsiz3,sz2sel,'<b>',txtsty3,sybsel);
     highlightend();      highlightend();
     return;      return;
   }    }
Line 1613  INNERJS Line 1713  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("<h1>$html_js_lt{'kehi'}<\\/h1>");
   
     hDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');      hDoc.write('<table border="0" width="100%"><tr style="background-color:#A1D676">');
     hDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');      hDoc.write("<th>$html_js_lt{'txtc'}<\\/th><th>$html_js_lt{'font'}<\\/th><th>$html_js_lt{'fnst'}<\\/th><\\/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>");
     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><br \\/>");
     hDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");      hDoc.write("<input type=\\"button\\" value=\\"$html_js_lt{'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=\\"$html_js_lt{'canc'}\\" 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 1685  sub gradeBox { Line 1783  sub gradeBox {
     $wgt       = ($wgt > 0 ? $wgt : '1');      $wgt       = ($wgt > 0 ? $wgt : '1');
     my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?      my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
   '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));    '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));
     my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";      my $data_WGT='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
     my $display_part= &get_display_part($partid,$symb);      my $display_part= &get_display_part($partid,$symb);
     my %last_resets = &get_last_resets($symb,$env{'request.course.id'},      my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
        [$partid]);         [$partid]);
Line 1693  sub gradeBox { Line 1791  sub gradeBox {
     if ($last_resets{$partid}) {      if ($last_resets{$partid}) {
         $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);          $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);
     }      }
     $result.=&Apache::loncommon::start_data_table_row();      my $result=&Apache::loncommon::start_data_table_row();
     my $ctr = 0;      my $ctr = 0;
     my $thisweight = 0;      my $thisweight = 0;
     my $increment = &get_increment();      my $increment = &get_increment();
Line 1730  sub gradeBox { Line 1828  sub gradeBox {
   
   
     $result .=       $result .= 
     '<td>'.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';      '<td>'.$data_WGT.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';
     $result.=&Apache::loncommon::end_data_table_row();      $result.=&Apache::loncommon::end_data_table_row();
       $result.=&Apache::loncommon::start_data_table_row().'<td colspan="6">';
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".      $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
  '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".   '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
  '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.   '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
Line 1742  sub gradeBox { Line 1841  sub gradeBox {
         $aggtries.'" />'."\n";          $aggtries.'" />'."\n";
     my $res_error;      my $res_error;
     $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error);      $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error);
       $result.='</td>'.&Apache::loncommon::end_data_table_row();
     if ($res_error) {      if ($res_error) {
         return &navmap_errormsg();          return &navmap_errormsg();
     }      }
Line 1749  sub gradeBox { Line 1849  sub gradeBox {
 }  }
   
 sub handback_box {  sub handback_box {
     my ($symb,$uname,$udom,$counter,$partid,$record,$res_error) = @_;      my ($symb,$uname,$udom,$counter,$partid,$record,$res_error_pointer) = @_;
     my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error);      my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error_pointer);
     my (@respids);      my (@respids);
      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) {
     my ($part,$resp) = @{ $part_response_id };      my ($part,$resp) = @{ $part_response_id };
         if ($part eq $partid) {          if ($part eq $partid) {
Line 1764  sub handback_box { Line 1864  sub handback_box {
  my $prefix = $counter.'_'.$partid.'_'.$respid.'_';   my $prefix = $counter.'_'.$partid.'_'.$respid.'_';
  my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);   my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
  next if (!@$files);   next if (!@$files);
  my $file_counter = 1;   my $file_counter = 0;
  foreach my $file (@$files) {   foreach my $file (@$files) {
     if ($file =~ /\/portfolio\//) {      if ($file =~ /\/portfolio\//) {
                   $file_counter++;
            my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);             my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
            my ($name,$version,$ext) = &file_name_version_ext($file_disp);             my ($name,$version,$ext) = &Apache::lonnet::file_name_version_ext($file_disp);
            $file_disp = "$name.$ext";             $file_disp = "$name.$ext";
            $file = $file_path.$file_disp;             $file = $file_path.$file_disp;
            $result.=&mt('Return commented version of [_1] to student.',             $result.=&mt('Return commented version of [_1] to student.',
     '<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 />'."\n";
            $result.='('.&mt('File will be uploaded when you click on Save &amp; Next below.').')<br />';  
            $file_counter++;  
     }      }
  }   }
           if ($file_counter) {
               $result .= '<input type="hidden" name="'.$prefix.'countreturndoc" value="'.$file_counter.'" />'."\n".
                          '<span class="LC_info">'.
                          '('.&mt('File(s) will be uploaded when you click on Save &amp; Next below.',$file_counter).')</span><br /><br />';
           }
     }      }
     return $result;          return $result;    
 }  }
Line 1811  sub show_problem { Line 1915  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 $renderheading = &mt('View of the problem');
       my $answerheading = &mt('Correct answer');
       if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
           my $stu_fullname = $env{'form.fullname'};
           if ($stu_fullname eq '') {
               $stu_fullname = &Apache::loncommon::plainname($uname,$udom,'lastname');
           }
           my $forwhom = &nameUserString(undef,$stu_fullname,$uname,$udom);
           if ($forwhom ne '') {
               $renderheading = &mt('View of the problem for[_1]',$forwhom);
               $answerheading = &mt('Correct answer for[_1]',$forwhom);
           }
       }
     $rendered=      $rendered=
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('View of the problem').'</h3>'         .'<h3 class="LC_hcell">'.$renderheading.'</h3>'
        .$rendered         .$rendered
        .'</div>';         .'</div>';
     $companswer=      $companswer=
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('Correct answer').'</h3>'         .'<h3 class="LC_hcell">'.$answerheading.'</h3>'
        .$companswer         .$companswer
        .'</div>';         .'</div>';
     my $result;      my $result;
Line 1854  sub files_exist { Line 1971  sub files_exist {
   
 sub download_all_link {  sub download_all_link {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
       unless (&files_exist($r, $symb)) {
          $r->print(&mt('There are currently no submitted documents.'));
          return;
       }
   
     my $all_students =       my $all_students = 
  join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));   join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));
   
Line 1866  sub download_all_link { Line 1988  sub download_all_link {
                              '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;
   }
   
   sub submit_download_link {
       my ($request,$symb) = @_;
       if (!$symb) { return ''; }
   #FIXME: Figure out which type of problem this is and provide appropriate download
       &download_all_link($request,$symb);
 }  }
   
 sub build_section_inputs {  sub build_section_inputs {
Line 1892  sub submission { Line 2021  sub submission {
   
     my $probtitle=&Apache::lonnet::gettitle($symb);       my $probtitle=&Apache::lonnet::gettitle($symb); 
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
       my $is_tool = ($symb =~ /ext\.tool$/);
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print('<span class="LC_warning">Unable to view requested student.('.          $request->print(
  $uname.':'.$udom.' in section '.$usec.' in course id '.              '<span class="LC_warning">'.
  $env{'request.course.id'}.')</span>');              &mt('Unable to view requested student.').
               ' '.&mt('([_1] in section [_2] in course id [_3])',
                           $uname.':'.$udom,$usec,$env{'request.course.id'}).
               '</span>');
  return;   return;
     }      }
   
     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }      if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
     if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }      unless ($is_tool) { 
     if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }          if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
           if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
       }
     my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');      my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
     my $checkIcon = '<img alt="'.&mt('Check Mark').      my $checkIcon = '<img alt="'.&mt('Check Mark').
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
   
     my %old_essays;  
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
  &sub_page_js($request);   &sub_page_js($request);
  &sub_page_kw_js($request) if ($env{'form.handgrade'} eq 'yes');   &sub_page_kw_js($request);
  if ($env{'form.handgrade'} eq 'yes' && &files_exist($request, $symb)) {  
     &download_all_link($request, $symb);  
  }  
  $request->print('<h3>&nbsp;<span class="LC_info">'.&mt('Submission Record').'</span></h3>');  
   
  # 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 1933  sub submission { Line 2063  sub submission {
     $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 not to be blank 
         # if this subroutine has been called once.          # if this subroutine has been called once.
  my %keyhash = ();   my %keyhash = ();
  if ($env{'form.kwclr'} eq '' && $env{'form.handgrade'} eq 'yes') {  # if ($env{'form.kwclr'} eq '' && $env{'form.handgrade'} eq 'yes') {
           if (1) {
     %keyhash = &Apache::lonnet::dump('nohist_handgrade',      %keyhash = &Apache::lonnet::dump('nohist_handgrade',
      $env{'course.'.$env{'request.course.id'}.'.domain'},       $env{'course.'.$env{'request.course.id'}.'.domain'},
      $env{'course.'.$env{'request.course.id'}.'.num'});       $env{'course.'.$env{'request.course.id'}.'.num'});
Line 1960  sub submission { Line 2091  sub submission {
  '<input type="hidden" name="studentNo"  value="" />'."\n".   '<input type="hidden" name="studentNo"  value="" />'."\n".
  '<input type="hidden" name="gradeOpt"   value="" />'."\n".   '<input type="hidden" name="gradeOpt"   value="" />'."\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="showgrading" value="'.$env{'form.showgrading'}.'" />'."\n".  
  '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".   '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".
  '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".   '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".
  '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".   '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".
  &build_section_inputs().   &build_section_inputs().
  '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".   '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".
  '<input type="hidden" name="handgrade"  value="'.$env{'form.handgrade'}.'" />'."\n".  
  '<input type="hidden" name="NCT"'.   '<input type="hidden" name="NCT"'.
  ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");   ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");
  if ($env{'form.handgrade'} eq 'yes') {  # if ($env{'form.handgrade'} eq 'yes') {
           if (1) {
     $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".      $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".
     '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".      '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".
     '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".      '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".
Line 1994  sub submission { Line 2124  sub submission {
  }   }
  $request->print($prnmsg);   $request->print($prnmsg);
   
  if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') {  # if ($env{'form.handgrade'} eq 'yes') {
           unless ($is_tool) {
   
               my %lt = &Apache::lonlocal::texthash(
                             keyh => 'Keyword Highlighting for Essays',
                             keyw => 'Keyword Options',
                             list => 'List',
                             past => 'Paste Selection to List',
                             high => 'Highlight Attribute',
                        );    
 #  #
 # Print out the keyword options line  # Print out the keyword options line
 #  #
     $request->print(<<KEYWORDS);      $request->print(
 &nbsp;<b>Keyword Options:</b>&nbsp;                  '<div class="LC_columnSection">'
 <a href="javascript:keywords(document.SCORE);" target="_self">List</a>&nbsp; &nbsp;                 .'<fieldset><legend>'.$lt{'keyh'}.'</legend>'
 <a href="#" onmousedown="javascript:getSel(); return false"                 .&Apache::lonhtmlcommon::funclist_from_array(
  CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;                      ['<a href="javascript:keywords(document.SCORE);" target="_self">'.$lt{'list'}.'</a>',
 <a href="javascript:kwhighlight();" target="_self">Highlight Attribute</a><br /><br />                       '<a href="#" onmousedown="javascript:getSel(); return false"
 KEYWORDS   class="page">'.$lt{'past'}.'</a>',
                        '<a href="javascript:kwhighlight();" target="_self">'.$lt{'high'}.'</a>'],
                       {legend => $lt{'keyw'}})
                  .'</fieldset></div>'
               );
   
 #  #
 # Load the other essays for similarity check  # Load the other essays for similarity check
 #  #
Line 2012  KEYWORDS Line 2156  KEYWORDS
     my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);      my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
     $apath=&escape($apath);      $apath=&escape($apath);
     $apath=~s/\W/\_/gs;      $apath=~s/\W/\_/gs;
     %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);              &init_old_essays($symb,$apath,$adom,$aname);
         }          }
     }      }
   
Line 2029  KEYWORDS Line 2173  KEYWORDS
     if ($perm{'vgr'}) {      if ($perm{'vgr'}) {
         $request->print(          $request->print(
             &Apache::loncommon::track_student_link(              &Apache::loncommon::track_student_link(
                 &mt('View recent activity'),                  'View recent activity',
                 $uname,$udom,'check')                  $uname,$udom,'check')
            .' '             .' '
         );          );
Line 2066  KEYWORDS Line 2210  KEYWORDS
     # Display student info      # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));      $request->print(($counter == 0 ? '' : '<br />'));
   
       my $boxtitle = &mt('Submissions');
       if ($is_tool) {
           $boxtitle = &mt('Transactions')
       }
     my $result='<div class="LC_Box">'      my $result='<div class="LC_Box">'
               .'<h3 class="LC_hcell">'.&mt('Submissions').'</h3>';                .'<h3 class="LC_hcell">'.$boxtitle.'</h3>';
     $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') {  #    if ($env{'form.handgrade'} eq 'no') {
       unless ($is_tool) {
         $result.='<p class="LC_info">'          $result.='<p class="LC_info">'
                 .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)                  .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
                 ."</p>\n";                  ."</p>\n";
Line 2079  KEYWORDS Line 2228  KEYWORDS
     # 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 $fullname;      my $fullname;
     my $col_fullnames = [];      my $col_fullnames = [];
     if ($env{'form.handgrade'} eq 'yes') {  #    if ($env{'form.handgrade'} eq 'yes') {
       unless ($is_tool) {
  (my $sub_result,$fullname,$col_fullnames)=   (my $sub_result,$fullname,$col_fullnames)=
     &check_collaborators($symb,$uname,$udom,\%record,$handgrade,      &check_collaborators($symb,$uname,$udom,\%record,$handgrade,
  $counter);   $counter);
  $result.=$sub_result;   $result.=$sub_result;
     }      }
     $request->print($result."\n");      $request->print($result."\n");
       
     # print student answer/submission      # print student answer/submission
     # Options are (1) Handgraded submission only      # Options are (1) Handgraded submission only
     #             (2) Last submission, includes submission that is not handgraded       #             (2) Last submission, includes submission that is not handgraded 
     #                  (for multi-response type part)      #                  (for multi-response type part)
     #             (3) Last submission plus the parts info      #             (3) Last submission plus the parts info
     #             (4) The whole record for this student      #             (4) The whole record for this student
     if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {      
  my ($string,$timestamp)= &get_last_submission(\%record);      my ($string,$timestamp)= &get_last_submission(\%record,$is_tool);
   
  my $lastsubonly;      my $lastsubonly;
   
         if ($$timestamp eq '') {      if ($$timestamp eq '') {
             $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>';           $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>'; 
         } else {      } elsif ($is_tool) {
             $lastsubonly =          $lastsubonly =
                 '<div class="LC_grade_submissions_body">'              '<div class="LC_grade_submissions_body">'
                .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";             .'<b>'.&mt('Date Grade Passed Back:').'</b> '.$$timestamp."</div>\n";
       } else {
     my %seenparts;          $lastsubonly =
     my @part_response_id = &flatten_responseType($responseType);              '<div class="LC_grade_submissions_body">'
     foreach my $part (@part_response_id) {             .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";
  next if ($env{'form.lastSub'} eq 'hdgrade'   
    my %seenparts;
    my @part_response_id = &flatten_responseType($responseType);
    foreach my $part (@part_response_id) {
       next if ($env{'form.lastSub'} eq 'hdgrade' 
  && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');   && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');
   
  my ($partid,$respid) = @{ $part };      my ($partid,$respid) = @{ $part };
  my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
  if ($env{"form.$uname:$udom:$partid:submitted_by"}) {      if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
     if (exists($seenparts{$partid})) { next; }   if (exists($seenparts{$partid})) { next; }
     $seenparts{$partid}=1;   $seenparts{$partid}=1;
     my $submitby='<b>Part:</b> '.$display_part.                  $request->print(
  ' <b>Collaborative submission by:</b> '.                      '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
  '<a href="javascript:viewSubmitter(\''.                      ' <b>'.&mt('Collaborative submission by: [_1]',
  $env{"form.$uname:$udom:$partid:submitted_by"}.                                 '<a href="javascript:viewSubmitter(\''.
  '\');" target="_self">'.                                 $env{"form.$uname:$udom:$partid:submitted_by"}.
  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';                                 '\');" target="_self">'.
     $request->print($submitby);                                 $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
     next;                      '<br />');
  }   next;
  my $responsetype = $responseType->{$partid}->{$respid};   }
  if (!exists($record{"resource.$partid.$respid.submission"})) {      my $responsetype = $responseType->{$partid}->{$respid};
                     $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.      if (!exists($record{"resource.$partid.$respid.submission"})) {
                         '<b>'.&mt('Part: [_1]',$display_part).'</b>'.                  $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
                         ' <span class="LC_internal_info">'.                      '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                         '('.&mt('Part ID: [_1]',$respid).')'.                      ' <span class="LC_internal_info">'.
                         '</span>&nbsp; &nbsp;'.                      '('.&mt('Response ID: [_1]',$respid).')'.
  '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';                      '</span>&nbsp; &nbsp;'.
     next;            '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
  }   next;
  foreach my $submission (@$string) {      }
     my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);      foreach my $submission (@$string) {
     if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }   my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
     my ($ressub,$hide,$subval) = split(/:/,$submission,3);   if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
     # Similarity check   my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
     my $similar='';   # Similarity check
     if($env{'form.checkPlag'}){                  my $similar='';
  my ($oname,$odom,$ocrsid,$oessay,$osim)=                  my ($type,$trial,$rndseed);
     &most_similar($uname,$udom,$subval,\%old_essays);                  if ($hide eq 'rand') {
  if ($osim) {                      $type = 'randomizetry';
     $osim=int($osim*100.0);                      $trial = $record{"resource.$partid.tries"};
     my %old_course_desc =                       $rndseed = $record{"resource.$partid.rndseed"};
  &Apache::lonnet::coursedescription($ocrsid,                  }
    {'one_time' => 1});          if ($env{'form.checkPlag'}) {
          my ($oname,$odom,$ocrsid,$oessay,$osim)=
                             if ($hide) {          &most_similar($uname,$udom,$symb,$subval);
                                 $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.      if ($osim) {
                                          &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';   $osim=int($osim*100.0);
                             } else {   my %old_course_desc = 
         $similar="<hr /><h3><span class=\"LC_warning\">".      &Apache::lonnet::coursedescription($ocrsid,
     &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',   {'one_time' => 1});
         $osim,  
         &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',                          if ($hide eq 'anon') {
                               $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
                                        &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
                           } else {
       $similar="<hr /><h3><span class=\"LC_warning\">".
    &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
       $osim,
       &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
         $old_course_desc{'description'},          $old_course_desc{'description'},
         $old_course_desc{'num'},          $old_course_desc{'num'},
         $old_course_desc{'domain'}).          $old_course_desc{'domain'}).
     '</span></h3><blockquote><i>'.      '</span></h3><blockquote><i>'.
     &keywords_highlight($oessay).      &keywords_highlight($oessay).
     '</i></blockquote><hr />';      '</i></blockquote><hr />';
                             }                          }
  }              }
     }   }
     my $order=&get_order($partid,$respid,$symb,$uname,$udom);   my $order=&get_order($partid,$respid,$symb,$uname,$udom,
     if ($env{'form.lastSub'} eq 'lastonly' ||                                        undef,$type,$trial,$rndseed);
  ($env{'form.lastSub'} eq 'hdgrade' &&                   if ($env{'form.lastSub'} eq 'lastonly' || $env{'form.lastSub'} eq 'datesub' || $env{'form.lastSub'} =~ /^(last|all)$/ || ($env{'form.lastSub'} eq 'hdgrade' && 
  $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {   $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {
  my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
                         $lastsubonly.='<div class="LC_grade_submission_part">'.                      $lastsubonly.='<div class="LC_grade_submission_part">'.
                             '<b>'.&mt('Part: [_1]',$display_part).'</b>'.                          '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                             ' <span class="LC_internal_info">'.                          ' <span class="LC_internal_info">'.
                             '('.&mt('Part ID: [_1]',$respid).')'.                          '('.&mt('Response ID: [_1]',$respid).')'.
                             '</span>&nbsp; &nbsp;';                          '</span>&nbsp; &nbsp;';
  my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);      my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
  if (@$files) {                          
                             if ($hide) {      if (@$files) {
                                 $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));                          if ($hide eq 'anon') {
                               $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
                           } else {
                               $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
                                           .'<br /><span class="LC_warning">';
                               if(@$files == 1) {
                                   $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
                             } else {                              } else {
                                 $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />';                                  $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
                                 foreach my $file (@$files) {                              }
                                     &Apache::lonnet::allowuploaded('/adm/grades',$file);                              $lastsubonly .= '</span>';                         
                                     $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" /> '.$file.'</a>';                              foreach my $file (@$files) {
                                 }                                  &Apache::lonnet::allowuploaded('/adm/grades',$file);
                                   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
                             }                              }
     $lastsubonly.='<br />';  
  }  
                         if ($hide) {  
                             $lastsubonly.='<b>'.&mt('Anonymous Survey').'</b>';   
                         } else {  
     $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.  
         &cleanRecord($subval,$responsetype,$symb,$partid,  
      $respid,\%record,$order,undef,$uname,$udom);  
                         }                          }
  if ($similar) {$lastsubonly.="<br /><br />$similar\n";}   $lastsubonly.='<br />';
  $lastsubonly.='</div>';                      }
     }                      if ($hide eq 'anon') {
                           $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; 
                       } else {
                        $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
                           if ($draft) {
                               $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
                           }
                           $subval =
       &cleanRecord($subval,$responsetype,$symb,$partid,
    $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
                           if ($responsetype eq 'essay') {
                               $subval =~ s{\n}{<br />}g;
                           }
                           $lastsubonly.=$subval."\n";
                       }
               if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
       $lastsubonly.='</div>';
  }   }
     }              }
     $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body  
  }   }
  $request->print($lastsubonly);   $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
    } elsif ($env{'form.lastSub'} eq 'datesub') {      }
 # my (undef,$responseType,undef,$parts) = &showResourceInfo($symb);      $request->print($lastsubonly);
     my ($parts,$handgrade,$responseType) = &response_type($symb);      if ($env{'form.lastSub'} eq 'datesub') {
           my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);
  $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));   $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
     } elsif ($env{'form.lastSub'} =~ /^(last|all)$/) {    
  $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,      } 
       if ($env{'form.lastSub'} =~ /^(last|all)$/) {
           my $identifier = (&canmodify($usec)? $counter : '');
           $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
  $env{'request.course.id'},   $env{'request.course.id'},
  $last,'.submission',   $last,'.submission',
  'Apache::grades::keywords_highlight'));   'Apache::grades::keywords_highlight',
                                                                    $usec,$identifier));
     }      }
   
     $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 (!&canmodify($usec)) {
  my $toGrade.='<input type="button" value="Grade Student" '.   $request->print('<p><span class="LC_warning">'.&mt('No grading privileges').'</span></p></div>');
     'onclick="javascript:checksubmit(this.form,\'Grade Student\',\''  
     .$counter.'\');" target="_self" /> &nbsp;'."\n" if (&canmodify($usec));  
  $toGrade.='</div>'."\n";  
  $request->print($toGrade);  
  return;   return;
     } else {      } else {
  $request->print('</div>'."\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') {
       if (1) {
  my $result='<div class="LC_grade_message_center">';   my $result='<div class="LC_grade_message_center">';
           
  $result.='<div class="LC_grade_message_center_header">'.   $result.='<div class="LC_grade_message_center_header">'.
Line 2248  KEYWORDS Line 2423  KEYWORDS
     '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";      '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
  $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.   $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
     ',\''.$msgfor.'\');" target="_self">'.      ',\''.$msgfor.'\');" target="_self">'.
     &mt('Compose message to student').(scalar(@$col_fullnames) >= 1 ? 's' : '').'</a><label> ('.      &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.
     &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.      &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
     '<img src="'.$request->dir_config('lonIconsURL').      ' <img src="'.$request->dir_config('lonIconsURL').
     '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".      '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".
     '<br />&nbsp;('.      '<br />&nbsp;('.
     &mt('Message will be sent when you click on Save &amp; Next below.').")\n";      &mt('Message will be sent when you click on Save &amp; Next below.').")\n";
  $result.='</div></div>';   $result.='</div></div>';
Line 2261  KEYWORDS Line 2436  KEYWORDS
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id;
       if ($is_tool) {
           @part_response_id = ([0,'']);
       } else {
           @part_response_id = &flatten_responseType($responseType);
       }
     $request->print(      $request->print(
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'         .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'
Line 2342  sub check_collaborators { Line 2522  sub check_collaborators {
  next if ($record->{'resource.'.$part.'.collaborators'} eq '');   next if ($record->{'resource.'.$part.'.collaborators'} eq '');
  my (@good_collaborators, @bad_collaborators);   my (@good_collaborators, @bad_collaborators);
  foreach my $possible_collaborator   foreach my $possible_collaborator
     (split(/,?\s+/,$record->{'resource.'.$part.'.collaborators'})) {       (split(/[,;\s]+/,$record->{'resource.'.$part.'.collaborators'})) { 
     $possible_collaborator =~ s/[\$\^\(\)]//g;      $possible_collaborator =~ s/[\$\^\(\)]//g;
     next if ($possible_collaborator eq '');      next if ($possible_collaborator eq '');
     my ($co_name,$co_dom) = split(/\@|:/,$possible_collaborator);      my ($co_name,$co_dom) = split(/:/,$possible_collaborator);
     $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);      $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
     next if ($co_name eq $uname && $co_dom eq $udom);      next if ($co_name eq $uname && $co_dom eq $udom);
     # Doing this grep allows 'fuzzy' specification      # Doing this grep allows 'fuzzy' specification
Line 2358  sub check_collaborators { Line 2538  sub check_collaborators {
     }      }
  }   }
  if (scalar(@good_collaborators) != 0) {   if (scalar(@good_collaborators) != 0) {
     $result.='<br />'.&mt('Collaborators: ');      $result.='<br />'.&mt('Collaborators:').'<ol>';
     foreach my $name (@good_collaborators) {      foreach my $name (@good_collaborators) {
  my ($lastname,$givenn) = split(/,/,$$fullname{$name});   my ($lastname,$givenn) = split(/,/,$$fullname{$name});
  push(@col_fullnames, $givenn.' '.$lastname);   push(@col_fullnames, $givenn.' '.$lastname);
  $result.=$fullname->{$name}.'&nbsp; &nbsp; &nbsp;';   $result.='<li>'.$fullname->{$name}.'</li>';
     }      }
     $result.='<br />'."\n";      $result.='</ol><br />'."\n";
     my ($part)=split(/\./,$part);      my ($part)=split(/\./,$part);
     $result.='<input type="hidden" name="collaborator'.$counter.      $result.='<input type="hidden" name="collaborator'.$counter.
  '" value="'.$part.':'.(join ':',@good_collaborators).'" />'.   '" value="'.$part.':'.(join ':',@good_collaborators).'" />'.
Line 2387  sub check_collaborators { Line 2567  sub check_collaborators {
   
 #--- 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,$is_tool)=@_;
     my (@string,$timestamp,%lasthidden);      my (@string,$timestamp,%lasthidden);
     if ($$returnhash{'version'}) {      if ($$returnhash{'version'}) {
  my %lasthash=();   my %lasthash=();
Line 2400  sub get_last_submission { Line 2580  sub get_last_submission {
     &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});      &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
     }      }
  }   }
         my %typeparts;          my (%typeparts,%randombytry);
         my $showsurv =           my $showsurv = 
             &Apache::lonnet::allowed('vas',$env{'request.course.id'});              &Apache::lonnet::allowed('vas',$env{'request.course.id'});
         foreach my $key (sort(keys(%lasthash))) {          foreach my $key (sort(keys(%lasthash))) {
             if ($key =~ /\.type$/) {              if ($key =~ /\.type$/) {
                 if (($lasthash{$key} eq 'anonsurvey') ||                   if (($lasthash{$key} eq 'anonsurvey') || 
                     ($lasthash{$key} eq 'anonsurveycred')) {                      ($lasthash{$key} eq 'anonsurveycred') ||
                       ($lasthash{$key} eq 'randomizetry')) {
                     my ($ign,@parts) = split(/\./,$key);                      my ($ign,@parts) = split(/\./,$key);
                     pop(@parts);                      pop(@parts);
                     unless ($showsurv) {                      my $id = join('.',@parts);
                         my $id = join(',',@parts);                      if ($lasthash{$key} eq 'randomizetry') {
                         $typeparts{$ign.'.'.$id} = $lasthash{$key};                          $randombytry{$ign.'.'.$id} = $lasthash{$key};
                       } else {
                           unless ($showsurv) {
                               $typeparts{$ign.'.'.$id} = $lasthash{$key};
                           }
                     }                      }
                     delete($lasthash{$key});                      delete($lasthash{$key});
                 }                  }
             }              }
         }          }
         my @hidden = keys(%typeparts);          my @hidden = keys(%typeparts);
           my @randomize = keys(%randombytry);
  foreach my $key (keys(%lasthash)) {   foreach my $key (keys(%lasthash)) {
     next if ($key !~ /\.submission$/);      next if ($key !~ /\.submission$/);
             my $hide;              my $hide;
             if (@hidden) {              if (@hidden) {
                 foreach my $id (@hidden) {                  foreach my $id (@hidden) {
                     if ($key =~ /^\Q$id\E/) {                      if ($key =~ /^\Q$id\E/) {
                         $hide = 1;                          $hide = 'anon';
                         last;                          last;
                     }                      }
                 }                  }
             }              }
               unless ($hide) {
                   if (@randomize) {
                       foreach my $id (@randomize) {
                           if ($key =~ /^\Q$id\E/) {
                               $hide = 'rand';
                               last;
                           }
                       }
                   }
               }
     my ($partid,$foo) = split(/submission$/,$key);      my ($partid,$foo) = split(/submission$/,$key);
     my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?      my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? 1 : 0;
  '<span class="LC_warning">Draft Copy</span> ' : '';              push(@string, join(':', $key, $hide, $draft, (
     push(@string, join(':', $key, $hide, $draft.$lasthash{$key}));                  ref($lasthash{$key}) eq 'ARRAY' ?
                       join(',', @{$lasthash{$key}}) : $lasthash{$key}) ));
  }   }
     }      }
     if (!@string) {      if (!@string) {
           my $msg;
           if ($is_tool) {
               $msg = &mt('No grade passed back.');
           } else {
               $msg = &mt('Nothing submitted - no attempts.');
           }
  $string[0] =   $string[0] =
     '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';      '<span class="LC_warning">'.$msg.'</span>';
     }      }
     return (\@string,\$timestamp);      return (\@string,\$timestamp);
 }  }
Line 2455  sub keywords_highlight { Line 2658  sub keywords_highlight {
     return $string;      return $string;
 }  }
   
   # For Tasks provide a mechanism to display previous version for one specific student
   
   sub show_previous_task_version {
       my ($request,$symb) = @_;
       if ($symb eq '') {
           $request->print(
               '<span class="LC_error">'.
               &mt('Unable to handle ambiguous references.').
               '</span>');
           return '';
       }
       my ($uname,$udom) = ($env{'form.student'},$env{'form.userdom'});
       my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
       if (!&canview($usec)) {
           $request->print(
               '<span class="LC_warning">'.
               &mt('Unable to view previous version for requested student.').
               ' '.&mt('([_1] in section [_2] in course id [_3])',
                       $uname.':'.$udom,$usec,$env{'request.course.id'}).
               '</span>');
           return;
       }
       my $mode = 'both';
       my $isTask = ($symb =~/\.task$/);
       if ($isTask) {
           if ($env{'form.previousversion'} =~ /^\d+$/) {
               if ($env{'form.fullname'} eq '') {
                   $env{'form.fullname'} =
                       &Apache::loncommon::plainname($uname,$udom,'lastname');
               }
               my $probtitle=&Apache::lonnet::gettitle($symb);
               $request->print("\n\n".
                               '<div class="LC_grade_show_user">'.
                               '<h2>'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).
                               '</h2>'."\n");
               &Apache::lonxml::clear_problem_counter();
               $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode,
                               {'previousversion' => $env{'form.previousversion'} }));
               $request->print("\n</div>");
           }
       }
       return;
   }
   
   sub choose_task_version_form {
       my ($symb,$uname,$udom,$nomenu) = @_;
       my $isTask = ($symb =~/\.task$/);
       my ($current,$version,$result,$js,$displayed,$rowtitle);
       if ($isTask) {
           my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
                                                 $udom,$uname);
           if (($record{'resource.0.version'} eq '') ||
               ($record{'resource.0.version'} < 2)) {
               return ($record{'resource.0.version'},
                       $record{'resource.0.version'},$result,$js);
           } else {
               $current = $record{'resource.0.version'};
           }
           if ($env{'form.previousversion'}) {
               $displayed = $env{'form.previousversion'};
               $rowtitle = &mt('Choose another version:')
           } else {
               $displayed = $current;
               $rowtitle = &mt('Show earlier version:');
           }
           $result = '<div class="LC_left_float">';
           my $list;
           my $numversions = 0;
           for (my $i=1; $i<=$record{'resource.0.version'}; $i++) {
               if ($i == $current) {
                   if (!$env{'form.previousversion'} || $nomenu) {
                       next;
                   } else {
                       $list .= '<option value="'.$i.'">'.&mt('Current').'</option>'."\n";
                       $numversions ++;
                   }
               } elsif (defined($record{'resource.'.$i.'.0.status'})) {
                   unless ($i == $env{'form.previousversion'}) {
                       $numversions ++;
                   }
                   $list .= '<option value="'.$i.'">'.$i.'</option>'."\n";
               }
           }
           if ($numversions) {
               $symb = &HTML::Entities::encode($symb,'<>"&');
               $result .=
                   '<form name="getprev" method="post" action=""'.
                   ' onsubmit="return previousVersion('."'$uname','$udom','$symb','$displayed'".');">'.
                   &Apache::loncommon::start_data_table().
                   &Apache::loncommon::start_data_table_row().
                   '<th align="left">'.$rowtitle.'</th>'.
                   '<td><select name="version">'.
                   '<option>'.&mt('Select').'</option>'.
                   $list.
                   '</select></td>'.
                   &Apache::loncommon::end_data_table_row();
               unless ($nomenu) {
                   $result .= &Apache::loncommon::start_data_table_row().
                   '<th align="left">'.&mt('Open in new window').'</th>'.
                   '<td><span class="LC_nobreak">'.
                   '<label><input type="radio" name="prevwin" value="1" />'.
                   &mt('Yes').'</label>'.
                   '<label><input type="radio" name="prevwin" value="0" checked="checked" />'.&mt('No').'</label>'.
                   '</span></td>'.
                   &Apache::loncommon::end_data_table_row();
               }
               $result .=
                   &Apache::loncommon::start_data_table_row().
                   '<th align="left">&nbsp;</th>'.
                   '<td>'.
                   '<input type="submit" name="prevsub" value="'.&mt('Display').'" />'.
                   '</td>'.
                   &Apache::loncommon::end_data_table_row().
                   &Apache::loncommon::end_data_table().
                   '</form>';
               $js = &previous_display_javascript($nomenu,$current);
           } elsif ($displayed && $nomenu) {
               $result .= '<a href="javascript:window.close()">'.&mt('Close window').'</a>';
           } else {
               $result .= &mt('No previous versions to show for this student');
           }
           $result .= '</div>';
       }
       return ($current,$displayed,$result,$js);
   }
   
   sub previous_display_javascript {
       my ($nomenu,$current) = @_;
       my $js = <<"JSONE";
   <script type="text/javascript">
   // <![CDATA[
   function previousVersion(uname,udom,symb) {
       var current = '$current';
       var version = document.getprev.version.options[document.getprev.version.selectedIndex].value;
       var prevstr = new RegExp("^\\\\d+\$");
       if (!prevstr.test(version)) {
           return false;
       }
       var url = '';
       if (version == current) {
           url = '/adm/grades?student='+uname+'&userdom='+udom+'&symb='+symb+'&command=submission';
       } else {
           url = '/adm/grades?student='+uname+'&userdom='+udom+'&symb='+symb+'&command=versionsub&previousversion='+version;
       }
   JSONE
       if ($nomenu) {
           $js .= <<"JSTWO";
       document.location.href = url;
   JSTWO
       } else {
           $js .= <<"JSTHREE";
       var newwin = 0;
       for (var i=0; i<document.getprev.prevwin.length; i++) {
           if (document.getprev.prevwin[i].checked == true) {
               newwin = document.getprev.prevwin[i].value;
           }
       }
       if (newwin == 1) {
           var options = 'height=600,width=800,resizable=yes,scrollbars=yes,location=no,menubar=no,toolbar=no';
           url = url+'&inhibitmenu=yes';
           if (typeof(previousWin) == 'undefined' || previousWin.closed) {
               previousWin = window.open(url,'',options,1);
           } else {
               previousWin.location.href = url;
           }
           previousWin.focus();
           return false;
       } else {
           document.location.href = url;
           return false;
       }
   JSTHREE
       }
       $js .= <<"ENDJS";
       return false;
   }
   // ]]>
   </script>
   ENDJS
   
   }
   
 #--- Called from submission routine  #--- Called from submission routine
 sub processHandGrade {  sub processHandGrade {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
Line 2469  sub processHandGrade { Line 2854  sub processHandGrade {
  my $ctr = 0;   my $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});      my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
     my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$symb,$uname,$udom,$ctr);      my ($errorflag,$pts,$wgt,$numhidden) = 
                   &saveHandGrade($request,$symb,$uname,$udom,$ctr);
     if ($errorflag eq 'no_score') {      if ($errorflag eq 'no_score') {
  $ctr++;   $ctr++;
  next;   next;
     }      }
     if ($errorflag eq 'not_allowed') {      if ($errorflag eq 'not_allowed') {
  $request->print("<span class=\"LC_warning\">Not allowed to modify grades for $uname:$udom</span>");   $request->print(
                       '<span class="LC_error">'
                      .&mt('Not allowed to modify grades for [_1]',"$uname:$udom")
                      .'</span>');
  $ctr++;   $ctr++;
  next;   next;
     }      }
               if ($numhidden) {
                   $request->print(
                       '<span class="LC_info">'
                      .&mt('For [_1]: [quant,_2,transaction] hidden',"$uname:$udom",$numhidden)
                      .'</span><br />');
               }
     my $includemsg = $env{'form.includemsg'.$ctr};      my $includemsg = $env{'form.includemsg'.$ctr};
     my ($subject,$message,$msgstatus) = ('','','');      my ($subject,$message,$msgstatus) = ('','','');
     my $restitle = &Apache::lonnet::gettitle($symb);      my $restitle = &Apache::lonnet::gettitle($symb);
Line 2506  sub processHandGrade { Line 2901  sub processHandGrade {
                                                      undef,undef,$showsymb,                                                       undef,undef,$showsymb,
                                                      $restitle);                                                       $restitle);
  $request->print('<br />'.&mt('Sending message to [_1]',$uname.':'.$udom).': '.   $request->print('<br />'.&mt('Sending message to [_1]',$uname.':'.$udom).': '.
  $msgstatus);   $msgstatus.'<br />');
     }      }
     if ($env{'form.collaborator'.$ctr}) {      if ($env{'form.collaborator'.$ctr}) {
  my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");   my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
Line 2537  sub processHandGrade { Line 2932  sub processHandGrade {
  }   }
     }      }
   
     if ($env{'form.handgrade'} eq 'yes') {  #    if ($env{'form.handgrade'} eq 'yes') {
       if (1) {
  # Keywords sorted in alphabatical order   # Keywords sorted in alphabatical order
  my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};   my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
  my %keyhash = ();   my %keyhash = ();
Line 2590  sub processHandGrade { Line 2986  sub processHandGrade {
     my $processUser = $env{'form.unamedom'.$ctr};      my $processUser = $env{'form.unamedom'.$ctr};
     ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);      ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
     $env{'form.fullname'} = $$fullname{$processUser};      $env{'form.fullname'} = $$fullname{$processUser};
     &submission($request,$ctr,$total-1);      &submission($request,$ctr,$total-1,$symb);
     $ctr++;      $ctr++;
  }   }
  return '';   return '';
     }      }
   
 # Go directly to grade student - from submission or link from chart page  
     if ($button eq 'Grade Student') {  
 # (undef,undef,$env{'form.handgrade'},undef,undef) = &showResourceInfo($symb);  
  my $processUser = $env{'form.unamedom'.$env{'form.studentNo'}};  
  ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);  
  $env{'form.fullname'} = $$fullname{$processUser};  
  &submission($request,0,0);  
  return '';  
     }  
   
     # Get the next/previous one or group of students      # Get the next/previous one or group of students
     my $firststu = $env{'form.unamedom0'};      my $firststu = $env{'form.unamedom0'};
     my $laststu = $env{'form.unamedom'.($ngrade-1)};      my $laststu = $env{'form.unamedom'.($ngrade-1)};
Line 2689  sub processHandGrade { Line 3075  sub processHandGrade {
  $env{'form.student'}  = $uname;   $env{'form.student'}  = $uname;
  $env{'form.userdom'}  = $udom;   $env{'form.userdom'}  = $udom;
  $env{'form.fullname'} = $$fullname{$_};   $env{'form.fullname'} = $$fullname{$_};
  &submission($request,$ctr,$total);   &submission($request,$ctr,$total,$symb);
  $ctr++;   $ctr++;
     }      }
     if ($total < 0) {      if ($total < 0) {
  my $the_end = '<h3><span class="LC_info">'.&mt('LON-CAPA User Message').'</span></h3><br />'."\n";   my $the_end.='<p>'.&mt('[_1]Message:[_2] No more students for this section or class.','<b>','</b>').'</p>'."\n";
  $the_end.=&mt('<b>Message: </b> No more students for this section or class.').'<br /><br />'."\n";  
  $the_end.=&mt('Click on the button below to return to the grading menu.').'<br /><br />'."\n";  
  $request->print($the_end);   $request->print($the_end);
     }      }
     return '';      return '';
Line 2711  sub saveHandGrade { Line 3095  sub saveHandGrade {
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
     my @parts_graded;      my @parts_graded;
     my %newrecord  = ();      my %newrecord  = ();
     my ($pts,$wgt) = ('','');      my ($pts,$wgt,$totchg) = ('','',0);
     my %aggregate = ();      my %aggregate = ();
     my $aggregateflag = 0;      my $aggregateflag = 0;
       if ($env{'form.HIDE'.$newflg}) {
           my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2);
           my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);
           $totchg += $numchgs;
       }
     my @parts = split(/:/,$env{'form.partlist'.$newflg});      my @parts = split(/:/,$env{'form.partlist'.$newflg});
     foreach my $new_part (@parts) {      foreach my $new_part (@parts) {
  #collaborator ($submi may vary for different parts   #collaborator ($submi may vary for different parts
Line 2816  sub saveHandGrade { Line 3205  sub saveHandGrade {
         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,          &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
       $cdom,$cnum);        $cdom,$cnum);
     }      }
     return ('',$pts,$wgt);      return ('',$pts,$wgt,$totchg);
   }
   
   sub makehidden {
       my ($version,$parts,$record,$symb,$domain,$stuname,$tolog) = @_;
       return unless (ref($record) eq 'HASH');
       my %modified;
       my $numchanged = 0;
       if (exists($record->{$version.':keys'})) {
           my $partsregexp = $parts;
           $partsregexp =~ s/,/|/g;
           foreach my $key (split(/\:/,$record->{$version.':keys'})) {
               if ($key =~ /^resource\.(?:$partsregexp)\.([^\.]+)$/) {
                    my $item = $1;
                    unless (($item eq 'solved') || ($item =~ /^award(|msg|ed)$/)) {
                        $modified{$key} = $record->{$version.':'.$key};
                    }
               } elsif ($key =~ m{^(resource\.(?:$partsregexp)\.[^\.]+\.)(.+)$}) {
                   $modified{$1.'hidden'.$2} = $record->{$version.':'.$key};
               } elsif ($key =~ /^(ip|timestamp|host)$/) {
                   $modified{$key} = $record->{$version.':'.$key};
               }
           }
           if (keys(%modified)) {
               if (&Apache::lonnet::putstore($env{'request.course.id'},$symb,$version,\%modified,
                                             $domain,$stuname,$tolog) eq 'ok') {
                   $numchanged ++;
               }
           }
       }
       return $numchanged;
 }  }
   
 sub check_and_remove_from_queue {  sub check_and_remove_from_queue {
Line 2846  sub handback_files { Line 3265  sub handback_files {
         $request->print('<br />'.&navmap_errormsg().'<br />');          $request->print('<br />'.&navmap_errormsg().'<br />');
         return;          return;
     }      }
       my @handedback;
       my $file_msg;
     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) {
     my ($part_id,$resp_id) = @{ $part_response_id };      my ($part_id,$resp_id) = @{ $part_response_id };
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
             if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) {          if (($env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'} =~ /^\d+$/) & ($new_part eq $part_id)) {
                 # if multiple files are uploaded names will be 'returndoc2','returndoc3'              for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {
                 my $file_counter = 1;                  # if multiple files are uploaded names will be 'returndoc2','returndoc3' 
  my $file_msg;                  if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
                 while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {                      my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};
                     my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'};  
                     my ($directory,$answer_file) =                       my ($directory,$answer_file) = 
                         ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/);                          ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);
                     my ($answer_name,$answer_ver,$answer_ext) =                      my ($answer_name,$answer_ver,$answer_ext) =
         &file_name_version_ext($answer_file);          &Apache::lonnet::file_name_version_ext($answer_file);
     my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);      my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
                     my $getpropath = 1;                      my $getpropath = 1;
     my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,$domain,$stuname,$getpropath);                      my ($dir_list,$listerror) = 
     my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);                          &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,
                     # fix file name                                                   $domain,$stuname,$getpropath);
       my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);
                       # fix filename
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);                      my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                     my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,                      my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
                                            $newflg.'_'.$part_resp.'_returndoc'.$file_counter,                                             $newflg.'_'.$part_resp.'_returndoc'.$counter,
                                            $save_file_name);                                             $save_file_name);
                     if ($result !~ m|^/uploaded/|) {                      if ($result !~ m|^/uploaded/|) {
                         $request->print('<br /><span class="LC_error">'.                          $request->print('<br /><span class="LC_error">'.
                             &mt('An error occurred ([_1]) while trying to upload [_2].',                              &mt('An error occurred ([_1]) while trying to upload [_2].',
                                 $result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter).                                  $result,$newflg.'_'.$part_resp.'_returndoc'.$counter).
                                         '</span>');                                          '</span>');
                     } else {                      } else {
                         # mark the file as read only                          # mark the file as read only
                         my @files = ($save_file_name);                          push(@handedback,$save_file_name);
                         my @what = ($symb,$env{'request.course.id'},'handback');  
                         &Apache::lonnet::mark_as_readonly($domain,$stuname,\@files,\@what);  
  if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) {   if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) {
     $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';      $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';
  }   }
                         $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;                          $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;
  $file_msg.= "\n".'<br /><span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span><br />";   $file_msg.= '<span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span> <br />";
   
                     }                      }
                     $request->print("<br />".$fname." will be the uploaded file name");                      $request->print('<br />'.&mt('[_1] will be the uploaded filename [_2]','<span class="LC_info">'.$fname.'</span>','<span class="LC_filename">'.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'</span>'));
                     $request->print(" ".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter});                  }
                     $file_counter++;  
                 }  
  my $subject = "File Handed Back by Instructor ";  
  my $message = "A file has been returned that was originally submitted in reponse to: <br />";  
  $message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />";  
  $message .= ' The returned file(s) are named: '. $file_msg;  
  $message .= " and can be found in your portfolio space.";  
  my ($feedurl,$showsymb) =   
     &get_feedurl_and_symb($symb,$domain,$stuname);  
                 my $restitle = &Apache::lonnet::gettitle($symb);  
  my $msgstatus =   
                    &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject.  
  ' (File Returned) ['.$restitle.']',$message,undef,  
                          $feedurl,undef,undef,undef,$showsymb,$restitle);  
             }              }
         }          }
       }
       if (@handedback > 0) {
           $request->print('<br />');
           my @what = ($symb,$env{'request.course.id'},'handback');
           &Apache::lonnet::mark_as_readonly($domain,$stuname,\@handedback,\@what);
           my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});    
           my ($subject,$message);
           if (scalar(@handedback) == 1) {
               $subject = &mt_user($user_lh,'File Handed Back by Instructor');
               $message = &mt_user($user_lh,'A file has been returned that was originally submitted in response to: ');
           } else {
               $subject = &mt_user($user_lh,'Files Handed Back by Instructor');
               $message = &mt_user($user_lh,'Files have been returned that were originally submitted in response to: ');
           }
           $message .= "<p><strong>".&Apache::lonnet::gettitle($symb)." </strong></p>";
           $message .= &mt_user($user_lh,'The returned file(s) are named: [_1]',"<br />$file_msg <br />").
                       &mt_user($user_lh,'The file(s) can be found in your [_1]portfolio[_2].','<a href="/adm/portfolio">','</a>');
           my ($feedurl,$showsymb) =
               &get_feedurl_and_symb($symb,$domain,$stuname);
           my $restitle = &Apache::lonnet::gettitle($symb);
           $subject .= ' '.&mt_user($user_lh,'(File Returned)').' ['.$restitle.']';
           my $msgstatus =
                &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject,
                    $message,undef,$feedurl,undef,undef,undef,$showsymb,
                    $restitle);
           if ($msgstatus) {
               $request->print(&mt('Notification message status: [_1]','<span class="LC_info">'.$msgstatus.'</span>').'<br />');
           }
       }
     return;      return;
 }  }
   
Line 3000  sub version_portfiles { Line 3434  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 = '/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$/ ) {
             my @versioned_portfiles;              my @versioned_portfiles;
             my @portfiles = split(/\s*,\s*/,$$record{$key});              my @portfiles = split(/\s*,\s*/,$$record{$key});
             foreach my $file (@portfiles) {              if (@portfiles) {
                 &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);                  &Apache::lonnet::portfiles_versioning($symb,$domain,$stu_name,\@portfiles,
                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);                                                        \@versioned_portfiles);
  my ($answer_name,$answer_ver,$answer_ext) =  
     &file_name_version_ext($answer_file);  
                 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 $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);  
                 if ($new_answer ne 'problem getting file') {  
                     push(@versioned_portfiles, $directory.$new_answer);  
                     &Apache::lonnet::mark_as_readonly($domain,$stu_name,  
                         [$directory.$new_answer],  
                         [$symb,$env{'request.course.id'},'graded']);  
                 }  
             }              }
             $$record{$key} = join(',',@versioned_portfiles);              $$record{$key} = join(',',@versioned_portfiles);
             push(@returned_keys,$key);              push(@returned_keys,$key);
Line 3029  sub version_portfiles { Line 3450  sub version_portfiles {
     return (@returned_keys);         return (@returned_keys);   
 }  }
   
 sub get_next_version {  
     my ($answer_name, $answer_ext, $dir_list) = @_;  
     my $version;  
     foreach my $row (@$dir_list) {  
         my ($file) = split(/\&/,$row,2);  
         my ($file_name,$file_version,$file_ext) =  
     &file_name_version_ext($file);  
         if (($file_name eq $answer_name) &&   
     ($file_ext eq $answer_ext)) {  
                 # gets here if filename and extension match, regardless of version  
                 if ($file_version ne '') {  
                 # a versioned file is found  so save it for later  
                 if ($file_version > $version) {  
     $version = $file_version;  
         }  
             }  
         }  
     }   
     $version ++;  
     return($version);  
 }  
   
 sub version_selected_portfile {  
     my ($domain,$stu_name,$directory,$file_name,$version) = @_;  
     my ($answer_name,$answer_ver,$answer_ext) =  
         &file_name_version_ext($file_name);  
     my $new_answer;  
     $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");  
     if($env{'form.copy'} eq '-1') {  
         $new_answer = 'problem getting file';  
     } else {  
         $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;  
         my $copy_result = &Apache::lonnet::finishuserfileupload(  
                             $stu_name,$domain,'copy',  
         '/portfolio'.$directory.$new_answer);  
     }      
     return ($new_answer);  
 }  
   
 sub file_name_version_ext {  
     my ($file)=@_;  
     my @file_parts = split(/\./, $file);  
     my ($name,$version,$ext);  
     if (@file_parts > 1) {  
  $ext=pop(@file_parts);  
  if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) {  
     $version=pop(@file_parts);  
  }  
  $name=join('.',@file_parts);  
     } else {  
  $name=join('.',@file_parts);  
     }  
     return($name,$version,$ext);  
 }  
   
 #--------------------------------------------------------------------------------------  #--------------------------------------------------------------------------------------
 #  #
 #-------------------------- Next few routines handles grading by section or whole class  #-------------------------- Next few routines handles grading by section or whole class
Line 3093  sub viewgrades_js { Line 3459  sub viewgrades_js {
     my ($request) = shift;      my ($request) = shift;
   
     my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');      my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
       &js_escape(\$alertmsg);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<VIEWJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<VIEWJAVASCRIPT));
    function writePoint(partid,weight,point) {     function writePoint(partid,weight,point) {
  var radioButton = document.classgrade["RADVAL_"+partid];   var radioButton = document.classgrade["RADVAL_"+partid];
Line 3260  VIEWJAVASCRIPT Line 3627  VIEWJAVASCRIPT
 #--- show scores for a section or whole class w/ option to change/update a score  #--- show scores for a section or whole class w/ option to change/update a score
 sub viewgrades {  sub viewgrades {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
       my ($is_tool,$toolsymb);
       if ($symb =~ /ext\.tool$/) {
           $is_tool = 1;
           $toolsymb = $symb;
       }
     &viewgrades_js($request);      &viewgrades_js($request);
   
     #need to make sure we have the correct data for later EXT calls,       #need to make sure we have the correct data for later EXT calls, 
Line 3282  sub viewgrades { Line 3654  sub viewgrades {
  &build_section_inputs().   &build_section_inputs().
  '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".   '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".
   
     my ($common_header,$specific_header);      #retrieve selected groups
     if ($env{'form.section'} eq 'all') {      my (@groups,$group_display);
  $common_header = &mt('Assign Common Grade to Class');      @groups = &Apache::loncommon::get_env_multiple('form.group');
         $specific_header = &mt('Assign Grade to Specific Students in Class');      if (grep(/^all$/,@groups)) {
     } elsif ($env{'form.section'} eq 'none') {          @groups = ('all');
         $common_header = &mt('Assign Common Grade to Students in no Section');      } elsif (grep(/^none$/,@groups)) {
  $specific_header = &mt('Assign Grade to Specific Students in no Section');          @groups = ('none');
     } else {      } elsif (@groups > 0) {
         my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));          $group_display = join(', ',@groups);
         $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);      }
  $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);  
       my ($common_header,$specific_header,@sections,$section_display);
       @sections = &Apache::loncommon::get_env_multiple('form.section');
       if (grep(/^all$/,@sections)) {
           @sections = ('all');
           if ($group_display) {
               $common_header = &mt('Assign Common Grade to Students in Group(s) [_1]',$group_display);
               $specific_header = &mt('Assign Grade to Specific Students in Group(s) [_1]',$group_display);
           } elsif (grep(/^none$/,@groups)) {
               $common_header = &mt('Assign Common Grade to Students not assigned to any groups');
               $specific_header = &mt('Assign Grade to Specific Students not assigned to any groups');
           } else {
       $common_header = &mt('Assign Common Grade to Class');
               $specific_header = &mt('Assign Grade to Specific Students in Class');
           }
       } elsif (grep(/^none$/,@sections)) {
           @sections = ('none');
           if ($group_display) {
               $common_header = &mt('Assign Common Grade to Students in no Section and in Group(s) [_1]',$group_display);
               $specific_header = &mt('Assign Grade to Specific Students in no Section and in Group(s)',$group_display);
           } elsif (grep(/^none$/,@groups)) {
               $common_header = &mt('Assign Common Grade to Students in no Section and in no Group');
               $specific_header = &mt('Assign Grade to Specific Students in no Section and in no Group');
           } else {
               $common_header = &mt('Assign Common Grade to Students in no Section');
       $specific_header = &mt('Assign Grade to Specific Students in no Section');
           }
       } else {
           $section_display = join (", ",@sections);
           if ($group_display) {
               $common_header = &mt('Assign Common Grade to Students in Section(s) [_1], and in Group(s) [_2]',
                                    $section_display,$group_display);
               $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1], and in Group(s) [_2]',
                                      $section_display,$group_display);
           } elsif (grep(/^none$/,@groups)) {
               $common_header = &mt('Assign Common Grade to Students in Section(s) [_1] and no Group',$section_display);
               $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1] and no Group',$section_display);
           } else {
               $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);
       $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);
           }
       }
       my %submit_types = &substatus_options();
       my $submission_status = $submit_types{$env{'form.submitonly'}};
   
       if ($env{'form.submitonly'} eq 'all') {
           $result.= '<h3>'.$common_header.'</h3>';
       } else {
           my $text;
           if ($is_tool) {
               $text = &mt('(transaction status: "[_1]")',$submission_status);
           } else {
               $text = &mt('(submission status: "[_1]")',$submission_status);
           }
           $result.= '<h3>'.$common_header.'&nbsp;'.$text.'</h3>';
     }      }
     $result.= '<h3>'.$common_header.'</h3>'.&Apache::loncommon::start_data_table();      $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 $res_error;      my $res_error;
Line 3305  sub viewgrades { Line 3731  sub viewgrades {
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     my %seen = ();      my %seen = ();
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id;
       if ($is_tool) {
           @part_response_id = ([0,'']);
       } else {
           @part_response_id = &flatten_responseType($responseType);
       }
     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 });
  next if $seen{$partid};   next if $seen{$partid};
  $seen{$partid}++;   $seen{$partid}++;
  my $handgrade=$$handgrade{$part_resp};  # my $handgrade=$$handgrade{$part_resp};
  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;
   
Line 3330  sub viewgrades { Line 3761  sub viewgrades {
     $partid.'" size="4" '.'onchange="javascript:writePoint(\''.      $partid.'" size="4" '.'onchange="javascript:writePoint(\''.
  $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.   $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
     $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";      $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";
  $line.= '<td><b>'.&mt('Grade Status').':</b><select name="SELVAL_'.$partid.'"'.          $line.= '<td><b>'.&mt('Grade Status').':</b>'.
     'onchange="javascript:writeRadText(\''.$partid.'\','.              '<select name="SELVAL_'.$partid.'" '.
  $weight{$partid}.')"> '.              'onchange="javascript:writeRadText(\''.$partid.'\','.
                   $weight{$partid}.')"> '.
     '<option selected="selected"> </option>'.      '<option selected="selected"> </option>'.
     '<option value="excused">'.&mt('excused').'</option>'.      '<option value="excused">'.&mt('excused').'</option>'.
     '<option value="reset status">'.&mt('reset status').'</option>'.      '<option value="reset status">'.&mt('reset status').'</option>'.
Line 3356  sub viewgrades { Line 3788  sub viewgrades {
   
     #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>'.$specific_header.'</h3>'.      if ($env{'form.submitonly'} eq 'all') {
               &Apache::loncommon::start_data_table().          $result.= '<h3>'.$specific_header.'</h3>';
       } else {
           my $text;
           if ($is_tool) {
               $text = &mt('(transaction status: "[_1]")',$submission_status);
           } else {
               $text = &mt('(submission status: "[_1]")',$submission_status);
           }
           $result.= '<h3>'.$specific_header.'&nbsp;'.$text.'</h3>';
       }
       $result.= &Apache::loncommon::start_data_table().
       &Apache::loncommon::start_data_table_header_row().        &Apache::loncommon::start_data_table_header_row().
       '<th>'.&mt('No.').'</th>'.        '<th>'.&mt('No.').'</th>'.
       '<th>'.&nameUserString('header')."</th>\n";        '<th>'.&nameUserString('header')."</th>\n";
Line 3369  sub viewgrades { Line 3811  sub viewgrades {
     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',$toolsymb);
         my $narrowtext = &mt('Tries');          my $narrowtext = &mt('Tries');
  $display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower   $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',$toolsymb); }
  my ($partid) = &split_part_type($part);   my ($partid) = &split_part_type($part);
         push(@partids,$partid);          push(@partids,$partid);
   #
   # FIXME: Looks like $display looks at English text
   #
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  if ($display =~ /^Partial Credit Factor/) {   if ($display =~ /^Partial Credit Factor/) {
     $result.='<th>'.      $result.='<th>'.
  &mt('Score Part: [_1]<br /> (weight = [_2])',   &mt('Score Part: [_1][_2](weight = [_3])',
     $display_part,$weight{$partid}).'</th>'."\n";      $display_part,'<br />',$weight{$partid}).'</th>'."\n";
     next;      next;
           
  } else {   } else {
Line 3400  sub viewgrades { Line 3845  sub viewgrades {
   
     #get info for each student      #get info for each student
     #list all the students - with points and grade status      #list all the students - with points and grade status
     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');      my (undef,undef,$fullname) = &getclasslist(\@sections,'1',\@groups);
     my $ctr = 0;      my $ctr = 0;
     foreach (sort       foreach (sort 
      {       {
Line 3409  sub viewgrades { Line 3854  sub viewgrades {
  }   }
  return $a cmp $b;   return $a cmp $b;
      } (keys(%$fullname))) {       } (keys(%$fullname))) {
  $ctr++;  
  $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,$is_tool);
     }      }
     $result.=&Apache::loncommon::end_data_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="'.&mt('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 ($ctr == 0) {
  my $colspan=3+scalar(@parts);  
  my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));  
         my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));          my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));
  $result='<span class="LC_warning">'.          $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>'.
     &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade.',                  '<span class="LC_warning">';
         $section_display, $stu_status).          if ($env{'form.submitonly'} eq 'all') {
     '</span>';              if (grep(/^all$/,@sections)) {
                   if (grep(/^all$/,@groups)) {
                       $result .= &mt('There are no students with enrollment status [_1] to modify or grade.',
                                      $stu_status);
                   } elsif (grep(/^none$/,@groups)) {
                       $result .= &mt('There are no students with no group assigned and with enrollment status [_1] to modify or grade.',
                                      $stu_status); 
                   } else {
                       $result .= &mt('There are no students in group(s) [_1] with enrollment status [_2] to modify or grade.',
                                      $group_display,$stu_status);
                   }
               } elsif (grep(/^none$/,@sections)) {
                   if (grep(/^all$/,@groups)) {
                       $result .= &mt('There are no students in no section with enrollment status [_1] to modify or grade.',
                                      $stu_status);
                   } elsif (grep(/^none$/,@groups)) {
                       $result .= &mt('There are no students in no section and no group with enrollment status [_1] to modify or grade.',
                                      $stu_status);
                   } else {
                       $result .= &mt('There are no students in no section in group(s) [_1] with enrollment status [_2] to modify or grade.',
                                      $group_display,$stu_status);
                   }
               } else {
                   if (grep(/^all$/,@groups)) {
                       $result .= &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade.',
                                      $section_display,$stu_status);
                   } elsif (grep(/^none$/,@groups)) {
                       $result .= &mt('There are no students in section(s) [_1] and no group with enrollment status [_2] to modify or grade.',
                                      $section_display,$stu_status);
                   } else {
                       $result .= &mt('There are no students in section(s) [_1] and group(s) [_2] with enrollment status [_3] to modify or grade.',
                                      $section_display,$group_display,$stu_status);
                   }
               }
           } else {
               if (grep(/^all$/,@sections)) {
                   if (grep(/^all$/,@groups)) {
                       $result .= &mt('There are no students with enrollment status [_1] and submission status "[_2]" to modify or grade.',
                                      $stu_status,$submission_status);
                   } elsif (grep(/^none$/,@groups)) {
                       $result .= &mt('There are no students with no group assigned with enrollment status [_1] and submission status "[_2]" to modify or grade.',
                                      $stu_status,$submission_status);
                   } else {
                       $result .= &mt('There are no students in group(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.',
                                      $group_display,$stu_status,$submission_status);
                   }
               } elsif (grep(/^none$/,@sections)) {
                   if (grep(/^all$/,@groups)) {
                       $result .= &mt('There are no students in no section with enrollment status [_1] and submission status "[_2]" to modify or grade.',
                                      $stu_status,$submission_status);
                   } elsif (grep(/^none$/,@groups)) {
                       $result .= &mt('There are no students in no section and no group with enrollment status [_1] and submission status "[_2]" to modify or grade.',
                                      $stu_status,$submission_status);
                   } else {
                       $result .= &mt('There are no students in no section in group(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.',
                                      $group_display,$stu_status,$submission_status);
                   }
               } else {
                   if (grep(/^all$/,@groups)) {
               $result .= &mt('There are no students in section(s) [_1] with enrollment status [_2] and submission status "[_3]" to modify or grade.',
                              $section_display,$stu_status,$submission_status);
                   } elsif (grep(/^none$/,@groups)) {
                       $result .= &mt('There are no students in section(s) [_1] and no group with enrollment status [_2] and submission status "[_3]" to modify or grade.',
                                      $section_display,$stu_status,$submission_status);
                   } else {
                       $result .= &mt('There are no students in section(s) [_1] and group(s) [_2] with enrollment status [_3] and submission status "[_4]" to modify or grade.',
                                      $section_display,$group_display,$stu_status,$submission_status);
                   }
               }
           }
    $result .= '</span><br />';
     }      }
     return $result;      return $result;
 }  }
   
 #--- call by previous routine to display each student  #--- call by previous routine to display each student who satisfies submission filter. 
 sub viewstudentgrade {  sub viewstudentgrade {
     my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;      my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets,$is_tool) = @_;
     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 $submitonly = $env{'form.submitonly'};
       unless (($submitonly eq 'all') || ($submitonly eq 'queued')) {
           my %partstatus = ();
           if (ref($parts) eq 'ARRAY') {
               foreach my $apart (@{$parts}) {
                   my ($part,$type) = &split_part_type($apart);
                   my ($status,undef) = split(/_/,$record{"resource.$part.solved"},2);
                   $status = 'nothing' if ($status eq '');
                   $partstatus{$part}      = $status;
                   my $subkey = "resource.$part.submitted_by";
                   $partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne '');
               }
               my $submitted = 0;
               my $graded = 0;
               my $incorrect = 0;
               foreach my $key (keys(%partstatus)) {
                   $submitted = 1 if ($partstatus{$key} ne 'nothing');
                   $graded = 1 if ($partstatus{$key} =~ /^ungraded/);
                   $incorrect = 1 if ($partstatus{$key} =~ /^incorrect/);
   
                   my $partid = (split(/\./,$key))[1];
                   if ($partstatus{'resource.'.$partid.'.'.$key.'.submitted_by'} ne '') {
                       $submitted = 0;
                   }
               }
               return if (!$submitted && ($submitonly eq 'yes' ||
                                          $submitonly eq 'incorrect' ||
                                          $submitonly eq 'graded'));
               return if (!$graded && ($submitonly eq 'graded'));
               return if (!$incorrect && $submitonly eq 'incorrect');
           }
       }
       if ($submitonly eq 'queued') {
           my ($cdom,$cnum) = split(/_/,$courseid);
           my %queue_status =
               &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
                                                       $udom,$uname);
           return if (!defined($queue_status{'gradingqueue'}));
       }
       $$ctr++;
       my %aggregates = ();
     my $result=&Apache::loncommon::start_data_table_row().'<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.
  '\');" target="_self">'.$fullname.'</a> '.   '\');" target="_self">'.$fullname.'</a> '.
  '<span class="LC_internal_info">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</span></td>'."\n";   '<span class="LC_internal_info">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</span></td>'."\n";
Line 3449  sub viewstudentgrade { Line 4001  sub viewstudentgrade {
         my ($aggtries,$totaltries);          my ($aggtries,$totaltries);
         unless (exists($aggregates{$part})) {          unless (exists($aggregates{$part})) {
     $totaltries = $record{'resource.'.$part.'.tries'};      $totaltries = $record{'resource.'.$part.'.tries'};
   
     $aggtries = $totaltries;      $aggtries = $totaltries;
             if ($$last_resets{$part}) {                if ($$last_resets{$part}) {  
                 $aggtries = &get_num_tries(\%record,$$last_resets{$part},                  $aggtries = &get_num_tries(\%record,$$last_resets{$part},
Line 3498  sub viewstudentgrade { Line 4049  sub viewstudentgrade {
 #    record does not get update if unchanged  #    record does not get update if unchanged
 sub editgrades {  sub editgrades {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
       my $toolsymb;
       if ($symb =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
   
     my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));      my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     my $title='<h2>'.&mt('Current Grade Status').'</h2>';      my $title='<h2>'.&mt('Current Grade Status').'</h2>';
Line 3535  sub editgrades { Line 4090  sub editgrades {
  $ctr++;   $ctr++;
     }      }
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
       my $totcolspan = 0;
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  $header .= '<th align="center">'.&mt('Old Score').'</th>'.   $header .= '<th align="center">'.&mt('Old Score').'</th>'.
     '<th align="center">'.&mt('New Score').'</th>';      '<th align="center">'.&mt('New Score').'</th>';
Line 3543  sub editgrades { Line 4099  sub editgrades {
     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',$toolsymb);
     $display =~ s/\[Part: \Q$part\E\]//;      $display =~ s/\[Part: \Q$part\E\]//;
             my $narrowtext = &mt('Tries');              my $narrowtext = &mt('Tries');
     $display =~ s/Number of Attempts/$narrowtext/;      $display =~ s/Number of Attempts/$narrowtext/;
Line 3551  sub editgrades { Line 4107  sub editgrades {
  '<th align="center">'.&mt('New').' '.$display.'</th>';   '<th align="center">'.&mt('New').' '.$display.'</th>';
     $columns{$partid}+=2;      $columns{$partid}+=2;
  }   }
           $totcolspan += $columns{$partid};
     }      }
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
Line 3566  sub editgrades { Line 4123  sub editgrades {
     my @noupdate;      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 $user = $env{'form.ctr'.$i};   my $user = $env{'form.ctr'.$i};
  my ($uname,$udom)=split(/:/,$user);   my ($uname,$udom)=split(/:/,$user);
  my %newrecord;   my %newrecord;
  my $updateflag = 0;   my $updateflag = 0;
  $line .= '<td>'.&nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';  
  my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
  if (!&canmodify($usec)) {   my $canmodify = &canmodify($usec);
     my $numcols=scalar(@partid)*4+2;   my $line = '<td'.($canmodify?'':' colspan="2"').'>'.
      &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
    if (!$canmodify) {
     push(@noupdate,      push(@noupdate,
  $line."<td colspan=\"$numcols\"><span class=\"LC_warning\">".   $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".
  &mt('Not allowed to modify student')."</span></td></tr>");   &mt('Not allowed to modify student')."</span></td>");
     next;      next;
  }   }
         my %aggregate = ();          my %aggregate = ();
Line 3694  sub editgrades { Line 4251  sub editgrades {
         }          }
     }      }
     if (@noupdate) {      if (@noupdate) {
 # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;          my $numcols=$totcolspan+2;
  my $numcols=scalar(@partid)*4+2;  
  $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').   $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').
     '<td align="center" colspan="'.$numcols.'">'.      '<td align="center" colspan="'.$numcols.'">'.
     &mt('No Changes Occurred For the Students Below').      &mt('No Changes Occurred For the Students Below').
Line 3736  sub split_part_type { Line 4292  sub split_part_type {
 #  #
 #--- Javascript to handle csv upload  #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {  sub csvupload_javascript_reverse_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
     &js_escape(\$error1);
     &js_escape(\$error2);
   return(<<ENDPICK);    return(<<ENDPICK);
   function verify(vf) {    function verify(vf) {
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (i==0 && tw!=0) { foundID=1; }        if (i==0 && tw!=0) { foundID=1; }
       if (i==1 && tw!=0) { founduname=1; }        if (i==1 && tw!=0) { founduname=1; }
       if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }        if (i==2 && tw!=0) { foundclicker=1; }
         if (i!=0 && i!=1 && i!=2 && i!=3 && tw!=0) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && foundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 3776  ENDPICK Line 4336  ENDPICK
 }  }
   
 sub csvupload_javascript_forward_associate {  sub csvupload_javascript_forward_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
     &js_escape(\$error1);
     &js_escape(\$error2);
   return(<<ENDPICK);    return(<<ENDPICK);
   function verify(vf) {    function verify(vf) {
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (tw==1) { foundID=1; }        if (tw==1) { foundID=1; }
       if (tw==2) { founduname=1; }        if (tw==2) { founduname=1; }
       if (tw>3) { foundsomething=1; }        if (tw==3) { foundclicker=1; }
         if (tw>4) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && Æ’oundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 3821  sub csvuploadmap_header { Line 4385  sub csvuploadmap_header {
  $javascript=&csvupload_javascript_forward_associate();   $javascript=&csvupload_javascript_forward_associate();
     }      }
   
     my $result='';  
     my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');  
     my $ignore=&mt('Ignore First Line');  
     $symb = &Apache::lonenc::check_encrypt($symb);      $symb = &Apache::lonenc::check_encrypt($symb);
       $request->print('<form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">'.
                       &mt('Total number of records found in file: [_1]',$distotal).'<hr />'.
                       &mt('Associate entries from the uploaded file with as many fields as you can.'));
       my $reverse=&mt("Reverse Association");
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <br />
 <h3><span class="LC_info">Uploading Class Grades</span></h3>  <input type="button" value="$reverse" onclick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
 $result  
 <hr />  
 <h3>Identify fields</h3>  
 Total number of records found in file: $distotal <hr />  
 Enter as many fields as you can. The system will inform you and bring you back  
 to this page if the data selected is insufficient to run your class.<hr />  
 <input type="button" value="Reverse Association" onclick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />  
 <label><input type="checkbox" name="noFirstLine" $checked />$ignore</label>  
 <input type="hidden" name="associate"  value="" />  <input type="hidden" name="associate"  value="" />
 <input type="hidden" name="phase"      value="three" />  <input type="hidden" name="phase"      value="three" />
 <input type="hidden" name="datatoken"  value="$datatoken" />  <input type="hidden" name="datatoken"  value="$datatoken" />
Line 3854  ENDPICK Line 4411  ENDPICK
   
 sub csvupload_fields {  sub csvupload_fields {
     my ($symb,$errorref) = @_;      my ($symb,$errorref) = @_;
       my $toolsymb;
       if ($symb =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
     my (@parts) = &getpartlist($symb,$errorref);      my (@parts) = &getpartlist($symb,$errorref);
     if (ref($errorref)) {      if (ref($errorref)) {
         if ($$errorref) {          if ($$errorref) {
Line 3863  sub csvupload_fields { Line 4424  sub csvupload_fields {
   
     my @fields=(['ID','Student/Employee ID'],      my @fields=(['ID','Student/Employee ID'],
  ['username','Student Username'],   ['username','Student Username'],
    ['clicker','Clicker ID'],
  ['domain','Student Domain']);   ['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $part (sort(@parts)) {      foreach my $part (sort(@parts)) {
  my @datum;   my @datum;
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
  my $name=$part;   my $name=$part;
  if  (!$display) { $display = $name; }   if (!$display) { $display = $name; }
  @datum=($name,$display);   @datum=($name,$display);
  if ($name=~/^stores_(.*)_awarded/) {   if ($name=~/^stores_(.*)_awarded/) {
     push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);      push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
Line 3881  sub csvupload_fields { Line 4443  sub csvupload_fields {
   
 sub csvuploadmap_footer {  sub csvuploadmap_footer {
     my ($request,$i,$keyfields) =@_;      my ($request,$i,$keyfields) =@_;
       my $buttontext = &mt('Assign Grades');
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 </table>  </table>
 <input type="hidden" name="nfields" value="$i" />  <input type="hidden" name="nfields" value="$i" />
 <input type="hidden" name="keyfields" value="$keyfields" />  <input type="hidden" name="keyfields" value="$keyfields" />
 <input type="button" onclick="javascript:verify(this.form)" value="Assign Grades" /><br />  <input type="button" onclick="javascript:verify(this.form)" value="$buttontext" /><br />
 </form>  </form>
 ENDPICK  ENDPICK
 }  }
   
 sub checkforfile_js {  sub checkforfile_js {
     my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');      my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
       &js_escape(\$alertmsg);
     my $result = &Apache::lonhtmlcommon::scripttag(<<CSVFORMJS);      my $result = &Apache::lonhtmlcommon::scripttag(<<CSVFORMJS);
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
Line 3908  sub upcsvScores_form { Line 4472  sub upcsvScores_form {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $result=&checkforfile_js();      my $result=&checkforfile_js();
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";      $result.=&Apache::loncommon::start_data_table().
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";               &Apache::loncommon::start_data_table_header_row().
     $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource.').               '<th>'.&mt('Specify a file containing the class scores for current resource.').'</th>'.
  '</b></td></tr>'."\n";               &Apache::loncommon::end_data_table_header_row().
     $result.='<tr bgcolor=#ffffe6><td>'."\n";               &Apache::loncommon::start_data_table_row().'<td>';
     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();
     my $ignore=&mt('Ignore First Line');      my $ignore=&mt('Ignore First Line');
Line 3923  sub upcsvScores_form { Line 4487  sub upcsvScores_form {
 <input type="hidden" name="command" value="csvuploadmap" />  <input type="hidden" name="command" value="csvuploadmap" />
 $upfile_select  $upfile_select
 <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />  <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 <label><input type="checkbox" name="noFirstLine" />$ignore</label>  
 </form>  </form>
 ENDUPFORM  ENDUPFORM
     $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV",      $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV",
                            &mt("How do I create a CSV file from a spreadsheet"))                             &mt("How do I create a CSV file from a spreadsheet")).
     .'</td></tr></table>'."\n";               '</td>'.
     $result.='</td></tr></table><br /><br />'."\n";              &Apache::loncommon::end_data_table_row().
               &Apache::loncommon::end_data_table();
     return $result;      return $result;
 }  }
   
Line 3942  sub csvuploadmap { Line 4506  sub csvuploadmap {
     if (!$env{'form.datatoken'}) {      if (!$env{'form.datatoken'}) {
  $datatoken=&Apache::loncommon::upfile_store($request);   $datatoken=&Apache::loncommon::upfile_store($request);
     } else {      } else {
  $datatoken=$env{'form.datatoken'};   $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
  &Apache::loncommon::load_tmp_file($request);          if ($datatoken ne '') {
       &Apache::loncommon::load_tmp_file($request,$datatoken);
           }
     }      }
     my @records=&Apache::loncommon::upfile_record_sep();      my @records=&Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@records); }  
     &csvuploadmap_header($request,$symb,$datatoken,$#records+1);      &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
     my ($i,$keyfields);      my ($i,$keyfields);
     if (@records) {      if (@records) {
Line 3982  sub csvuploadmap { Line 4547  sub csvuploadmap {
   
 sub csvuploadoptions {  sub csvuploadoptions {
     my ($request,$symb)= @_;      my ($request,$symb)= @_;
     my $checked=(($env{'form.noFirstLine'})?'1':'0');      my $overwrite=&mt('Overwrite any existing score');
     my $ignore=&mt('Ignore First Line');  
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <h3><span class="LC_info">Uploading Class Grade Options</span></h3>  
 <input type="hidden" name="command"    value="csvuploadassign" />  <input type="hidden" name="command"    value="csvuploadassign" />
 <!--  
 <p>  
 <label>  
    <input type="checkbox" name="show_full_results" />  
    Show a table of all changes  
 </label>  
 </p>  
 -->  
 <p>  <p>
 <label>  <label>
    <input type="checkbox" name="overwite_scores" checked="checked" />     <input type="checkbox" name="overwite_scores" checked="checked" />
    Overwrite any existing score     $overwrite
 </label>  </label>
 </p>  </p>
 ENDPICK  ENDPICK
     my %fields=&get_fields();      my %fields=&get_fields();
     if (!defined($fields{'domain'})) {      if (!defined($fields{'domain'})) {
  my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');   my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');
  $request->print("\n<p> Users are in domain: ".$domform."</p>\n");   $request->print("\n<p>".&mt('Users are in domain: [_1]',$domform)."</p>\n");
     }      }
     foreach my $key (sort(keys(%env))) {      foreach my $key (sort(keys(%env))) {
  if ($key !~ /^form\.(.*)$/) { next; }   if ($key !~ /^form\.(.*)$/) { next; }
Line 4017  ENDPICK Line 4572  ENDPICK
     }      }
     # FIXME do a check for any duplicated user ids...      # FIXME do a check for any duplicated user ids...
     # FIXME do a check for any invalid user ids?...      # FIXME do a check for any invalid user ids?...
     $request->print('<input type="submit" value="Assign Grades" /><br />      $request->print('<input type="submit" value="'.&mt('Assign Grades').'" /><br />
 <hr /></form>'."\n");  <hr /></form>'."\n");
     return '';      return '';
 }  }
Line 4043  sub csvuploadassign { Line 4598  sub csvuploadassign {
     my ($request,$symb)= @_;      my ($request,$symb)= @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $error_msg = '';      my $error_msg = '';
     &Apache::loncommon::load_tmp_file($request);      my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
       if ($datatoken ne '') { 
           &Apache::loncommon::load_tmp_file($request,$datatoken);
       }
     my @gradedata = &Apache::loncommon::upfile_record_sep();      my @gradedata = &Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@gradedata); }  
     my %fields=&get_fields();      my %fields=&get_fields();
     $request->print('<h3>Assigning Grades</h3>');  
     my $courseid=$env{'request.course.id'};      my $courseid=$env{'request.course.id'};
     my ($classlist) = &getclasslist('all',0);      my ($classlist) = &getclasslist('all',0);
     my @notallowed;      my @notallowed;
     my @skipped;      my @skipped;
       my @warnings;
     my $countdone=0;      my $countdone=0;
     foreach my $grade (@gradedata) {      foreach my $grade (@gradedata) {
  my %entries=&Apache::loncommon::record_sep($grade);   my %entries=&Apache::loncommon::record_sep($grade);
Line 4067  sub csvuploadassign { Line 4624  sub csvuploadassign {
  if (!$username) {   if (!$username) {
     my $id=$entries{$fields{'ID'}};      my $id=$entries{$fields{'ID'}};
     $id=~s/\s//g;      $id=~s/\s//g;
     my %ids=&Apache::lonnet::idget($domain,$id);              if ($id ne '') {
     $username=$ids{$id};          my %ids=&Apache::lonnet::idget($domain,[$id]);
           $username=$ids{$id};
               } else {
                   if ($entries{$fields{'clicker'}}) {
                       my $clicker = $entries{$fields{'clicker'}};
                       $clicker=~s/\s//g;
                       if ($clicker ne '') {
                           my %clickers = &Apache::lonnet::idget($domain,[$clicker],'clickers');
                           if ($clickers{$clicker} ne '') {  
                               my $match = 0;
                               my @inclass;
                               foreach my $poss (split(/,/,$clickers{$clicker})) {
                                   if (exists($$classlist{"$poss:$domain"})) {
                                       $username = $poss;
                                       push(@inclass,$poss);
                                       $match ++;
                                       
                                   }
                               }
                               if ($match > 1) {
                                   undef($username); 
                                   $request->print('<p class="LC_warning">'.
                                                   &mt('Score not saved for clicker: [_1] (matched multiple usernames: [_2])',
                                                   $clicker,join(', ',@inclass)).'</p>');
                               }
                           }
                       }
                   }
               }
  }   }
  if (!exists($$classlist{"$username:$domain"})) {   if (!exists($$classlist{"$username:$domain"})) {
     my $id=$entries{$fields{'ID'}};      my $id=$entries{$fields{'ID'}};
     $id=~s/\s//g;      $id=~s/\s//g;
     if ($id) {              my $clicker = $entries{$fields{'clicker'}};
               $clicker=~s/\s//g;
               if ($clicker) {
                   push(@skipped,"$clicker:$domain");
       } elsif ($id) {
  push(@skipped,"$id:$domain");   push(@skipped,"$id:$domain");
     } else {      } else {
  push(@skipped,"$username:$domain");   push(@skipped,"$username:$domain");
Line 4100  sub csvuploadassign { Line 4689  sub csvuploadassign {
                     my $pcr=$entries{$fields{$dest}} / $wgt;                      my $pcr=$entries{$fields{$dest}} / $wgt;
                     my $award=($pcr == 0) ? 'incorrect_by_override'                      my $award=($pcr == 0) ? 'incorrect_by_override'
                                           : 'correct_by_override';                                            : 'correct_by_override';
                       if ($pcr>1) {
                          push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));
                       }
                     $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 4127  sub csvuploadassign { Line 4719  sub csvuploadassign {
    $env{'request.course.id'},     $env{'request.course.id'},
    $domain,$username);     $domain,$username);
    if ($result eq 'ok') {     if ($result eq 'ok') {
   # Successfully stored
       $request->print('.');        $request->print('.');
    } else {  # Remove from grading queue
                 &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,
                                                $env{'course.'.$env{'request.course.id'}.'.domain'},
                                                $env{'course.'.$env{'request.course.id'}.'.num'},
                                                $domain,$username);
                 $countdone++;
              } else {
       $request->print("<p><span class=\"LC_error\">".        $request->print("<p><span class=\"LC_error\">".
                               &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",                                &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
                                   "$username:$domain",$result)."</span></p>");                                    "$username:$domain",$result)."</span></p>");
    }     }
    $request->rflush();     $request->rflush();
    $countdone++;  
         }          }
     }      }
     $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));      $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
       if (@warnings) {
           $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Warnings generated for the following saved scores:'),1).'<br />');
           $request->print(join(', ',@warnings));
       }
     if (@skipped) {      if (@skipped) {
  $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />');   $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />');
         $request->print(join(', ',@skipped));          $request->print(join(', ',@skipped));
Line 4160  sub pickStudentPage { Line 4762  sub pickStudentPage {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
   
     my $alertmsg = &mt('Please select the student you wish to grade.');      my $alertmsg = &mt('Please select the student you wish to grade.');
       &js_escape(\$alertmsg);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));
   
 function checkPickOne(formname) {  function checkPickOne(formname) {
Line 4193  LISTJAVASCRIPT Line 4796  LISTJAVASCRIPT
     my ($curpage) =&Apache::lonnet::decode_symb($symb);       my ($curpage) =&Apache::lonnet::decode_symb($symb); 
 #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb);   #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
 #    my $type=($curpage =~ /\.(page|sequence)/);  #    my $type=($curpage =~ /\.(page|sequence)/);
     my $select = '<select name="selectpage">'."\n";  
       # Collection of hidden fields
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);          my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $select.='<option value="'.$ctr.'" '.          $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
     ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').          $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
     '>'.$showtitle.'</option>'."\n";          $ctr++;
  $ctr++;  
     }      }
     $select.= '</select>';      $result.='<input type="hidden" name="page" />'."\n".
     $result.='&nbsp;<b>'.&mt('Problems from').':</b> '.$select."<br />\n";          '<input type="hidden" name="title" />'."\n";
   
       $result.=&build_section_inputs();
       my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
       $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".
    '<input type="hidden" name="command" value="displayPage" />'."\n".
    '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
   
       # Show grading options
       $result.=&Apache::lonhtmlcommon::start_pick_box();
       my $select = '<select name="selectpage">'."\n";
     $ctr=0;      $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";   $select.='<option value="'.$ctr.'"'.
  $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";      ($$symbx{$_} =~ /$curpage$/ ? ' selected="selected"' : '').
       '>'.$showtitle.'</option>'."\n";
  $ctr++;   $ctr++;
     }      }
     $result.='<input type="hidden" name="page" />'."\n".      $select.= '</select>';
  '<input type="hidden" name="title" />'."\n";  
   
     my $options =      $result.=
  '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".          &Apache::lonhtmlcommon::row_title(&mt('Problems from'))
  '<label><input type="radio" name="vProb" value="yes" /> '.&mt('yes').' </label>'."<br />\n";         .$select
     $result.='&nbsp;<b>'.&mt('View Problem Text').': </b>'.$options;         .&Apache::lonhtmlcommon::row_closure();
   
     $options =      $result.=
  '<label><input type="radio" name="lastSub" value="none" /> '.&mt('none').' </label>'."\n".          &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
  '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.&mt('by dates and submissions').'</label>'."\n".         .'<label><input type="radio" name="vProb" value="no"'
  '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').' </label>'."\n";             .' checked="checked" /> '.&mt('no').' </label>'."\n"
     $result.='&nbsp;<b>'.&mt('Submissions').': </b>'.$options;         .'<label><input type="radio" name="vProb" value="yes" />'
              .&mt('yes').'</label>'."\n"
          .&Apache::lonhtmlcommon::row_closure();
   
       $result.=
           &Apache::lonhtmlcommon::row_title(&mt('View Submissions'))
          .'<label><input type="radio" name="lastSub" value="none" /> '
              .&mt('none').' </label>'."\n"
          .'<label><input type="radio" name="lastSub" value="datesub"'
              .' checked="checked" /> '.&mt('all submissions').'</label>'."\n"
          .'<label><input type="radio" name="lastSub" value="all" /> '
              .&mt('all submissions with details').' </label>'
          .&Apache::lonhtmlcommon::row_closure();
           
     $result.=&build_section_inputs();      $result.=
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));          &Apache::lonhtmlcommon::row_title(&mt('Use CODE'))
     $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".         .'<input type="text" name="CODE" value="" />'
  '<input type="hidden" name="command" value="displayPage" />'."\n".         .&Apache::lonhtmlcommon::row_closure(1)
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."<br />\n";         .&Apache::lonhtmlcommon::end_pick_box();
   
     $result.='&nbsp;<b>'.&mt('Use CODE').': </b> <input type="text" name="CODE" value="" /> <br />'."\n";      # Show list of students to select for grading
       $result.='<br /><input type="button" '.
     $result.='&nbsp;<input type="button" '.  
              'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";               'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";
   
     $request->print($result);      $request->print($result);
Line 4298  sub getSymbMap { Line 4921  sub getSymbMap {
     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },      my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
        1,0,1);         1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {      for my $sequence ($navmap->getById('0.0'), @sequences) {
  if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {   if ($navmap->hasResource($sequence, sub { shift->is_gradable(); }, 0) ) {
     my $title = $minder.'.'.      my $title = $minder.'.'.
  &HTML::Entities::encode($sequence->compTitle(),'"\'&');   &HTML::Entities::encode($sequence->compTitle(),'"\'&');
     push(@titles, $title); # minder in case two titles are identical      push(@titles, $title); # minder in case two titles are identical
Line 4329  sub displayPage { Line 4952  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">'.&mt('Unable to view requested student. ([_1])',$env{'form.student'}).'</span>');          $request->print(
  return;              '<span class="LC_warning">'.
               &mt('Unable to view requested student. ([_1])',
                       $env{'form.student'}).
               '</span>');
           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;'.&mt('Student: [_1]',&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom)).      $result.='<h3>&nbsp;'.&mt('Student: [_1]',&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom)).
Line 4379  sub displayPage { Line 5006  sub displayPage {
         '</span>'."\n".          '</span>'."\n".
  &Apache::loncommon::start_data_table().   &Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
  '<th align="center">&nbsp;Prob.&nbsp;</th>'.   '<th>'.&mt('Prob.').'</th>'.
  '<th>&nbsp;'.($env{'form.vProb'} eq 'no' ? &mt('Title') : &mt('Problem Text')).'/'.&mt('Grade').'</th>'.   '<th>&nbsp;'.($env{'form.vProb'} eq 'no' ? &mt('Title') : &mt('Problem Text')).'/'.&mt('Grade').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
   
Line 4391  sub displayPage { Line 5018  sub displayPage {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
   
         if (ref($curRes) && $curRes->is_problem()) {          if (ref($curRes) && $curRes->is_gradable()) {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
               my $is_tool = ($symbx =~ /ext\.tool$/);
     $studentTable.=      $studentTable.=
  &Apache::loncommon::start_data_table_row().   &Apache::loncommon::start_data_table_row().
  '<td align="center" valign="top" >'.$prob.   '<td align="center" valign="top" >'.$prob.
  (scalar(@{$parts}) == 1 ? ''    (scalar(@{$parts}) == 1 ? '' 
                         : '<br />('.&mt('[_1]&nbsp;parts)',                          : '<br />('.&mt('[_1]parts',
  scalar(@{$parts}))   scalar(@{$parts}).'&nbsp;').')'
  ).   ).
  '</td>';   '</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 ($is_tool) {
  $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,                  $studentTable.='&nbsp;<b>'.$title.'</b><br />';
      undef,'both',\%form);              } else {
     } else {          if ($env{'form.vProb'} eq 'yes' ) {
  my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);      $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
  $companswer =~ s|<form(.*?)>||g;           undef,'both',\%form);
  $companswer =~ s|</form>||g;          } else {
 # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>      my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
 #    $companswer =~ s/$1/ /ms;      $companswer =~ s|<form(.*?)>||g;
 #    $request->print('match='.$1."<br />\n");      $companswer =~ s|</form>||g;
 # }  #    while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
 # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;  #        $companswer =~ s/$1/ /ms;
  $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;  #        $request->print('match='.$1."<br />\n");
   #    }
   #    $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
       $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">'.&mt('No recorded submission for this problem.').'</span><br />';                      my $msg = &mt('No recorded submission for this problem.');
                       if ($is_tool) {
                           $msg = &mt('No recorded transactions for this external tool');
                       }
       $studentTable.='<br />&nbsp;<span class="LC_warning">'.$msg.'</span><br />';
  } else {   } else {
     my %responseType = ();      my %responseType = ();
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
Line 4437  sub displayPage { Line 5073  sub displayPage {
  $responseType{$partid} = \%responseIds;   $responseType{$partid} = \%responseIds;
     }      }
     $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);      $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
   
  }   }
     } elsif ($env{'form.lastSub'} eq 'all') {      } elsif ($env{'form.lastSub'} eq 'all') {
  my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');   my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
                   my $identifier = (&canmodify($usec)? $prob : ''); 
  $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,   $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
  $env{'request.course.id'},   $env{'request.course.id'},
  '','.submission');   '','.submission',undef,
                                                                           $usec,$identifier);
     
     }      }
     if (&canmodify($usec)) {      if (&canmodify($usec)) {
Line 4476  sub displaySubByDates { Line 5113  sub displaySubByDates {
     my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;      my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
     my $isCODE=0;      my $isCODE=0;
     my $isTask = ($symb =~/\.task$/);      my $isTask = ($symb =~/\.task$/);
       my $is_tool = ($symb =~/\.tool$/);
     if (exists($record->{'resource.CODE'})) { $isCODE=1; }      if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     my $studentTable=&Apache::loncommon::start_data_table().      my $studentTable=&Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
  '<th>'.&mt('Date/Time').'</th>'.   '<th>'.&mt('Date/Time').'</th>'.
  ($isCODE?'<th>'.&mt('CODE').'</th>':'').   ($isCODE?'<th>'.&mt('CODE').'</th>':'').
  '<th>'.&mt('Submission').'</th>'.          ($isTask?'<th>'.&mt('Version').'</th>':'').
    '<th>'.($is_tool?&mt('Grade'):&mt('Submission')).'</th>'.
  '<th>'.&mt('Status').'</th>'.   '<th>'.&mt('Status').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     my ($version);      my ($version);
Line 4489  sub displaySubByDates { Line 5128  sub displaySubByDates {
     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">'.&mt('Nothing submitted - no attempts.').'</span><br />';          if ($is_tool) {
               return '<br />&nbsp;<span class="LC_warning">'.&mt('No grade passed back.').'</span><br />';
           } else {
               return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
           }
     }      }
   
     my $interaction;      my $interaction;
     my $no_increment = 1;      my $no_increment = 1;
       my (%lastrndseed,%lasttype);
     for ($version=1;$version<=$$record{'version'};$version++) {      for ($version=1;$version<=$$record{'version'};$version++) {
  my $timestamp =    my $timestamp = 
     &Apache::lonlocal::locallocaltime($$record{$version.':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'};
  }   }
           if ($isTask && $env{'form.previousversion'}) {
               next unless ($interaction == $env{'form.previousversion'});
           }
  my $where = ($isTask ? "$version:resource.$interaction"   my $where = ($isTask ? "$version:resource.$interaction"
              : "$version:resource");               : "$version:resource");
  $studentTable.=&Apache::loncommon::start_data_table_row().   $studentTable.=&Apache::loncommon::start_data_table_row().
Line 4508  sub displaySubByDates { Line 5154  sub displaySubByDates {
  if ($isCODE) {   if ($isCODE) {
     $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>';      $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>';
  }   }
           if ($isTask) {
               $studentTable.='<td>'.$interaction.'</td>';
           }
  my @versionKeys = split(/\:/,$$record{$version.':keys'});   my @versionKeys = split(/\:/,$$record{$version.':keys'});
  my @displaySub = ();   my @displaySub = ();
  foreach my $partid (@{$parts}) {   foreach my $partid (@{$parts}) {
             my $hidden;              my ($hidden,$type);
             if (($$record{$version.':resource.'.$partid.'.type'} eq 'anonsurvey') ||              $type = $$record{$version.':resource.'.$partid.'.type'};
                 ($$record{$version.':resource.'.$partid.'.type'} eq 'anonsurveycred')) {              if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                 $hidden = 1;                  $hidden = 1;
             }              }
     my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)              my @matchKey;
             : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));              if ($isTask) {
                       @matchKey = sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys);
               } elsif ($is_tool) {
                   @matchKey = sort(grep /^resource\.\Q$partid\E\.awarded$/,@versionKeys);
               } else {
                   @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
               }
 #    next if ($$record{"$version:resource.$partid.solved"} eq '');  #    next if ($$record{"$version:resource.$partid.solved"} eq '');
     my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
     foreach my $matchKey (@matchKey) {      foreach my $matchKey (@matchKey) {
  if (exists($$record{$version.':'.$matchKey}) &&   if (exists($$record{$version.':'.$matchKey}) &&
     $$record{$version.':'.$matchKey} ne '') {      $$record{$version.':'.$matchKey} ne '') {
                                           if ($is_tool) {
     my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)                          $displaySub[0].=$$record{"$version:resource.$partid.awarded"};
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));  
                     $displaySub[0].='<span class="LC_nobreak"';  
                     $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'  
                                    .' <span class="LC_internal_info">'  
                                    .'('.&mt('Part ID: [_1]',$responseId).')'  
                                    .'</span>'  
                                    .' <b>';  
                     if ($hidden) {  
                         $displaySub[0].= &mt('Anonymous Survey').'</b>';  
                     } else {                      } else {
         if ($$record{"$where.$partid.tries"} eq '') {          my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
     $displaySub[0].=&mt('Trial not counted');                     : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
         } else {                          $displaySub[0].='<span class="LC_nobreak">';
     $displaySub[0].=&mt('Trial: [_1]',                          $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
     $$record{"$where.$partid.tries"});                                         .' <span class="LC_internal_info">'
         }                                         .'('.&mt('Response ID: [_1]',$responseId).')'
         my $responseType=($isTask ? 'Task'                                         .'</span>'
                                          .' <b>';
                           if ($hidden) {
                               $displaySub[0].= &mt('Anonymous Survey').'</b>';
                           } else {
                               my ($trial,$rndseed,$newvariation);
                               if ($type eq 'randomizetry') {
                                   $trial = $$record{"$where.$partid.tries"};
                                   $rndseed = $$record{"$where.$partid.rndseed"};
                               }
               if ($$record{"$where.$partid.tries"} eq '') {
           $displaySub[0].=&mt('Trial not counted');
               } else {
           $displaySub[0].=&mt('Trial: [_1]',
           $$record{"$where.$partid.tries"});
                                   if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
                                       if (($rndseed ne $lastrndseed{$partid}) &&
                                           (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
                                           $newvariation = '&nbsp;('.&mt('New variation this try').')';
                                       }
                                   }
                                   $lastrndseed{$partid} = $rndseed;
                                   $lasttype{$partid} = $type;
               }
               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})) || ($trial)) {
     $orders{$partid}->{$responseId}=          $orders{$partid}->{$responseId}=
         &get_order($partid,$responseId,$symb,$uname,$udom,              &get_order($partid,$responseId,$symb,$uname,$udom,
                                            $no_increment);                                                 $no_increment,$type,$trial,$rndseed);
         }              }
         $displaySub[0].='</b></span>'; # /nobreak              $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
         $displaySub[0].='&nbsp; '.              $displaySub[0].='&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,$type,$trial,$rndseed).'<br />';
                           }
                     }                      }
  }   }
     }      }
Line 4567  sub displaySubByDates { Line 5237  sub displaySubByDates {
     lc($$record{"$where.$partid.award"}).' '.      lc($$record{"$where.$partid.award"}).' '.
     $mark{$$record{"$where.$partid.solved"}}.      $mark{$$record{"$where.$partid.solved"}}.
     '<br />';      '<br />';
       } elsif (($is_tool) && (exists($$record{"$version:resource.$partid.solved"}))) {
    if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {
       $displaySub[1].=&mt('Grade passed back by external tool');
    }
     }      }
     if (exists $$record{"$where.$partid.regrader"}) {      if (exists $$record{"$where.$partid.regrader"}) {
  $displaySub[2].=$$record{"$where.$partid.regrader"}.   $displaySub[2].=$$record{"$where.$partid.regrader"};
     ' (<b>'.&mt('Part').':</b> '.$display_part.')';   unless ($is_tool) {
       $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
    }
     } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {      } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {
  $displaySub[2].=   $displaySub[2].=
     $$record{"$version:resource.$partid.regrader"}.      $$record{"$version:resource.$partid.regrader"};
     ' (<b>'.&mt('Part').':</b> '.$display_part.')';                  unless ($is_tool) {
       $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
                   }
     }      }
  }   }
  # needed because old essay regrader has not parts info   # needed because old essay regrader has not parts info
Line 4638  sub updateGradeByPage { Line 5316  sub updateGradeByPage {
   
     $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"
     my ($depth,$question,$prob,$changeflag)= (1,1,1,0);      my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);
     while ($depth > 0) {      while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
Line 4651  sub updateGradeByPage { Line 5329  sub updateGradeByPage {
  &Apache::loncommon::start_data_table_row().   &Apache::loncommon::start_data_table_row().
  '<td align="center" valign="top" >'.$prob.   '<td align="center" valign="top" >'.$prob.
  (scalar(@{$parts}) == 1 ? ''    (scalar(@{$parts}) == 1 ? '' 
                                         : '<br />('.&mt('[quant,_1,&nbsp;part]',scalar(@{$parts}))                                          : '<br />('.&mt('[quant,_1,part]',scalar(@{$parts}))
  .')').'</td>';   .')').'</td>';
     $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';      $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
   
Line 4659  sub updateGradeByPage { Line 5337  sub updateGradeByPage {
     my @displayPts=();      my @displayPts=();
             my %aggregate = ();              my %aggregate = ();
             my $aggregateflag = 0;              my $aggregateflag = 0;
               if ($env{'form.HIDE'.$prob}) {
                   my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
                   my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);
                   my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);
                   $hideflag += $numchgs;
               }
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
  my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};   my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
  my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};   my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
Line 4749  sub updateGradeByPage { Line 5433  sub updateGradeByPage {
     $studentTable.=&Apache::loncommon::end_data_table();      $studentTable.=&Apache::loncommon::end_data_table();
     my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') :      my $grademsg=($changeflag == 0 ? &mt('No score was changed or updated.') :
   &mt('The scores were changed for [quant,_1,problem].',    &mt('The scores were changed for [quant,_1,problem].',
   $changeflag));    $changeflag).'<br />');
     $request->print($grademsg.$studentTable);      my $hidemsg=($hideflag == 0 ? '' :
                    &mt('Submissions were marked "hidden" for [quant,_1,transaction].',
                        $hideflag).'<br />');
       $request->print($hidemsg.$grademsg.$studentTable);
   
     return '';      return '';
 }  }
Line 4773  sub updateGradeByPage { Line 5460  sub updateGradeByPage {
    from the file that we are parsing that represents one entire sheet     from the file that we are parsing that represents one entire sheet
   
    'bubble line' refers to the data     'bubble line' refers to the data
    representing the line of bubbles that are on the physical bubble sheet     representing the line of bubbles that are on the physical bubblesheet
   
   
 The overall process is that a scanned in bubble sheet data is uploaded  The overall process is that a scanned in bubblesheet data is uploaded
 into a course. When a user wants to grade, they select a  into a course. When a user wants to grade, they select a
 sequence/folder of resources, a file of bubble sheet info, and pick  sequence/folder of resources, a file of bubblesheet info, and pick
 one of the predefined configurations for what each scanline looks  one of the predefined configurations for what each scanline looks
 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 mis-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 than one letter picked), invalid or duplicated CODE,
 invalid student/employee ID  invalid student/employee ID
   
 If the CODE option is used that determines the randomization of the  If the CODE option is used that determines the randomization of the
Line 4794  username:domain. Line 5481  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 are now 3 bubble sheet files  After the validation phase, there are now 3 bubblesheet 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)
   scantron_skipped_filename (contains the exact text of scanlines that where skipped)    scantron_skipped_filename (contains the exact text of scanlines that where skipped)
   
 Also there is a separate hash nohist_scantrondata that contains extra  Also there is a separate hash nohist_scantrondata that contains extra
 correction information that isn't representable in the bubble sheet  correction information that isn't representable in the bubblesheet
 file (see &scantron_getfile() for more information)  file (see &scantron_getfile() for more information)
   
 After all scanlines are either valid, marked as valid or skipped, then  After all scanlines are either valid, marked as valid or skipped, then
Line 4873  my %subdivided_bubble_lines;       # no. Line 5560  my %subdivided_bubble_lines;       # no.
   
 my %responsetype_per_response;     # responsetype for each response  my %responsetype_per_response;     # responsetype for each response
   
   my %masterseq_id_responsenum;      # src_id (e.g., 12.3_0.11 etc.) for each
                                      # numbered response. Needed when randomorder
                                      # or randompick are in use. Key is ID, value 
                                      # is response number.
   
 # Save and restore the bubble lines array to the form env.  # Save and restore the bubble lines array to the form env.
   
   
Line 4886  sub save_bubble_lines { Line 5578  sub save_bubble_lines {
         $env{"form.scantron.responsetype.$line"} =          $env{"form.scantron.responsetype.$line"} =
             $responsetype_per_response{$line};              $responsetype_per_response{$line};
     }      }
       foreach my $resid (keys(%masterseq_id_responsenum)) {
           my $line = $masterseq_id_responsenum{$resid};
           $env{"form.scantron.residpart.$line"} = $resid;
       }
 }  }
   
   
 sub restore_bubble_lines {  sub restore_bubble_lines {
     my $line = 0;      my $line = 0;
     %bubble_lines_per_response = ();      %bubble_lines_per_response = ();
       %masterseq_id_responsenum = ();
     while ($env{"form.scantron.bubblelines.$line"}) {      while ($env{"form.scantron.bubblelines.$line"}) {
  my $value = $env{"form.scantron.bubblelines.$line"};   my $value = $env{"form.scantron.bubblelines.$line"};
  $bubble_lines_per_response{$line} = $value;   $bubble_lines_per_response{$line} = $value;
Line 4901  sub restore_bubble_lines { Line 5598  sub restore_bubble_lines {
             $env{"form.scantron.sub_bubblelines.$line"};              $env{"form.scantron.sub_bubblelines.$line"};
         $responsetype_per_response{$line} =          $responsetype_per_response{$line} =
             $env{"form.scantron.responsetype.$line"};              $env{"form.scantron.responsetype.$line"};
           my $id = $env{"form.scantron.residpart.$line"};
           $masterseq_id_responsenum{$id} = $line;
  $line++;   $line++;
     }      }
 }  }
   
 #  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 
   
 =item scantron_filenames  =item scantron_filenames
Line 4935  sub scantron_filenames { Line 5616  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 $getpropath = 1;
     my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,      my ($dirlist,$listerror) = &Apache::lonnet::dirlist('userfiles',$cdom,
                                        $getpropath);                                                          $cname,$getpropath);
     my @possiblenames;      my @possiblenames;
     foreach my $filename (sort(@files)) {      if (ref($dirlist) eq 'ARRAY') {
  ($filename)=split(/&/,$filename);          foreach my $filename (sort(@{$dirlist})) {
  if ($filename!~/^scantron_orig_/) { next ; }      ($filename)=split(/&/,$filename);
  $filename=~s/^scantron_orig_//;      if ($filename!~/^scantron_orig_/) { next ; }
  push(@possiblenames,$filename);      $filename=~s/^scantron_orig_//;
       push(@possiblenames,$filename);
           }
     }      }
     return @possiblenames;      return @possiblenames;
 }  }
Line 5096  sub scantron_CODEunique { Line 5779  sub scantron_CODEunique {
   
 =item scantron_selectphase  =item scantron_selectphase
   
   Generates the initial screen to start the bubble sheet process.    Generates the initial screen to start the bubblesheet process.
   Allows for - starting a grading run.    Allows for - starting a grading run.
              - downloading existing scan data (original, corrected               - downloading existing scan data (original, corrected
                                                 or skipped info)                                                  or skipped info)
Line 5146  sub scantron_selectphase { Line 5829  sub scantron_selectphase {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     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 $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
       &js_escape(\$alertmsg);
     $r->print(&Apache::lonhtmlcommon::scripttag('      $r->print(&Apache::lonhtmlcommon::scripttag('
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
     alert("'.&mt('Please use the browse button to select a file from your local directory.').'");      alert("'.$alertmsg.'");
     return false;      return false;
  }   }
  formname.submit();   formname.submit();
Line 5248  sub scantron_selectphase { Line 5933  sub scantron_selectphase {
   
     &Apache::lonpickcode::code_list($r,2);      &Apache::lonpickcode::code_list($r,2);
   
     $r->print('<br /><form method="post" name="checkscantron">'.      $r->print('<br /><form method="post" name="checkscantron" action="">'.
              $default_form_data."\n".               $default_form_data."\n".
              &Apache::loncommon::start_data_table('LC_scantron_action')."\n".               &Apache::loncommon::start_data_table('LC_scantron_action')."\n".
              &Apache::loncommon::start_data_table_header_row()."\n".               &Apache::loncommon::start_data_table_header_row()."\n".
Line 5287  sub scantron_selectphase { Line 5972  sub scantron_selectphase {
   
 =item get_scantron_config  =item get_scantron_config
   
    Parse and return the scantron configuration line selected as a     Parse and return the bubblesheet configuration line selected as a
    hash of configuration file fields.     hash of configuration file fields.
   
  Arguments:   Arguments:
Line 5337  sub scantron_selectphase { Line 6022  sub scantron_selectphase {
     
       LastName    - column that the last name starts in        LastName    - column that the last name starts in
       LastNameLength - number of columns that the last name spans        LastNameLength - number of columns that the last name spans
         BubblesPerRow - number of bubbles available in each row used to 
                         bubble an answer. (If not specified, 10 assumed).
   
 =cut  =cut
   
Line 5367  sub get_scantron_config { Line 6054  sub get_scantron_config {
  $config{'FirstNamelength'}=$config[14];   $config{'FirstNamelength'}=$config[14];
  $config{'LastName'}=$config[15];   $config{'LastName'}=$config[15];
  $config{'LastNamelength'}=$config[16];   $config{'LastNamelength'}=$config[16];
           $config{'BubblesPerRow'}=$config[17];
  last;   last;
     }      }
     return %config;      return %config;
Line 5377  sub get_scantron_config { Line 6065  sub get_scantron_config {
 =item username_to_idmap  =item username_to_idmap
   
     creates a hash keyed by student/employee ID with values of the corresponding      creates a hash keyed by student/employee ID with values of the corresponding
     student username:domain.      student username:domain. If a single ID occurs for more than one student,
       the status of the student is checked, and if Active, the value in the hash
       will be set to the Active student.
   
   Arguments:    Arguments:
   
Line 5395  sub username_to_idmap { Line 6085  sub username_to_idmap {
     my ($classlist)= @_;      my ($classlist)= @_;
     my %idmap;      my %idmap;
     foreach my $student (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
  $idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}=          my $id = $classlist->{$student}->[&Apache::loncoursedata::CL_ID];
     $student;          unless ($id eq '') {
               if (!exists($idmap{$id})) {
                   $idmap{$id} = $student;
               } else {
                   my $status = $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS];
                   if ($status eq 'Active') {
                       $idmap{$id} = $student;
                   }
               }
           }
     }      }
     return %idmap;      return %idmap;
 }  }
Line 5568  sub digits_to_letters { Line 6267  sub digits_to_letters {
   
 =item scantron_parse_scanline  =item scantron_parse_scanline
   
   Decodes a scanline from the selected scantron file    Decodes a scanline from the selected bubblesheet file
   
  Arguments:   Arguments:
     line             - The text of the scantron file line to process      line             - The text of the bubblesheet file line to process
     whichline        - Line number      whichline        - Line number
     scantron_config  - Hash describing the format of the scantron lines.      scantron_config  - Hash describing the format of the bubblesheet lines.
     scan_data        - Hash of extra information about the scanline      scan_data        - Hash of extra information about the scanline
                        (see scantron_getfile for more information)                         (see scantron_getfile for more information)
     just_header      - True if should not process question answers but only      just_header      - True if should not process question answers but only
                        the stuff to the left of the answers.                         the stuff to the left of the answers.
       randomorder      - True if randomorder in use
       randompick       - True if randompick in use
       sequence         - Exam folder URL
       master_seq       - Ref to array containing symbs in exam folder
       symb_to_resource - Ref to hash of symbs for resources in exam folder
                          (corresponding values are resource objects)
       partids_by_symb  - Ref to hash of symb -> array ref of partIDs
       orderedforcode   - Ref to hash of arrays. keys are CODEs and values
                          are refs to an array of resource objects, ordered
                          according to order used for CODE, when randomorder
                          and or randompick are in use.
       respnumlookup    - Ref to hash mapping question numbers in bubble lines
                          for current line to question number used for same question
                           in "Master Sequence" (as seen by Course Coordinator).
       startline        - Ref to hash where key is question number (0 is first)
                          and value is number of first bubble line for current 
                          student or code-based randompick and/or randomorder.
       totalref         - Ref of scalar used to score total number of bubble
                          lines needed for responses in a scan line (used when
                          randompick in use. 
       
  Returns:   Returns:
    Hash containing the result of parsing the scanline     Hash containing the result of parsing the scanline
   
Line 5622  sub digits_to_letters { Line 6342  sub digits_to_letters {
 =cut  =cut
   
 sub scantron_parse_scanline {  sub scantron_parse_scanline {
     my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;      my ($line,$whichline,$scantron_config,$scan_data,$just_header,$idmap,
           $randomorder,$randompick,$sequence,$master_seq,$symb_to_resource,
           $partids_by_symb,$orderedforcode,$respnumlookup,$startline,$totalref)=@_;
   
     my %record;      my %record;
     my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};      my $data=substr($line,0,$$scantron_config{'Qstart'}-1); # stuff before answers
     my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers  
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff  
     if (!($$scantron_config{'CODElocation'} eq 0 ||      if (!($$scantron_config{'CODElocation'} eq 0 ||
   $$scantron_config{'CODElocation'} eq 'none')) {    $$scantron_config{'CODElocation'} eq 'none')) {
  if ($$scantron_config{'CODElocation'} < 0 ||   if ($$scantron_config{'CODElocation'} < 0 ||
Line 5663  sub scantron_parse_scanline { Line 6383  sub scantron_parse_scanline {
     my $questnum=0;      my $questnum=0;
     my $ansnum  =1; # Multiple 'answer lines'/question.      my $ansnum  =1; # Multiple 'answer lines'/question.
   
       my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};
       if ($randompick || $randomorder) {
           my $total = &get_respnum_lookups($sequence,$scan_data,$idmap,$line,\%record,
                                            $master_seq,$symb_to_resource,
                                            $partids_by_symb,$orderedforcode,
                                            $respnumlookup,$startline);
           if ($total) {
               $lastpos = $total*$$scantron_config{'Qlength'}; 
           }
           if (ref($totalref)) {
               $$totalref = $total;
           }
       }
       my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers
     chomp($questions); # Get rid of any trailing \n.      chomp($questions); # Get rid of any trailing \n.
     $questions =~ s/\r$//;      # Get rid of trailing \r too (MAC or Win uploads).      $questions =~ s/\r$//;      # Get rid of trailing \r too (MAC or Win uploads).
     while (length($questions)) {      while (length($questions)) {
  my $answers_needed = $bubble_lines_per_response{$questnum};          my $answers_needed;
           if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
               $answers_needed = $bubble_lines_per_response{$respnumlookup->{$questnum}};
           } else {
       $answers_needed = $bubble_lines_per_response{$questnum};
           }
         my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)          my $answer_length  = ($$scantron_config{'Qlength'} * $answers_needed)
                              || 1;                               || 1;
         $questnum++;          $questnum++;
Line 5675  sub scantron_parse_scanline { Line 6414  sub scantron_parse_scanline {
         $questions       = substr($questions,$answer_length);          $questions       = substr($questions,$answer_length);
         if (length($currentquest) < $answer_length) { next; }          if (length($currentquest) < $answer_length) { next; }
   
         if ($subdivided_bubble_lines{$questnum-1} =~ /,/) {          my $subdivided;
           if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
               $subdivided = $subdivided_bubble_lines{$respnumlookup->{$questnum-1}};
           } else {
               $subdivided = $subdivided_bubble_lines{$questnum-1};
           }
           if ($subdivided =~ /,/) {
             my $subquestnum = 1;              my $subquestnum = 1;
             my $subquestions = $currentquest;              my $subquestions = $currentquest;
             my @subanswers_needed =               my @subanswers_needed = split(/,/,$subdivided);
                 split(/,/,$subdivided_bubble_lines{$questnum-1});    
             foreach my $subans (@subanswers_needed) {              foreach my $subans (@subanswers_needed) {
                 my $subans_length =                  my $subans_length =
                     ($$scantron_config{'Qlength'} * $subans)  || 1;                      ($$scantron_config{'Qlength'} * $subans)  || 1;
Line 5690  sub scantron_parse_scanline { Line 6434  sub scantron_parse_scanline {
                     ($$scantron_config{'Qon'} eq 'number')) {                      ($$scantron_config{'Qon'} eq 'number')) {
                     $ansnum = &scantron_validator_lettnum($ansnum,                       $ansnum = &scantron_validator_lettnum($ansnum, 
                         $questnum,$quest_id,$subans,$currsubquest,$whichline,                          $questnum,$quest_id,$subans,$currsubquest,$whichline,
                         \@alphabet,\%record,$scantron_config,$scan_data);                          \@alphabet,\%record,$scantron_config,$scan_data,
                           $randomorder,$randompick,$respnumlookup);
                 } else {                  } else {
                     $ansnum = &scantron_validator_positional($ansnum,                      $ansnum = &scantron_validator_positional($ansnum,
                         $questnum,$quest_id,$subans,$currsubquest,$whichline,                        \@alphabet,\%record,$scantron_config,$scan_data);                          $questnum,$quest_id,$subans,$currsubquest,$whichline,
                           \@alphabet,\%record,$scantron_config,$scan_data,
                           $randomorder,$randompick,$respnumlookup);
                 }                  }
                 $subquestnum ++;                  $subquestnum ++;
             }              }
Line 5702  sub scantron_parse_scanline { Line 6449  sub scantron_parse_scanline {
                 ($$scantron_config{'Qon'} eq 'number')) {                  ($$scantron_config{'Qon'} eq 'number')) {
                 $ansnum = &scantron_validator_lettnum($ansnum,$questnum,                  $ansnum = &scantron_validator_lettnum($ansnum,$questnum,
                     $quest_id,$answers_needed,$currentquest,$whichline,                      $quest_id,$answers_needed,$currentquest,$whichline,
                     \@alphabet,\%record,$scantron_config,$scan_data);                      \@alphabet,\%record,$scantron_config,$scan_data,
                       $randomorder,$randompick,$respnumlookup);
             } else {              } else {
                 $ansnum = &scantron_validator_positional($ansnum,$questnum,                  $ansnum = &scantron_validator_positional($ansnum,$questnum,
                     $quest_id,$answers_needed,$currentquest,$whichline,                      $quest_id,$answers_needed,$currentquest,$whichline,
                     \@alphabet,\%record,$scantron_config,$scan_data);                      \@alphabet,\%record,$scantron_config,$scan_data,
                       $randomorder,$randompick,$respnumlookup);
             }              }
         }          }
     }      }
Line 5714  sub scantron_parse_scanline { Line 6463  sub scantron_parse_scanline {
     return \%record;      return \%record;
 }  }
   
   sub get_master_seq {
       my ($resources,$master_seq,$symb_to_resource) = @_;
       return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') && 
                      (ref($symb_to_resource) eq 'HASH'));
       my $resource_error;
       foreach my $resource (@{$resources}) {
           my $ressymb;
           if (ref($resource)) {
               $ressymb = $resource->symb();
               push(@{$master_seq},$ressymb);
               $symb_to_resource->{$ressymb} = $resource;
           } else {
               $resource_error = 1;
               last;
           }
       }
       return $resource_error;
   }
   
   sub get_respnum_lookups {
       my ($sequence,$scan_data,$idmap,$line,$record,$master_seq,$symb_to_resource,
           $partids_by_symb,$orderedforcode,$respnumlookup,$startline) = @_;
       return unless ((ref($record) eq 'HASH') && (ref($master_seq) eq 'ARRAY') &&
                      (ref($symb_to_resource) eq 'HASH') && (ref($partids_by_symb) eq 'HASH') &&
                      (ref($orderedforcode) eq 'HASH') && (ref($respnumlookup) eq 'HASH') &&
                      (ref($startline) eq 'HASH'));
       my ($user,$scancode);
       if ((exists($record->{'scantron.CODE'})) &&
           (&Apache::lonnet::validCODE($record->{'scantron.CODE'}))) {
           $scancode = $record->{'scantron.CODE'};
       } else {
           $user = &scantron_find_student($record,$scan_data,$idmap,$line);
       }
       my @mapresources =
           &users_order($user,$scancode,$sequence,$master_seq,$symb_to_resource,
                        $orderedforcode);
       my $total = 0;
       my $count = 0;
       foreach my $resource (@mapresources) {
           my $id = $resource->id();
           my $symb = $resource->symb();
           if (ref($partids_by_symb->{$symb}) eq 'ARRAY') {
               foreach my $partid (@{$partids_by_symb->{$symb}}) {
                   my $respnum = $masterseq_id_responsenum{$id.'_'.$partid};
                   if ($respnum ne '') {
                       $respnumlookup->{$count} = $respnum;
                       $startline->{$count} = $total;
                       $total += $bubble_lines_per_response{$respnum};
                       $count ++;
                   }
               }
           }
       }
       return $total;
   }
   
 sub scantron_validator_lettnum {  sub scantron_validator_lettnum {
     my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,$whichline,      my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,$whichline,
         $alphabet,$record,$scantron_config,$scan_data) = @_;          $alphabet,$record,$scantron_config,$scan_data,$randomorder,
           $randompick,$respnumlookup) = @_;
   
     # Qon 'letter' implies for each slot in currquest we have:      # Qon 'letter' implies for each slot in currquest we have:
     #    ? or * for doubles, a letter in A-Z for a bubble, and      #    ? or * for doubles, a letter in A-Z for a bubble, and
Line 5735  sub scantron_validator_lettnum { Line 6541  sub scantron_validator_lettnum {
         $matchon = '\d';          $matchon = '\d';
     }      }
     my $occurrences = 0;      my $occurrences = 0;
     if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||      my $responsenum = $questnum-1;
         ($responsetype_per_response{$questnum-1} eq 'formularesponse') ||      if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
         ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||         $responsenum = $respnumlookup->{$questnum-1} 
         ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||      }
         ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||      if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
         ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {          ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
           ($responsetype_per_response{$responsenum} eq 'stringresponse') ||
           ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
           ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
           ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
         my @singlelines = split('',$currquest);          my @singlelines = split('',$currquest);
         foreach my $entry (@singlelines) {          foreach my $entry (@singlelines) {
             $occurrences = &occurence_count($entry,$matchon);              $occurrences = &occurence_count($entry,$matchon);
             if ($occurrences > 1) {              if ($occurrences > 1) {
                 last;                  last;
             }              }
         }           }
     } else {      } else {
         $occurrences = &occurence_count($currquest,$matchon);           $occurrences = &occurence_count($currquest,$matchon); 
     }      }
Line 5795  sub scantron_validator_lettnum { Line 6605  sub scantron_validator_lettnum {
   
 sub scantron_validator_positional {  sub scantron_validator_positional {
     my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,      my ($ansnum,$questnum,$quest_id,$answers_needed,$currquest,
         $whichline,$alphabet,$record,$scantron_config,$scan_data) = @_;          $whichline,$alphabet,$record,$scantron_config,$scan_data,
           $randomorder,$randompick,$respnumlookup) = @_;
   
     # Otherwise there's a positional notation;      # Otherwise there's a positional notation;
     # each bubble line requires Qlength items, and there are filled in      # each bubble line requires Qlength items, and there are filled in
Line 5837  sub scantron_validator_positional { Line 6648  sub scantron_validator_positional {
         #  record all the bubbles filled in as well as the          #  record all the bubbles filled in as well as the
         #  fact this response consists of multiple bubbles.          #  fact this response consists of multiple bubbles.
         #          #
         if (($responsetype_per_response{$questnum-1} eq 'essayresponse') ||          my $responsenum = $questnum-1;
             ($responsetype_per_response{$questnum-1} eq 'formularesponse') ||          if (($randompick || $randomorder) && (ref($respnumlookup) eq 'HASH')) {
             ($responsetype_per_response{$questnum-1} eq 'stringresponse') ||              $responsenum = $respnumlookup->{$questnum-1}
             ($responsetype_per_response{$questnum-1} eq 'imageresponse') ||          }
             ($responsetype_per_response{$questnum-1} eq 'reactionresponse') ||          if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
             ($responsetype_per_response{$questnum-1} eq 'organicresponse')) {              ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
               ($responsetype_per_response{$responsenum} eq 'stringresponse') ||
               ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
               ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
               ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
             my $doubleerror = 0;              my $doubleerror = 0;
             while (($currquest >= $$scantron_config{'Qlength'}) &&               while (($currquest >= $$scantron_config{'Qlength'}) && 
                    (!$doubleerror)) {                     (!$doubleerror)) {
Line 6024  sub scantron_process_corrections { Line 6839  sub scantron_process_corrections {
  }   }
     }      }
     if ($err) {      if ($err) {
  $r->print("<span class=\"LC_warning\">Unable to accept last correction, an error occurred :$errmsg:</span>");          $r->print(
               '<p class="LC_error">'
              .&mt('Unable to accept last correction, an error occurred: [_1]',
                   $errmsg)
              .'</p>');
     } else {      } else {
  &scantron_put_line($scanlines,$scan_data,$which,$line,$skip);   &scantron_put_line($scanlines,$scan_data,$which,$line,$skip);
  &scantron_putfile($scanlines,$scan_data);   &scantron_putfile($scanlines,$scan_data);
Line 6116  sub remember_current_skipped { Line 6935  sub remember_current_skipped {
 =item check_for_error  =item check_for_error
   
     Checks if there was an error when attempting to remove a specific      Checks if there was an error when attempting to remove a specific
     scantron_.. bubble sheet data file. Prints out an error if      scantron_.. bubblesheet data file. Prints out an error if
     something went wrong.      something went wrong.
   
 =cut  =cut
Line 6138  sub check_for_error { Line 6957  sub check_for_error {
 =cut  =cut
   
 sub scantron_warning_screen {  sub scantron_warning_screen {
     my ($button_text)=@_;      my ($button_text,$symb)=@_;
     my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});      my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my $CODElist;      my $CODElist;
Line 6146  sub scantron_warning_screen { Line 6965  sub scantron_warning_screen {
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
  $scantron_config{'CODElength'}) {   $scantron_config{'CODElength'}) {
  $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">'.&mt('None').'</span>'; }
  $CODElist=   $CODElist=
     '<tr><td><b>'.&mt('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>';
     }      }
       my $lastbubblepoints;
       if ($env{'form.scantron_lastbubblepoints'} ne '') {
           $lastbubblepoints =
               '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.
               $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';
       }
     return ('      return ('
 <p>  <p>
 <span class="LC_warning">  <span class="LC_warning">
 '.&mt('Please double check the information below before clicking on \'[_1]\'',&mt($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>'.&mt('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>'.&mt('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.$lastbubblepoints.'
 </table>  </table>
 <br />  <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />
 <p> '.&mt('If this information is correct, please click on \'[_1]\'.',&mt($button_text)).'</p>  '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>
 <p> '.&mt('If something is incorrect, please click the \'Grading Menu\' button to start over.').'</p>  
   
 <br />  <br />
 ');  ');
Line 6186  sub scantron_do_warning { Line 7010  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>".&mt('You have forgetten to specify some information. Please go Back and try again.')."</p>");   $r->print("<p>".&mt('You have forgotten 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">'.&mt('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">'.&mt('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">'.&mt('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 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',$symb);
           my $bubbledbyhand=&hand_bubble_option();
  $r->print('   $r->print('
 '.$warning.'  '.$warning.$bubbledbyhand.'
 <input type="submit" name="submit" value="'.&mt('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" />
 ');  ');
Line 6241  SCANTRONFORM Line 7066  SCANTRONFORM
            '<input type="hidden" name="scantron.sub_bubblelines.'.$line.'" value="'.$env{"form.scantron.sub_bubblelines.$line"}.'" />'."\n";             '<input type="hidden" name="scantron.sub_bubblelines.'.$line.'" value="'.$env{"form.scantron.sub_bubblelines.$line"}.'" />'."\n";
        $chunk .=         $chunk .=
            '<input type="hidden" name="scantron.responsetype.'.$line.'" value="'.$env{"form.scantron.responsetype.$line"}.'" />'."\n";             '<input type="hidden" name="scantron.responsetype.'.$line.'" value="'.$env{"form.scantron.responsetype.$line"}.'" />'."\n";
          $chunk .=
              '<input type="hidden" name="scantron.residpart.'.$line.'" value="'.$env{"form.scantron.residpart.$line"}.'" />'."\n";
        $result .= $chunk;         $result .= $chunk;
        $line++;         $line++;
    }      }
     return $result;      return $result;
 }  }
   
Line 6251  SCANTRONFORM Line 7078  SCANTRONFORM
   
 =item scantron_validate_file  =item scantron_validate_file
   
     Dispatch routine for doing validation of a bubble sheet data file.      Dispatch routine for doing validation of a bubblesheet data file.
   
     Also processes any necessary information resets that need to      Also processes any necessary information resets that need to
     occur before validation begins (ignore previous corrections,      occur before validation begins (ignore previous corrections,
Line 6264  sub scantron_validate_file { Line 7091  sub scantron_validate_file {
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
           
     # do the detection of only doing skipped records first befroe we delete      # do the detection of only doing skipped records first before we delete
     # them when doing the corrections reset      # them when doing the corrections reset
     if ($env{'form.scantron_options_redo'} ne 'redo_skipped_ready') {      if ($env{'form.scantron_options_redo'} ne 'redo_skipped_ready') {
  &reset_skipping_status();   &reset_skipping_status();
Line 6288  sub scantron_validate_file { Line 7115  sub scantron_validate_file {
     #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 $nav_error;      my $nav_error;
     my $max_bubble=&scantron_get_maxbubble(\$nav_error);      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
     }      }
     my $result=&scantron_form_start($max_bubble).$default_form_data;      my $result=&scantron_form_start($max_bubble).$default_form_data;
       if ($env{'form.scantron_lastbubblepoints'} ne '') {
           $result .= '<input type="hidden" name="scantron_lastbubblepoints" value="'.$env{'form.scantron_lastbubblepoints'}.'" />';
       }
     $r->print($result);      $r->print($result);
           
     my @validate_phases=( 'sequence',      my @validate_phases=( 'sequence',
Line 6311  sub scantron_validate_file { Line 7142  sub scantron_validate_file {
     while (!$stop && $currentphase < scalar(@validate_phases)) {      while (!$stop && $currentphase < scalar(@validate_phases)) {
  $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');   $r->print(&mt('Validating '.$validate_phases[$currentphase]).'<br />');
  $r->rflush();   $r->rflush();
        
  my $which="scantron_validate_".$validate_phases[$currentphase];   my $which="scantron_validate_".$validate_phases[$currentphase];
  {   {
     no strict 'refs';      no strict 'refs';
Line 6318  sub scantron_validate_file { Line 7150  sub scantron_validate_file {
  }   }
     }      }
     if (!$stop) {      if (!$stop) {
  my $warning=&scantron_warning_screen('Start Grading');   my $warning=&scantron_warning_screen('Start Grading',$symb);
  $r->print(&mt('Validation process complete.').'<br />'.   $r->print(&mt('Validation process complete.').'<br />'.
                   $warning.                    $warning.
                   &mt('Perform verification for each student after storage of submissions?').                    &mt('Perform verification for each student after storage of submissions?').
Line 6328  sub scantron_validate_file { Line 7160  sub scantron_validate_file {
                   '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').                    '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').
                   '</label></span><br />'.                    '</label></span><br />'.
                   &mt('Grading will take longer if you use verification.').'<br />'.                    &mt('Grading will take longer if you use verification.').'<br />'.
                   &mt("Alternatively, the 'Review bubblesheet data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.                    &mt('Otherwise, Grade/Manage/Review Bubblesheets [_1] Review bubblesheet data can be used once grading is complete.','&raquo;').'<br /><br />'.
                   '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.                    '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.
                   '<input type="hidden" name="command" value="scantron_process" />'."\n");                    '<input type="hidden" name="command" value="scantron_process" />'."\n");
     } else {      } else {
Line 6340  sub scantron_validate_file { Line 7172  sub scantron_validate_file {
     $r->print('<input type="submit" name="submit" value="'.&mt('Ignore').' &rarr; " />');      $r->print('<input type="submit" name="submit" value="'.&mt('Ignore').' &rarr; " />');
     $r->print(' '.&mt('this error').' <br />');      $r->print(' '.&mt('this error').' <br />');
   
     $r->print(" <p>".&mt("Or click the 'Grading Menu' button to start over.")."</p>");      $r->print('<p>'.&mt('Or return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>');
  } else {   } else {
             if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {              if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {
         $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue').' &rarr;" onclick="javascript:verify_bubble_radio(this.form)" />');          $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue').' &rarr;" onclick="javascript:verify_bubble_radio(this.form)" />');
Line 6361  sub scantron_validate_file { Line 7193  sub scantron_validate_file {
   
 =item scantron_remove_file  =item scantron_remove_file
   
    Removes the requested bubble sheet data file, makes sure that     Removes the requested bubblesheet data file, makes sure that
    scantron_original_<filename> is never removed     scantron_original_<filename> is never removed
   
   
Line 6386  sub scantron_remove_file { Line 7218  sub scantron_remove_file {
   
 =item scantron_remove_scan_data  =item scantron_remove_scan_data
   
    Removes all scan_data correction for the requested bubble sheet     Removes all scan_data correction for the requested bubblesheet
    data file.  (In the case that both the are doing skipped records we need     data file.  (In the case that both the are doing skipped records we need
    to remember the old skipped lines for the time being so that element     to remember the old skipped lines for the time being so that element
    persists for a while.)     persists for a while.)
Line 6423  sub scantron_remove_scan_data { Line 7255  sub scantron_remove_scan_data {
   
 =item scantron_getfile  =item scantron_getfile
   
     Fetches the requested bubble sheet data file (all 3 versions), and      Fetches the requested bubblesheet data file (all 3 versions), and
     the scan_data hash      the scan_data hash
       
   Arguments:    Arguments:
Line 6523  sub lonnet_putfile { Line 7355  sub lonnet_putfile {
   
 =item scantron_putfile  =item scantron_putfile
   
     Stores the current version of the bubble sheet data files, and the      Stores the current version of the bubblesheet data files, and the
     scan_data hash. (Does not modify the original version only the      scan_data hash. (Does not modify the original version only the
     corrected and skipped versions.      corrected and skipped versions.
   
Line 6617  sub get_todo_count { Line 7449  sub get_todo_count {
   
 =item scantron_put_line  =item scantron_put_line
   
     Updates the 'corrected' or 'skipped' versions of the bubble sheet      Updates the 'corrected' or 'skipped' versions of the bubblesheet
     data file.      data file.
   
  Arguments:   Arguments:
Line 6719  sub scantron_validate_sequence { Line 7551  sub scantron_validate_sequence {
  my @resources=   my @resources=
     $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);      $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);
  if (@resources) {   if (@resources) {
     $r->print("<p>".&mt('Some resources in the sequence currently are not set to exam mode. Grading these resources currently may not work correctly.')."</p>");      $r->print(
                   '<p class="LC_warning">'
                  .&mt('Some resources in the sequence currently are not set to'
                      .' bubblesheet exam mode. Grading these resources currently may not'
                      .' work correctly.')
                  .'</p>'
               );
     return (1,$currentphase);      return (1,$currentphase);
  }   }
     }      }
Line 6741  sub scantron_validate_ID { Line 7579  sub scantron_validate_ID {
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # parse needs the bubble_lines.. array.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble_lines.. array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
Line 6800  sub scantron_validate_ID { Line 7638  sub scantron_validate_ID {
   
   
 sub scantron_get_correction {  sub scantron_get_correction {
     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;      my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,
           $randomorder,$randompick,$respnumlookup,$startline)=@_;
 #FIXME in the case of a duplicated ID the previous line, probably need  #FIXME in the case of a duplicated ID the previous line, probably need
 #to show both the current line and the previous one and allow skipping  #to show both the current line and the previous one and allow skipping
 #the previous one or the current one  #the previous one or the current one
   
     if ( $$scan_record{'scantron.PaperID'} =~ /\S/) {      if ( $$scan_record{'scantron.PaperID'} =~ /\S/) {
  $r->print("<p>".&mt("<b>An error was detected ($error)</b>".          $r->print(
     " for PaperID <tt>[_1]</tt>",              '<p class="LC_warning">'
     $$scan_record{'scantron.PaperID'})."</p> \n");             .&mt('An error was detected ([_1]) for PaperID [_2]',
     } else {                  "<b>$error</b>",
  $r->print("<p>".&mt("<b>An error was detected ($error)</b>".                  '<tt>'.$$scan_record{'scantron.PaperID'}.'</tt>')
     " in scanline [_1] <pre>[_2]</pre>",             ."</p> \n");
     $i,$line)."</p> \n");      } else {
     }          $r->print(
     my $message="<p>".&mt("The ID on the form is  <tt>[_1]</tt><br />".              '<p class="LC_warning">'
   "The name on the paper is [_2],[_3]",             .&mt('An error was detected ([_1]) in scanline [_2] [_3]',
   $$scan_record{'scantron.ID'},                  "<b>$error</b>", $i, "<pre>$line</pre>")
   $$scan_record{'scantron.LastName'},             ."</p> \n");
   $$scan_record{'scantron.FirstName'})."</p>";      }
       my $message =
           '<p>'
          .&mt('The ID on the form is [_1]',
               "<tt>$$scan_record{'scantron.ID'}</tt>")
          .'<br />'
          .&mt('The name on the paper is [_1], [_2]',
               $$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");
Line 6828  sub scantron_get_correction { Line 7676  sub scantron_get_correction {
   
     if ($error =~ /ID$/) {      if ($error =~ /ID$/) {
  if ($error eq 'incorrectID') {   if ($error eq 'incorrectID') {
     $r->print("<p>".&mt("The encoded ID is not in the classlist").              $r->print('<p class="LC_warning">'.&mt("The encoded ID is not in the classlist").
       "</p>\n");        "</p>\n");
  } elsif ($error eq 'duplicateID') {   } elsif ($error eq 'duplicateID') {
     $r->print("<p>".&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."</p>\n");              $r->print('<p class="LC_warning">'.&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>".&mt("How should I handle this?")." <br /> \n");   $r->print("<p>".&mt("How should I handle this?")." <br /> \n");
Line 6841  sub scantron_get_correction { Line 7689  sub scantron_get_correction {
  $r->print(&Apache::loncommon::selectstudent_link('scantronupload',   $r->print(&Apache::loncommon::selectstudent_link('scantronupload',
        'scantron_username','scantron_domain'));         'scantron_username','scantron_domain'));
  $r->print(": <input type='text' name='scantron_username' value='' />");   $r->print(": <input type='text' name='scantron_username' value='' />");
  $r->print("\n@".   $r->print("\n:\n".
  &Apache::loncommon::select_dom_form($env{'request.role.domain'},'scantron_domain'));   &Apache::loncommon::select_dom_form($env{'request.role.domain'},'scantron_domain'));
   
  $r->print('</li>');   $r->print('</li>');
     } elsif ($error =~ /CODE$/) {      } elsif ($error =~ /CODE$/) {
  if ($error eq 'incorrectCODE') {   if ($error eq 'incorrectCODE') {
     $r->print("<p>".&mt("The encoded CODE is not in the list of possible CODEs.")."</p>\n");      $r->print('<p class="LC_warning">'.&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>".&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 class="LC_warning">'.&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>".&mt("The CODE on the form is  <tt>'[_1]'</tt>",   $r->print("<p>".&mt('The CODE on the form is [_1]',
     $$scan_record{'scantron.CODE'})."<br />\n");      "<tt>'$$scan_record{'scantron.CODE'}'</tt>")
                    ."</p>\n");
  $r->print($message);   $r->print($message);
  $r->print("<p>".&mt("How should I handle this?")." <br /> \n");   $r->print("<p>".&mt("How should I handle this?")."</p>\n");
  $r->print("\n<br /> ");   $r->print("\n<br /> ");
  my $i=0;   my $i=0;
  if ($error eq 'incorrectCODE'    if ($error eq 'incorrectCODE' 
Line 6881  sub scantron_get_correction { Line 7730  sub scantron_get_correction {
     $r->print("      $r->print("
     <label>      <label>
         <input type='radio' name='scantron_CODE_resolution' value='use_unfound'$checked />          <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.",         ".&mt("Use the CODE [_1] that was on the paper, ignoring the error.",
      "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."       "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."
     </label>");      </label>");
     $r->print("\n<br />");      $r->print("\n<br />");
Line 6919  ENDSCRIPT Line 7768  ENDSCRIPT
      "</label><input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" />"));       "</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>".&mt("There have been multiple bubbles scanned for some question(s)")."</p>\n");   $r->print('<p class="LC_warning">'.&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.   # The form field scantron_questions is acutally a list of line numbers.
  # represented by this form so:   # represented by this form so:
   
  my $line_list = &questions_to_line_list($arg);   my $line_list = &questions_to_line_list($arg,$randomorder,$randompick,
                                                   $respnumlookup,$startline);
   
  $r->print('<input type="hidden" name="scantron_questions" value="'.   $r->print('<input type="hidden" name="scantron_questions" value="'.
   $line_list.'" />');    $line_list.'" />');
Line 6932  ENDSCRIPT Line 7782  ENDSCRIPT
  $r->print("<p>".&mt("Please indicate which bubble should be used for grading")."</p>");   $r->print("<p>".&mt("Please indicate which bubble should be used for grading")."</p>");
  foreach my $question (@{$arg}) {   foreach my $question (@{$arg}) {
     my @linenums = &prompt_for_corrections($r,$question,$scan_config,      my @linenums = &prompt_for_corrections($r,$question,$scan_config,
                                                    $scan_record, $error);                                                     $scan_record, $error,
                                                      $randomorder,$randompick,
                                                      $respnumlookup,$startline);
             push(@lines_to_correct,@linenums);              push(@lines_to_correct,@linenums);
  }   }
         $r->print(&verify_bubbles_checked(@lines_to_correct));          $r->print(&verify_bubbles_checked(@lines_to_correct));
     } elsif ($error eq 'missingbubble') {      } elsif ($error eq 'missingbubble') {
  $r->print("<p>".&mt("There have been <b>no</b> bubbles scanned for some question(s)")."</p>\n");   $r->print('<p class="LC_warning">'.&mt("There have been [_1]no[_2] bubbles scanned for some question(s)",'<b>','</b>')."</p>\n");
  $r->print($message);   $r->print($message);
  $r->print("<p>".&mt("Please indicate which bubble should be used for grading.")."</p>");   $r->print("<p>".&mt("Please indicate which bubble should be used for grading.")."</p>");
  $r->print(&mt("Some questions have no scanned bubbles.")."\n");   $r->print(&mt("Some questions have no scanned bubbles.")."\n");
Line 6945  ENDSCRIPT Line 7797  ENDSCRIPT
  # The form field scantron_questions is actually a list of line numbers not   # The form field scantron_questions is actually a list of line numbers not
  # a list of question numbers. Therefore:   # a list of question numbers. Therefore:
  #   #
   
  my $line_list = &questions_to_line_list($arg);   my $line_list = &questions_to_line_list($arg,$randomorder,$randompick,
                                                   $respnumlookup,$startline);
   
  $r->print('<input type="hidden" name="scantron_questions" value="'.   $r->print('<input type="hidden" name="scantron_questions" value="'.
   $line_list.'" />');    $line_list.'" />');
  foreach my $question (@{$arg}) {   foreach my $question (@{$arg}) {
     my @linenums = &prompt_for_corrections($r,$question,$scan_config,      my @linenums = &prompt_for_corrections($r,$question,$scan_config,
                                                    $scan_record, $error);                                                     $scan_record, $error,
                                                      $randomorder,$randompick,
                                                      $respnumlookup,$startline);
             push(@lines_to_correct,@linenums);              push(@lines_to_correct,@linenums);
  }   }
         $r->print(&verify_bubbles_checked(@lines_to_correct));          $r->print(&verify_bubbles_checked(@lines_to_correct));
Line 6966  sub verify_bubbles_checked { Line 7821  sub verify_bubbles_checked {
     my (@ansnums) = @_;      my (@ansnums) = @_;
     my $ansnumstr = join('","',@ansnums);      my $ansnumstr = join('","',@ansnums);
     my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");      my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
       &js_escape(\$warning);
     my $output = &Apache::lonhtmlcommon::scripttag((<<ENDSCRIPT));      my $output = &Apache::lonhtmlcommon::scripttag((<<ENDSCRIPT));
 function verify_bubble_radio(form) {  function verify_bubble_radio(form) {
     var ansnumArray = new Array ("$ansnumstr");      var ansnumArray = new Array ("$ansnumstr");
Line 7003  used to fill in the scantron_questions f Line 7859  used to fill in the scantron_questions f
   
   Arguments:    Arguments:
      questions    - Reference to an array of questions.       questions    - Reference to an array of questions.
        randomorder  - True if randomorder in use.
        randompick   - True if randompick in use.
        respnumlookup - Reference to HASH mapping question numbers in bubble lines
                        for current line to question number used for same question
                        in "Master Seqence" (as seen by Course Coordinator).
        startline    - Reference to hash where key is question number (0 is first)
                       and key is number of first bubble line for current student
                       or code-based randompick and/or randomorder.
   
 =cut  =cut
   
   
 sub questions_to_line_list {  sub questions_to_line_list {
     my ($questions) = @_;      my ($questions,$randomorder,$randompick,$respnumlookup,$startline) = @_;
     my @lines;      my @lines;
   
     foreach my $item (@{$questions}) {      foreach my $item (@{$questions}) {
Line 7017  sub questions_to_line_list { Line 7881  sub questions_to_line_list {
         if ($item =~ /^(\d+)\.(\d+)$/) {          if ($item =~ /^(\d+)\.(\d+)$/) {
             $question = $1;              $question = $1;
             my $subquestion = $2;              my $subquestion = $2;
             $first = $first_bubble_line{$question-1} + 1;              my $responsenum = $question-1;
             my @subans = split(/,/,$subdivided_bubble_lines{$question-1});              if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
                   $responsenum = $respnumlookup->{$question-1};
                   if (ref($startline) eq 'HASH') {
                       $first = $startline->{$question-1} + 1;
                   }
               } else {
                   $first = $first_bubble_line{$responsenum} + 1;
               }
               my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
             my $subcount = 1;              my $subcount = 1;
             while ($subcount<$subquestion) {              while ($subcount<$subquestion) {
                 $first += $subans[$subcount-1];                  $first += $subans[$subcount-1];
Line 7026  sub questions_to_line_list { Line 7898  sub questions_to_line_list {
             }              }
             $count = $subans[$subquestion-1];              $count = $subans[$subquestion-1];
         } else {          } else {
     $first   = $first_bubble_line{$question-1} + 1;              my $responsenum = $question-1;
     $count   = $bubble_lines_per_response{$question-1};              if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
                   $responsenum = $respnumlookup->{$question-1};
                   if (ref($startline) eq 'HASH') {
                       $first = $startline->{$question-1} + 1;
                   }
               } else {
                   $first = $first_bubble_line{$responsenum} + 1;
               }
       $count   = $bubble_lines_per_response{$responsenum};
         }          }
         $last = $first+$count-1;          $last = $first+$count-1;
         push(@lines, ($first..$last));          push(@lines, ($first..$last));
Line 7049  for multi and missing bubble cases). Line 7929  for multi and missing bubble cases).
    $scan_config - The scantron file configuration hash.     $scan_config - The scantron file configuration hash.
    $scan_record - Reference to the hash that has the the parsed scanlines.     $scan_record - Reference to the hash that has the the parsed scanlines.
    $error       - Type of error     $error       - Type of error
      $randomorder - True if randomorder in use.
      $randompick  - True if randompick in use.
      $respnumlookup - Reference to HASH mapping question numbers in bubble lines
                       for current line to question number used for same question
                       in "Master Seqence" (as seen by Course Coordinator).
      $startline   - Reference to hash where key is question number (0 is first)
                     and value is number of first bubble line for current student
                     or code-based randompick and/or randomorder.
   
   
  Implicit inputs:   Implicit inputs:
    %bubble_lines_per_response   - Starting line numbers for each question.     %bubble_lines_per_response   - Starting line numbers for each question.
Line 7073  for multi and missing bubble cases). Line 7962  for multi and missing bubble cases).
 =cut  =cut
   
 sub prompt_for_corrections {  sub prompt_for_corrections {
     my ($r, $question, $scan_config, $scan_record, $error) = @_;      my ($r, $question, $scan_config, $scan_record, $error, $randomorder,
           $randompick, $respnumlookup, $startline) = @_;
     my ($current_line,$lines);      my ($current_line,$lines);
     my @linenums;      my @linenums;
     my $questionnum = $question;      my $questionnum = $question;
       my ($first,$responsenum);
     if ($question =~ /^(\d+)\.(\d+)$/) {      if ($question =~ /^(\d+)\.(\d+)$/) {
         $question = $1;          $question = $1;
         $current_line = $first_bubble_line{$question-1} + 1 ;  
         my $subquestion = $2;          my $subquestion = $2;
         my @subans = split(/,/,$subdivided_bubble_lines{$question-1});          if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
               $responsenum = $respnumlookup->{$question-1};
               if (ref($startline) eq 'HASH') {
                   $first = $startline->{$question-1};
               }
           } else {
               $responsenum = $question-1;
               $first = $first_bubble_line{$responsenum};
           }
           $current_line = $first + 1 ;
           my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
         my $subcount = 1;          my $subcount = 1;
         while ($subcount<$subquestion) {          while ($subcount<$subquestion) {
             $current_line += $subans[$subcount-1];              $current_line += $subans[$subcount-1];
Line 7089  sub prompt_for_corrections { Line 7989  sub prompt_for_corrections {
         }          }
         $lines = $subans[$subquestion-1];          $lines = $subans[$subquestion-1];
     } else {      } else {
         $current_line = $first_bubble_line{$question-1} + 1 ;          if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH')) {
         $lines        = $bubble_lines_per_response{$question-1};              $responsenum = $respnumlookup->{$question-1};
               if (ref($startline) eq 'HASH') { 
                   $first = $startline->{$question-1};
               }
           } else {
               $responsenum = $question-1;
               $first = $first_bubble_line{$responsenum};
           }
           $current_line = $first + 1;
           $lines        = $bubble_lines_per_response{$responsenum};
     }      }
     if ($lines > 1) {      if ($lines > 1) {
         $r->print(&mt('The group of bubble lines below responds to a single question.').'<br />');          $r->print(&mt('The group of bubble lines below responds to a single question.').'<br />');
         if (($responsetype_per_response{$question-1} eq 'essayresponse') ||          if (($responsetype_per_response{$responsenum} eq 'essayresponse') ||
             ($responsetype_per_response{$question-1} eq 'formularesponse') ||              ($responsetype_per_response{$responsenum} eq 'formularesponse') ||
             ($responsetype_per_response{$question-1} eq 'stringresponse') ||              ($responsetype_per_response{$responsenum} eq 'stringresponse') ||
             ($responsetype_per_response{$question-1} eq 'imageresponse') ||              ($responsetype_per_response{$responsenum} eq 'imageresponse') ||
             ($responsetype_per_response{$question-1} eq 'reactionresponse') ||              ($responsetype_per_response{$responsenum} eq 'reactionresponse') ||
             ($responsetype_per_response{$question-1} eq 'organicresponse')) {              ($responsetype_per_response{$responsenum} eq 'organicresponse')) {
             $r->print(&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 bubblesheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during bubblesheet 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 />');              $r->print(
                   &mt("Although this particular question type requires handgrading, the instructions for this question in the bubblesheet exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines)
                  .'<br /><br />'
                  .&mt('A non-zero score can be assigned to the student during bubblesheet 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 {          } else {
             $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");              $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++) {      for (my $i =0; $i < $lines; $i++) {
         my $selected = $$scan_record{"scantron.$current_line.answer"};          my $selected = $$scan_record{"scantron.$current_line.answer"};
  &scantron_bubble_selector($r,$scan_config,$current_line,    &scantron_bubble_selector($r,$scan_config,$current_line,
           $questionnum,$error,split('', $selected));            $questionnum,$error,split('', $selected));
         push(@linenums,$current_line);          push(@linenums,$current_line);
  $current_line++;   $current_line++;
Line 7140  sub scantron_bubble_selector { Line 8058  sub scantron_bubble_selector {
     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') { 
           if (($$scan_config{'BubblesPerRow'} =~ /^\d+$/) &&
               ($$scan_config{'BubblesPerRow'} > 0)) {
               $max=$$scan_config{'BubblesPerRow'};
               if (($scmode eq 'number') && ($max > 10)) {
                   $max = 10;
               } elsif (($scmode eq 'letter') && $max > 26) {
                   $max = 26;
               }
           } else {
               $max = 10;
           }
       }
   
     my @alphabet=('A'..'Z');      my @alphabet=('A'..'Z');
     $r->print(&Apache::loncommon::start_data_table().      $r->print(&Apache::loncommon::start_data_table().
Line 7295  sub scantron_validate_CODE { Line 8225  sub scantron_validate_CODE {
     my %allcodes=&get_codes();      my %allcodes=&get_codes();
   
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # parse needs the lines per response array.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the lines per response array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
Line 7349  sub scantron_validate_doublebubble { Line 8279  sub scantron_validate_doublebubble {
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my (undef,undef,$sequence)=
           &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
       my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb,%orderedforcode,%respnumlookup,%startline);
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
   
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # parse needs the bubble line array.      if (ref($map)) {
           $randomorder = $map->randomorder();
           $randompick = $map->randompick();
           if ($randomorder || $randompick) {
               $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
               if ($nav_error) {
                   $r->print(&navmap_errormsg());
                   return(1,$currentphase);
               }
               &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                                       \%grader_randomlists_by_symb,$bubbles_per_row);
           }
       } else {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
       &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble line array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
Line 7364  sub scantron_validate_doublebubble { Line 8325  sub scantron_validate_doublebubble {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
  my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,   my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
  $scan_data);   $scan_data,undef,\%idmap,$randomorder,
                                                    $randompick,$sequence,\@master_seq,
                                                    \%symb_to_resource,\%grader_partids_by_symb,
                                                    \%orderedforcode,\%respnumlookup,\%startline);
  if (!defined($$scan_record{'scantron.doubleerror'})) { next; }   if (!defined($$scan_record{'scantron.doubleerror'})) { next; }
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line,
  'doublebubble',   'doublebubble',
  $$scan_record{'scantron.doubleerror'});   $$scan_record{'scantron.doubleerror'},
                                    $randomorder,$randompick,\%respnumlookup,\%startline);
     return (1,$currentphase);      return (1,$currentphase);
     }      }
     return (0,$currentphase+1);      return (0,$currentphase+1);
Line 7376  sub scantron_validate_doublebubble { Line 8341  sub scantron_validate_doublebubble {
   
   
 sub scantron_get_maxbubble {  sub scantron_get_maxbubble {
     my ($nav_error) = @_;      my ($nav_error,$scantron_config) = @_;
     if (defined($env{'form.scantron_maxbubble'}) &&      if (defined($env{'form.scantron_maxbubble'}) &&
  $env{'form.scantron_maxbubble'}) {   $env{'form.scantron_maxbubble'}) {
  &restore_bubble_lines();   &restore_bubble_lines();
Line 7395  sub scantron_get_maxbubble { Line 8360  sub scantron_get_maxbubble {
     }      }
     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 $bubbles_per_row = &bubblesheet_bubbles_per_row($scantron_config);
   
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
   
Line 7406  sub scantron_get_maxbubble { Line 8372  sub scantron_get_maxbubble {
     %first_bubble_line         = ();      %first_bubble_line         = ();
     %subdivided_bubble_lines   = ();      %subdivided_bubble_lines   = ();
     %responsetype_per_response = ();      %responsetype_per_response = ();
       %masterseq_id_responsenum  = ();
   
     my $response_number = 0;      my $response_number = 0;
     my $bubble_line     = 0;      my $bubble_line     = 0;
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
         my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,$udom);          my $resid = $resource->id(); 
           my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,
                                                             $udom,undef,$bubbles_per_row);
         if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {          if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
     foreach my $part_id (@{$parts}) {      foreach my $part_id (@{$parts}) {
                 my $lines;                  my $lines;
Line 7439  sub scantron_get_maxbubble { Line 8408  sub scantron_get_maxbubble {
                     if (ref($analysis->{$part_id.'.shown'}) eq 'ARRAY') {                      if (ref($analysis->{$part_id.'.shown'}) eq 'ARRAY') {
                         $numshown = scalar(@{$analysis->{$part_id.'.shown'}});                          $numshown = scalar(@{$analysis->{$part_id.'.shown'}});
                     }                      }
                     my $bubbles_per_line = 10;                      my $bubbles_per_row =
                     my $inner_bubble_lines = int($numbub/$bubbles_per_line);                          &bubblesheet_bubbles_per_row($scantron_config);
                     if (($numbub % $bubbles_per_line) != 0) {                      my $inner_bubble_lines = int($numbub/$bubbles_per_row);
                       if (($numbub % $bubbles_per_row) != 0) {
                         $inner_bubble_lines++;                          $inner_bubble_lines++;
                     }                      }
                     for (my $i=0; $i<$numshown; $i++) {                      for (my $i=0; $i<$numshown; $i++) {
Line 7452  sub scantron_get_maxbubble { Line 8422  sub scantron_get_maxbubble {
                     $lines = $numshown * $inner_bubble_lines;                      $lines = $numshown * $inner_bubble_lines;
                 } else {                  } else {
                     $lines = $analysis->{"$part_id.bubble_lines"};                      $lines = $analysis->{"$part_id.bubble_lines"};
                 }                   }
   
                 $first_bubble_line{$response_number} = $bubble_line;                  $first_bubble_line{$response_number} = $bubble_line;
         $bubble_lines_per_response{$response_number} = $lines;          $bubble_lines_per_response{$response_number} = $lines;
                 $responsetype_per_response{$response_number} =                   $responsetype_per_response{$response_number} = 
                     $analysis->{$part_id.'.type'};                      $analysis->{$part_id.'.type'};
                   $masterseq_id_responsenum{$resid.'_'.$part_id} = $response_number;  
         $response_number++;          $response_number++;
   
         $bubble_line +=  $lines;          $bubble_line +=  $lines;
Line 7473  sub scantron_get_maxbubble { Line 8444  sub scantron_get_maxbubble {
     return $env{'form.scantron_maxbubble'};      return $env{'form.scantron_maxbubble'};
 }  }
   
   sub bubblesheet_bubbles_per_row {
       my ($scantron_config) = @_;
       my $bubbles_per_row;
       if (ref($scantron_config) eq 'HASH') {
           $bubbles_per_row = $scantron_config->{'BubblesPerRow'};
       }
       if ((!$bubbles_per_row) || ($bubbles_per_row < 1)) {
           $bubbles_per_row = 10;
       }
       return $bubbles_per_row;
   }
   
 sub scantron_validate_missingbubbles {  sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my (undef,undef,$sequence)=
           &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
       my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb,%orderedforcode,%respnumlookup,%startline);
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
   
     my $nav_error;      my $nav_error;
     my $max_bubble=&scantron_get_maxbubble(\$nav_error);      if (ref($map)) {
           $randomorder = $map->randomorder();
           $randompick = $map->randompick();
           if ($randomorder || $randompick) {
               $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
               if ($nav_error) {
                   $r->print(&navmap_errormsg());
                   return(1,$currentphase);
               }
               &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                                       \%grader_randomlists_by_symb,$bubbles_per_row);
           }
       } else {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
   
       my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
           $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
     }      }
   
     if (!$max_bubble) { $max_bubble=2**31; }      if (!$max_bubble) { $max_bubble=2**31; }
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
  my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,   my $scan_record =
  $scan_data);              &scantron_parse_scanline($line,$i,\%scantron_config,$scan_data,undef,\%idmap,
        $randomorder,$randompick,$sequence,\@master_seq,
                                        \%symb_to_resource,\%grader_partids_by_symb,
                                        \%orderedforcode,\%respnumlookup,\%startline);
  if (!defined($$scan_record{'scantron.missingerror'})) { next; }   if (!defined($$scan_record{'scantron.missingerror'})) { next; }
  my @to_correct;   my @to_correct;
   
Line 7503  sub scantron_validate_missingbubbles { Line 8524  sub scantron_validate_missingbubbles {
             if ($missing =~ /^(\d+)\.(\d+)$/) {              if ($missing =~ /^(\d+)\.(\d+)$/) {
                my $question = $1;                 my $question = $1;
                my $subquestion = $2;                 my $subquestion = $2;
                if (!defined($first_bubble_line{$question -1})) { next; }                 my ($first,$responsenum);
                my $first = $first_bubble_line{$question-1};                 if ($randomorder || $randompick) {
                my @subans = split(/,/,$subdivided_bubble_lines{$question-1});                     $responsenum = $respnumlookup{$question-1};
                      $first = $startline{$question-1};
                  } else {
                      $responsenum = $question-1; 
                      $first = $first_bubble_line{$responsenum};
                  }
                  if (!defined($first)) { next; }
                  my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
                my $subcount = 1;                 my $subcount = 1;
                while ($subcount<$subquestion) {                 while ($subcount<$subquestion) {
                    $first += $subans[$subcount-1];                     $first += $subans[$subcount-1];
Line 7514  sub scantron_validate_missingbubbles { Line 8542  sub scantron_validate_missingbubbles {
                my $count = $subans[$subquestion-1];                 my $count = $subans[$subquestion-1];
                $lastbubble = $first + $count;                 $lastbubble = $first + $count;
             } else {              } else {
                 if (!defined($first_bubble_line{$missing - 1})) { next; }                 my ($first,$responsenum);
                 $lastbubble = $first_bubble_line{$missing - 1} + $bubble_lines_per_response{$missing - 1};                 if ($randomorder || $randompick) {
                      $responsenum = $respnumlookup{$missing-1};
                      $first = $startline{$missing-1};
                  } else {
                      $responsenum = $missing-1;
                      $first = $first_bubble_line{$responsenum};
                  }
                  if (!defined($first)) { next; }
                  $lastbubble = $first + $bubble_lines_per_response{$responsenum};
             }              }
             if ($lastbubble > $max_bubble) { next; }              if ($lastbubble > $max_bubble) { next; }
     push(@to_correct,$missing);      push(@to_correct,$missing);
  }   }
  if (@to_correct) {   if (@to_correct) {
     &scantron_get_correction($r,$i,$scan_record,\%scantron_config,      &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
      $line,'missingbubble',\@to_correct);       $line,'missingbubble',\@to_correct,
                                        $randomorder,$randompick,\%respnumlookup,
                                        \%startline);
     return (1,$currentphase);      return (1,$currentphase);
  }   }
   
Line 7530  sub scantron_validate_missingbubbles { Line 8568  sub scantron_validate_missingbubbles {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   sub hand_bubble_option {
       my (undef, undef, $sequence) =
           &Apache::lonnet::decode_symb($env{'form.selectpage'});
       return if ($sequence eq '');
       my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           return;
       }
       my $needs_hand_bubbles;
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       foreach my $res (@resources) {
           if (ref($res)) {
               if ($res->is_problem()) {
                   my $partlist = $res->parts();
                   foreach my $part (@{ $partlist }) {
                       my @types = $res->responseType($part);
                       if (grep(/^(chem|essay|image|formula|math|string|functionplot)$/,@types)) {
                           $needs_hand_bubbles = 1;
                           last;
                       }
                   }
               }
           }
       }
       if ($needs_hand_bubbles) {
           my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
           my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
           return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.
                  &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').
                  '<label><input type="radio" name="scantron_lastbubblepoints" value="'.$bubbles_per_row.'" checked="checked" />'.&mt('[quant,_1,point]',$bubbles_per_row).'</label>&nbsp;'.&mt('or').'&nbsp;'.
                  '<label><input type="radio" name="scantron_lastbubblepoints" value="0" />'.&mt('0 points').'</label></p>';
       }
       return;
   }
   
 sub scantron_process_students {  sub scantron_process_students {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
Line 7541  sub scantron_process_students { Line 8614  sub scantron_process_students {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); 
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
Line 7548  sub scantron_process_students { Line 8622  sub scantron_process_students {
     unless (ref($navmap)) {      unless (ref($navmap)) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
     }    
     my $map=$navmap->getResourceByUrl($sequence);  
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);  
     my (%grader_partids_by_symb,%grader_randomlists_by_symb);  
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,  
                             \%grader_randomlists_by_symb);  
     my $resource_error;  
     foreach my $resource (@resources) {  
         my $ressymb;  
         if (ref($resource)) {  
             $ressymb = $resource->symb();  
         } else {  
             $resource_error = 1;  
             last;  
         }  
         my ($analysis,$parts) =  
             &scantron_partids_tograde($resource,$env{'request.course.id'},  
                                       $env{'user.name'},$env{'user.domain'},1);  
         $grader_partids_by_symb{$ressymb} = $parts;  
         if (ref($analysis) eq 'HASH') {  
             if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {  
                 $grader_randomlists_by_symb{$ressymb} =   
                     $analysis->{'parts_withrandomlist'};  
             }  
         }  
     }      }
     if ($resource_error) {      my $map=$navmap->getResourceByUrl($sequence);
       my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb);
       if (ref($map)) {
           $randomorder = $map->randomorder();
           $randompick = $map->randompick();
       } else {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
     }      }
       my $nav_error;
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       if ($randomorder || $randompick) {
           $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
           if ($nav_error) {
               $r->print(&navmap_errormsg());
               return '';
           }
       }
       &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                               \%grader_randomlists_by_symb,$bubbles_per_row);
   
     my ($uname,$udom);      my ($uname,$udom);
     my $result= <<SCANTRONFORM;      my $result= <<SCANTRONFORM;
Line 7592  SCANTRONFORM Line 8658  SCANTRONFORM
           
     my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));      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,'Bubblesheet Status',      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
      'Bubblesheet Progress',$count,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,'Processing first student');
     'inline',undef,'scantronupload');  
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,  
   'Processing first student');  
     $r->print('<br />');      $r->print('<br />');
     my $start=&Time::HiRes::time();      my $start=&Time::HiRes::time();
     my $i=-1;      my $i=-1;
     my $started;      my $started;
   
     my $nav_error;      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.  
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
Line 7621  SCANTRONFORM Line 8683  SCANTRONFORM
   
     my %lettdig = &letter_to_digits();      my %lettdig = &letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
       my %orderedforcode;
   
     while ($i<$scanlines->{'count'}) {      while ($i<$scanlines->{'count'}) {
   ($uname,$udom)=('','');    ($uname,$udom)=('','');
Line 7628  SCANTRONFORM Line 8691  SCANTRONFORM
   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; }
  if ($started) {   if ($started) {
     &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student');
      'last student');  
  }   }
  $started=1;   $started=1;
           my %respnumlookup = ();
           my %startline = ();
           my $total;
   my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
   $scan_data);                                                   $scan_data,undef,\%idmap,$randomorder,
                                                    $randompick,$sequence,\@master_seq,
                                                    \%symb_to_resource,\%grader_partids_by_symb,
                                                    \%orderedforcode,\%respnumlookup,\%startline,
                                                    \$total);
   unless ($uname=&scantron_find_student($scan_record,$scan_data,    unless ($uname=&scantron_find_student($scan_record,$scan_data,
        \%idmap,$i)) {         \%idmap,$i)) {
      &scantron_add_delay(\@delayqueue,$line,       &scantron_add_delay(\@delayqueue,$line,
Line 7645  SCANTRONFORM Line 8714  SCANTRONFORM
   'Student '.$uname.' has multiple sheets',2);    'Student '.$uname.' has multiple sheets',2);
      next;       next;
   }    }
           my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
           my $user = $uname.':'.$usec;
   ($uname,$udom)=split(/:/,$uname);    ($uname,$udom)=split(/:/,$uname);
   
           my $scancode;
           if ((exists($scan_record->{'scantron.CODE'})) &&
               (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) {
               $scancode = $scan_record->{'scantron.CODE'};
           } else {
               $scancode = '';
           }
   
           my @mapresources = @resources;
           if ($randomorder || $randompick) {
               @mapresources = 
                   &users_order($user,$scancode,$sequence,\@master_seq,\%symb_to_resource,
                                \%orderedforcode);
           }
         my (%partids_by_symb,$res_error);          my (%partids_by_symb,$res_error);
         foreach my $resource (@resources) {          foreach my $resource (@mapresources) {
             my $ressymb;              my $ressymb;
             if (ref($resource)) {              if (ref($resource)) {
                 $ressymb = $resource->symb();                  $ressymb = $resource->symb();
Line 7658  SCANTRONFORM Line 8743  SCANTRONFORM
             }              }
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                   my $currcode;
                   if (exists($grader_randomlists_by_symb{$ressymb})) {
                       $currcode = $scancode;
                   }
                 my ($analysis,$parts) =                  my ($analysis,$parts) =
                     &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);                      &scantron_partids_tograde($resource,$env{'request.course.id'},
                                                 $uname,$udom,undef,$bubbles_per_row,
                                                 $currcode);
                 $partids_by_symb{$ressymb} = $parts;                  $partids_by_symb{$ressymb} = $parts;
             } else {              } else {
                 $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};                  $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
Line 7679  SCANTRONFORM Line 8770  SCANTRONFORM
     &scantron_putfile($scanlines,$scan_data);      &scantron_putfile($scanlines,$scan_data);
  }   }
   
         my $scancode;  
         if ((exists($scan_record->{'scantron.CODE'})) &&  
             (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) {  
             $scancode = $scan_record->{'scantron.CODE'};  
         } else {  
             $scancode = '';  
         }  
   
         if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,          if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                    \@resources,\%partids_by_symb) eq 'ssi_error') {                                     \@mapresources,\%partids_by_symb,
                                      $bubbles_per_row,$randomorder,$randompick,
                                      \%respnumlookup,\%startline) 
               eq 'ssi_error') {
             $ssi_error = 0; # So end of handler error message does not trigger.              $ssi_error = 0; # So end of handler error message does not trigger.
             $r->print("</form>");              $r->print("</form>");
             &ssi_print_error($r);              &ssi_print_error($r);
Line 7696  SCANTRONFORM Line 8782  SCANTRONFORM
             return '';      # Why return ''?  Beats me.              return '';      # Why return ''?  Beats me.
         }          }
   
           if (($scancode) && ($randomorder || $randompick)) {
               my $parmresult =
                   &Apache::lonparmset::storeparm_by_symb($symb,
                                                          '0_examcode',2,$scancode,
                                                          'string_examcode',$uname,
                                                          $udom);
           }
  $completedstudents{$uname}={'line'=>$line};   $completedstudents{$uname}={'line'=>$line};
         if ($env{'form.verifyrecord'}) {          if ($env{'form.verifyrecord'}) {
             my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};              my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};
               if ($randompick) {
                   if ($total) {
                       $lastpos = $total*$scantron_config{'Qlength'};
                   }
               }
   
             my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos);              my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos);
             chomp($studentdata);              chomp($studentdata);
             $studentdata =~ s/\r$//;              $studentdata =~ s/\r$//;
             my $studentrecord = '';              my $studentrecord = '';
             my $counter = -1;              my $counter = -1;
             foreach my $resource (@resources) {              foreach my $resource (@mapresources) {
                 my $ressymb = $resource->symb();                  my $ressymb = $resource->symb();
                 ($counter,my $recording) =                  ($counter,my $recording) =
                     &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},                      &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                              $counter,$studentdata,$partids_by_symb{$ressymb},                                               $counter,$studentdata,$partids_by_symb{$ressymb},
                                              \%scantron_config,\%lettdig,$numletts);                                               \%scantron_config,\%lettdig,$numletts,$randomorder,
                                                $randompick,\%respnumlookup,\%startline);
                 $studentrecord .= $recording;                  $studentrecord .= $recording;
             }              }
             if ($studentrecord ne $studentdata) {              if ($studentrecord ne $studentdata) {
                 &Apache::lonxml::clear_problem_counter();                  &Apache::lonxml::clear_problem_counter();
                 if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,                  if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                            \@resources,\%partids_by_symb) eq 'ssi_error') {                                             \@mapresources,\%partids_by_symb,
                                              $bubbles_per_row,$randomorder,$randompick,
                                              \%respnumlookup,\%startline) 
                       eq 'ssi_error') {
                     $ssi_error = 0; # So end of handler error message does not trigger.                      $ssi_error = 0; # So end of handler error message does not trigger.
                     $r->print("</form>");                      $r->print("</form>");
                     &ssi_print_error($r);                      &ssi_print_error($r);
Line 7725  SCANTRONFORM Line 8828  SCANTRONFORM
                 }                  }
                 $counter = -1;                  $counter = -1;
                 $studentrecord = '';                  $studentrecord = '';
                 foreach my $resource (@resources) {                  foreach my $resource (@mapresources) {
                     my $ressymb = $resource->symb();                      my $ressymb = $resource->symb();
                     ($counter,my $recording) =                      ($counter,my $recording) =
                         &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},                          &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                                  $counter,$studentdata,$partids_by_symb{$ressymb},                                                   $counter,$studentdata,$partids_by_symb{$ressymb},
                                                  \%scantron_config,\%lettdig,$numletts);                                                   \%scantron_config,\%lettdig,$numletts,
                                                    $randomorder,$randompick,\%respnumlookup,
                                                    \%startline);
                     $studentrecord .= $recording;                      $studentrecord .= $recording;
                 }                  }
                 if ($studentrecord ne $studentdata) {                  if ($studentrecord ne $studentdata) {
                     $r->print('<p><span class="LC_error">');                      $r->print('<p><span class="LC_warning">');
                     if ($scancode eq '') {                      if ($scancode eq '') {
                         $r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2].',                          $r->print(&mt('Mismatch grading bubblesheet for user: [_1] with ID: [_2].',
                                   $uname.':'.$udom,$scan_record->{'scantron.ID'}));                                    $uname.':'.$udom,$scan_record->{'scantron.ID'}));
                     } else {                      } else {
                         $r->print(&mt('Mismatch grading bubble sheet for user: [_1] with ID: [_2] and CODE: [_3].',                          $r->print(&mt('Mismatch grading bubblesheet for user: [_1] with ID: [_2] and CODE: [_3].',
                                   $uname.':'.$udom,$scan_record->{'scantron.ID'},$scancode));                                    $uname.':'.$udom,$scan_record->{'scantron.ID'},$scancode));
                     }                      }
                     $r->print('</span><br />'.&Apache::loncommon::start_data_table()."\n".                      $r->print('</span><br />'.&Apache::loncommon::start_data_table()."\n".
Line 7747  SCANTRONFORM Line 8852  SCANTRONFORM
                               '<th>'.&mt('Source').'</th><th>'.&mt('Bubbled responses').'</th>'.                                '<th>'.&mt('Source').'</th><th>'.&mt('Bubbled responses').'</th>'.
                               &Apache::loncommon::end_data_table_header_row()."\n".                                &Apache::loncommon::end_data_table_header_row()."\n".
                               &Apache::loncommon::start_data_table_row().                                &Apache::loncommon::start_data_table_row().
                               '<td>'.&mt('Bubble Sheet').'</td>'.                                '<td>'.&mt('Bubblesheet').'</td>'.
                               '<td><span class="LC_nobreak">'.$studentdata.'</span></td>'.                                '<td><span class="LC_nobreak" style="white-space: pre;"><tt>'.$studentdata.'</tt></span></td>'.
                               &Apache::loncommon::end_data_table_row().                                &Apache::loncommon::end_data_table_row().
                               &Apache::loncommon::start_data_table_row().                                &Apache::loncommon::start_data_table_row().
                               '<td>Stored submissions</td>'.                                '<td>'.&mt('Stored submissions').'</td>'.
                               '<td><span class="LC_nobreak">'.$studentrecord.'</span></td>'."\n".                                '<td><span class="LC_nobreak" style="white-space: pre;"><tt>'.$studentrecord.'</tt></span></td>'."\n".
                               &Apache::loncommon::end_data_table_row().                                &Apache::loncommon::end_data_table_row().
                               &Apache::loncommon::end_data_table().'</p>');                                &Apache::loncommon::end_data_table().'</p>');
                 } else {                  } else {
Line 7778  SCANTRONFORM Line 8883  SCANTRONFORM
 }  }
   
 sub graders_resources_pass {  sub graders_resources_pass {
     my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb) = @_;      my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb,
           $bubbles_per_row) = @_;
     if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) &&       if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) && 
         (ref($grader_randomlists_by_symb) eq 'HASH')) {          (ref($grader_randomlists_by_symb) eq 'HASH')) {
         foreach my $resource (@{$resources}) {          foreach my $resource (@{$resources}) {
             my $ressymb = $resource->symb();              my $ressymb = $resource->symb();
             my ($analysis,$parts) =              my ($analysis,$parts) =
                 &scantron_partids_tograde($resource,$env{'request.course.id'},                  &scantron_partids_tograde($resource,$env{'request.course.id'},
                                           $env{'user.name'},$env{'user.domain'},1);                                            $env{'user.name'},$env{'user.domain'},
                                             1,$bubbles_per_row);
             $grader_partids_by_symb->{$ressymb} = $parts;              $grader_partids_by_symb->{$ressymb} = $parts;
             if (ref($analysis) eq 'HASH') {              if (ref($analysis) eq 'HASH') {
                 if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {                  if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
Line 7798  sub graders_resources_pass { Line 8905  sub graders_resources_pass {
     return;      return;
 }  }
   
   =pod
   
   =item users_order
   
     Returns array of resources in current map, ordered based on either CODE,
     if this is a CODEd exam, or based on student's identity if this is a 
     "NAMEd" exam.
   
     Should be used when randomorder and/or randompick applied when the 
     corresponding exam was printed, prior to students completing bubblesheets 
     for the version of the exam the student received.
   
   =cut
   
   sub users_order  {
       my ($user,$scancode,$mapurl,$master_seq,$symb_to_resource,$orderedforcode) = @_;
       my @mapresources;
       unless ((ref($master_seq) eq 'ARRAY') && (ref($symb_to_resource) eq 'HASH')) {
           return @mapresources;
       }
       if ($scancode) {
           if ((ref($orderedforcode) eq 'HASH') && (ref($orderedforcode->{$scancode}) eq 'ARRAY')) {
               @mapresources = @{$orderedforcode->{$scancode}};
           } else {
               $env{'form.CODE'} = $scancode;
               my $actual_seq =
                   &Apache::lonprintout::master_seq_to_person_seq($mapurl,
                                                                  $master_seq,
                                                                  $user,$scancode,1);
               if (ref($actual_seq) eq 'ARRAY') {
                   @mapresources = map { $symb_to_resource->{$_}; } @{$actual_seq};
                   if (ref($orderedforcode) eq 'HASH') {
                       if (@mapresources > 0) { 
                           $orderedforcode->{$scancode} = \@mapresources;
                       }
                   }
               }
               delete($env{'form.CODE'});
           }
       } else {
           my $actual_seq =
               &Apache::lonprintout::master_seq_to_person_seq($mapurl,
                                                              $master_seq,
                                                              $user,undef,1);
           if (ref($actual_seq) eq 'ARRAY') {
               @mapresources = 
                   map { $symb_to_resource->{$_}; } @{$actual_seq};
           }
       }
       return @mapresources;
   }
   
 sub grade_student_bubbles {  sub grade_student_bubbles {
     my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts) = @_;      my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts,$bubbles_per_row,
           $randomorder,$randompick,$respnumlookup,$startline) = @_;
       my $uselookup = 0;
       if (($randomorder || $randompick) && (ref($respnumlookup) eq 'HASH') &&
           (ref($startline) eq 'HASH')) {
           $uselookup = 1;
       }
   
     if (ref($resources) eq 'ARRAY') {      if (ref($resources) eq 'ARRAY') {
         my $count = 0;          my $count = 0;
         foreach my $resource (@{$resources}) {          foreach my $resource (@{$resources}) {
Line 7812  sub grade_student_bubbles { Line 8978  sub grade_student_bubbles {
                         'grade_symb'     => $ressymb,                          'grade_symb'     => $ressymb,
                         'CODE'           => $scancode                          'CODE'           => $scancode
                        );                         );
               if ($bubbles_per_row ne '') {
                   $form{'bubbles_per_row'} = $bubbles_per_row;
               }
               if ($env{'form.scantron_lastbubblepoints'} ne '') {
                   $form{'scantron_lastbubblepoints'} = $env{'form.scantron_lastbubblepoints'};
               }
             if (ref($parts) eq 'HASH') {              if (ref($parts) eq 'HASH') {
                 if (ref($parts->{$ressymb}) eq 'ARRAY') {                  if (ref($parts->{$ressymb}) eq 'ARRAY') {
                     foreach my $part (@{$parts->{$ressymb}}) {                      foreach my $part (@{$parts->{$ressymb}}) {
                         $form{'scantron_questnum_start.'.$part} =                          if ($uselookup) {
                             1+$env{'form.scantron.first_bubble_line.'.$count};                              $form{'scantron_questnum_start.'.$part} = $startline->{$count} + 1;
                           } else {
                               $form{'scantron_questnum_start.'.$part} =
                                   1+$env{'form.scantron.first_bubble_line.'.$count};
                           }
                         $count++;                          $count++;
                     }                      }
                 }                  }
Line 7841  sub scantron_upload_scantron_data { Line 9017  sub scantron_upload_scantron_data {
                        ('&nbsp'x2).&mt('(shows course personnel)');                          ('&nbsp'x2).&mt('(shows course personnel)'); 
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');      my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
       &js_escape(\$nofile_alert);
     my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded.");      my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded.");
       &js_escape(\$nocourseid_alert);
     $r->print(&Apache::lonhtmlcommon::scripttag('      $r->print(&Apache::lonhtmlcommon::scripttag('
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
Line 7871  sub scantron_upload_scantron_data { Line 9049  sub scantron_upload_scantron_data {
   
 '));  '));
     $r->print('      $r->print('
 <h3>'.&mt('Send scanned bubblesheet data to a course').'</h3>  <h3>'.&mt('Send bubblesheet data to a course').'</h3>
   
 <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.
Line 7916  sub scantron_upload_scantron_data_save { Line 9094  sub scantron_upload_scantron_data_save {
     }      }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});      my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
     my $uploadedfile;      my $uploadedfile;
     $r->print('<h3>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</h3>');      $r->print('<p>'.&mt('Uploading file to [_1]','"'.$coursedata{'description'}.'"').'</p>');
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
         $r->print(&mt('[_1]Error:[_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.','<span class="LC_error">','</span>','<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));          $r->print(
               &Apache::lonhtmlcommon::confirm_success(
                   &mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',
                           '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1));
     } else {      } else {
         my $result =           my $result = 
             &Apache::lonnet::userfileupload('upfile','','scantron','','','',              &Apache::lonnet::userfileupload('upfile','','scantron','','','',
                                             $env{'form.courseid'},$env{'form.domainid'});                                              $env{'form.courseid'},$env{'form.domainid'});
  if ($result =~ m{^/uploaded/}) {          if ($result =~ m{^/uploaded/}) {
     $r->print(&mt('[_1]Success:[_2] Successfully uploaded [_3] bytes of data into location: [_4]',              $r->print(
                           '<span class="LC_success">','</span>',(length($env{'form.upfile'})-1),                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload successful')).'<br />'.
   '<span class="LC_filename">'.$result.'</span>'));                  &mt('Uploaded [_1] bytes of data into location: [_2]',
                           (length($env{'form.upfile'})-1),
                           '<span class="LC_filename">'.$result.'</span>'));
             ($uploadedfile) = ($result =~ m{/([^/]+)$});              ($uploadedfile) = ($result =~ m{/([^/]+)$});
             $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},              $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
                                                        $env{'form.courseid'},$uploadedfile));                                                         $env{'form.courseid'},$uploadedfile));
  } else {          } else {
     $r->print(&mt('[_1]Error:[_2] An error ([_3]) occurred when attempting to upload the file, [_4]',              $r->print(
                           '<span class="LC_error">','</span>',$result,                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.
                       &mt('An error ([_1]) occurred when attempting to upload the file: [_2]',
                             $result,
   '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));    '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
  }   }
     }      }
Line 7954  sub validate_uploaded_scantron_file { Line 9139  sub validate_uploaded_scantron_file {
     my $output;      my $output;
     if (@lines) {      if (@lines) {
         my (%counts,$max_match_format);          my (%counts,$max_match_format);
         my ($max_match_count,$max_match_pct) = (0,0);          my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);
         my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cname);          my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cname);
         my %idmap = &username_to_idmap($classlist);          my %idmap = &username_to_idmap($classlist);
         foreach my $key (keys(%idmap)) {          foreach my $key (keys(%idmap)) {
Line 7997  sub validate_uploaded_scantron_file { Line 9182  sub validate_uploaded_scantron_file {
                 if (($max_match_format eq '') || ($percent_match > $max_match_pct)) {                  if (($max_match_format eq '') || ($percent_match > $max_match_pct)) {
                     $max_match_pct = $percent_match;                      $max_match_pct = $percent_match;
                     $max_match_format = $key;                      $max_match_format = $key;
                       $found_match_count = $counts{$key}{'found'};
                     $max_match_count = $counts{$key}{'total'};                      $max_match_count = $counts{$key}{'total'};
                 }                  }
             }              }
Line 8015  sub validate_uploaded_scantron_file { Line 9201  sub validate_uploaded_scantron_file {
                 }                  }
             }              }
             my $showpct = sprintf("%.0f",$max_match_pct).'%';              my $showpct = sprintf("%.0f",$max_match_pct).'%';
             $output .= '<br />'.&mt('Comparison of student IDs in the uploaded file with the course roster found matches for [_1] of the [_2] entries in the file (for the format defined for [_3]).','<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs).              $output .= '<br />';
                        '<br />'.&mt('A low percentage of matches results from one of the following:').'<ul>'.              if ($found_match_count == $max_match_count) {
                        '<li>'.&mt('The file was uploaded to the wrong course').'</li>'.                  # 100% matching entries
                        '<li>'.&mt('The data are not in the format expected for the domain: [_1]',                  $output .= &Apache::lonhtmlcommon::confirm_success(
                                   '<i>'.$cdom.'</i>').'</li>'.                       &mt('Comparison of student IDs: [_1] matching ([quant,_2,entry,entries])',
                        '<li>'.&mt('Students did not bubble their IDs, or mis-bubbled them').'</li>'.                              '<b>'.$showpct.'</b>',$found_match_count)).'<br />'.
                        '<li>'.&mt('The course roster is not up to date').'</li>'.                  &mt('Comparison of student IDs in the uploaded file with'.
                        '</ul>';                      ' the course roster found matches for [_1] of the [_2] entries'.
                       ' in the file (for the format defined for [_3]).',
                           '<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs);
               } else {
                   # Not all entries matching? -> Show warning and additional info
                   $output .=
                       &Apache::lonhtmlcommon::confirm_success(
                           &mt('Comparison of student IDs: [_1] matching ([_2]/[quant,_3,entry,entries])',
                                   '<b>'.$showpct.'</b>',$found_match_count,$max_match_count).'<br />'.
                           &mt('Not all entries could be matched!'),1).'<br />'.
                       &mt('Comparison of student IDs in the uploaded file with'.
                           ' the course roster found matches for [_1] of the [_2] entries'.
                           ' in the file (for the format defined for [_3]).',
                               '<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs).
                       '<p class="LC_info">'.
                       &mt('A low percentage of matches results from one of the following:').
                       '</p><ul>'.
                       '<li>'.&mt('The file was uploaded to the wrong course.').'</li>'.
                       '<li>'.&mt('The data is not in the format expected for the domain: [_1]',
                                  '<i>'.$cdom.'</i>').'</li>'.
                       '<li>'.&mt('Students did not bubble their IDs, or mis-bubbled them').'</li>'.
                       '<li>'.&mt('The course roster is not up to date.').'</li>'.
                       '</ul>';
               }
         }          }
     } else {      } else {
         $output = '<span class="LC_warning">'.&mt('Uploaded file contained no data').'</span>';          $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';
     }      }
     return $output;      return $output;
 }  }
Line 8047  sub scantron_download_scantron_data { Line 9256  sub scantron_download_scantron_data {
     if (! &valid_file($file)) {      if (! &valid_file($file)) {
  $r->print('   $r->print('
  <p>   <p>
     '.&mt('The requested file name was invalid.').'      '.&mt('The requested filename was invalid.').'
         </p>          </p>
 ');  ');
  return;   return;
Line 8060  sub scantron_download_scantron_data { Line 9269  sub scantron_download_scantron_data {
     &Apache::lonnet::allowuploaded('/adm/grades',$skipped);      &Apache::lonnet::allowuploaded('/adm/grades',$skipped);
     $r->print('      $r->print('
     <p>      <p>
  '.&mt('[_1]Original[_2] file as uploaded by the scantron office.',   '.&mt('[_1]Original[_2] file as uploaded by the bubblesheet scanning office.',
       '<a href="'.$orig.'">','</a>').'        '<a href="'.$orig.'">','</a>').'
     </p>      </p>
     <p>      <p>
Line 8087  sub checkscantron_results { Line 9296  sub checkscantron_results {
     my %record;      my %record;
     my %scantron_config =      my %scantron_config =
         &Apache::grades::get_scantron_config($env{'form.scantron_format'});          &Apache::grades::get_scantron_config($env{'form.scantron_format'});
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();      my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);      my %idmap=&Apache::grades::username_to_idmap($classlist);
Line 8096  sub checkscantron_results { Line 9306  sub checkscantron_results {
         return '';          return '';
     }      }
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
       my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
           %grader_randomlists_by_symb,%orderedforcode);
       if (ref($map)) { 
           $randomorder=$map->randomorder();
           $randompick=$map->randompick();
       }
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     my (%grader_partids_by_symb,%grader_randomlists_by_symb);      my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,                             \%grader_randomlists_by_symb);      if ($nav_error) {
           $r->print(&navmap_errormsg());
           return '';
       }
       &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                               \%grader_randomlists_by_symb,$bubbles_per_row);
     my ($uname,$udom);      my ($uname,$udom);
     my (%scandata,%lastname,%bylast);      my (%scandata,%lastname,%bylast);
     $r->print('      $r->print('
Line 8108  sub checkscantron_results { Line 9328  sub checkscantron_results {
     my @delayqueue;      my @delayqueue;
     my %completedstudents;      my %completedstudents;
   
     my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);      my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet/Submissions Comparison Status',      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
                                     'Progress of Bubblesheet Data/Submission Records Comparison',$count,  
                                     'inline',undef,'checkscantron');  
     my ($username,$domain,$started);      my ($username,$domain,$started);
     my $nav_error;      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.  
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
     }      }
   
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,'Processing first student');
                                           'Processing first student');  
     my $start=&Time::HiRes::time();      my $start=&Time::HiRes::time();
     my $i=-1;      my $i=-1;
   
Line 8131  sub checkscantron_results { Line 9347  sub checkscantron_results {
         my $line=&Apache::grades::scantron_get_line($scanlines,$scan_data,$i);          my $line=&Apache::grades::scantron_get_line($scanlines,$scan_data,$i);
         if ($line=~/^[\s\cz]*$/) { next; }          if ($line=~/^[\s\cz]*$/) { next; }
         if ($started) {          if ($started) {
             &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,              &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student');
                                                      'last student');  
         }          }
         $started=1;          $started=1;
         my $scan_record=          my $scan_record=
             &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,              &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,
                                                      $scan_data);                                                       $scan_data);
         unless ($uname=&Apache::grades::scantron_find_student($scan_record,$scan_data,          unless ($uname=&scantron_find_student($scan_record,$scan_data,
                                                               \%idmap,$i)) {                                                \%idmap,$i)) {
             &Apache::grades::scantron_add_delay(\@delayqueue,$line,              &Apache::grades::scantron_add_delay(\@delayqueue,$line,
                                 'Unable to find a student that matches',1);                                  'Unable to find a student that matches',1);
             next;              next;
Line 8152  sub checkscantron_results { Line 9367  sub checkscantron_results {
         my $pid = $scan_record->{'scantron.ID'};          my $pid = $scan_record->{'scantron.ID'};
         $lastname{$pid} = $scan_record->{'scantron.LastName'};          $lastname{$pid} = $scan_record->{'scantron.LastName'};
         push(@{$bylast{$lastname{$pid}}},$pid);          push(@{$bylast{$lastname{$pid}}},$pid);
           my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
           my $user = $uname.':'.$usec;
           ($username,$domain)=split(/:/,$uname);
   
           my $scancode;
           if ((exists($scan_record->{'scantron.CODE'})) &&
               (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) {
               $scancode = $scan_record->{'scantron.CODE'};
           } else {
               $scancode = '';
           }
   
           my @mapresources = @resources;
         my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};          my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};
           my %respnumlookup=();
           my %startline=();
           if ($randomorder || $randompick) {
               @mapresources =
                   &users_order($user,$scancode,$sequence,\@master_seq,\%symb_to_resource,
                                \%orderedforcode);
               my $total = &get_respnum_lookups($sequence,$scan_data,\%idmap,$line,
                                                $scan_record,\@master_seq,\%symb_to_resource,
                                                \%grader_partids_by_symb,\%orderedforcode,
                                                \%respnumlookup,\%startline);
               if ($randompick && $total) {
                   $lastpos = $total*$scantron_config{'Qlength'};
               }
           }
         $scandata{$pid} = substr($line,$scantron_config{'Qstart'}-1,$lastpos);          $scandata{$pid} = substr($line,$scantron_config{'Qstart'}-1,$lastpos);
         chomp($scandata{$pid});          chomp($scandata{$pid});
         $scandata{$pid} =~ s/\r$//;          $scandata{$pid} =~ s/\r$//;
         ($username,$domain)=split(/:/,$uname);  
         my $counter = -1;          my $counter = -1;
         foreach my $resource (@resources) {          foreach my $resource (@mapresources) {
             my $parts;              my $parts;
             my $ressymb = $resource->symb();              my $ressymb = $resource->symb();
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                   my $currcode;
                   if (exists($grader_randomlists_by_symb{$ressymb})) {
                       $currcode = $scancode;
                   }
                 (my $analysis,$parts) =                  (my $analysis,$parts) =
                     &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain);                      &scantron_partids_tograde($resource,$env{'request.course.id'},
                                                 $username,$domain,undef,
                                                 $bubbles_per_row,$currcode);
             } else {              } else {
                 $parts = $grader_partids_by_symb{$ressymb};                  $parts = $grader_partids_by_symb{$ressymb};
             }              }
             ($counter,my $recording) =              ($counter,my $recording) =
                 &verify_scantron_grading($resource,$domain,$username,$cid,$counter,                  &verify_scantron_grading($resource,$domain,$username,$cid,$counter,
                                          $scandata{$pid},$parts,                                           $scandata{$pid},$parts,
                                          \%scantron_config,\%lettdig,$numletts);                                           \%scantron_config,\%lettdig,$numletts,
                                            $randomorder,$randompick,
                                            \%respnumlookup,\%startline);
             $record{$pid} .= $recording;              $record{$pid} .= $recording;
         }          }
     }      }
Line 8194  sub checkscantron_results { Line 9444  sub checkscantron_results {
 '<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".  '<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".  '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".  '<tr class="'.$css_class.'">'."\n".
 '<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n";  '<td>'.&mt('Submissions').'</td><td>'.$showrecord.'</td></tr>'."\n";
                     $passed ++;                      $passed ++;
                 } else {                  } else {
                     my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row';                      my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row';
                     $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".                      $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".  '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".  '<tr class="'.$css_class.'">'."\n".
 '<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".  '<td>'.&mt('Submissions').'</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".
 '</tr>'."\n";  '</tr>'."\n";
                     $failed ++;                      $failed ++;
                 }                  }
Line 8209  sub checkscantron_results { Line 9459  sub checkscantron_results {
             }              }
         }          }
     }      }
     $r->print('<p>'.&mt('Comparison of bubblesheet 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(
     $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>');          '<p>'
          .&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([quant,_4,bubblesheet line] per student).',
               '<b>',
               $numstudents,
               '</b>',
               $env{'form.scantron_maxbubble'})
          .'</p>'
       );
       $r->print('<p>'
                .&mt('Exact matches for [_1][quant,_2,student][_3].','<b>',$passed,'</b>')
                .'<br />'
                .&mt('Discrepancies detected for [_1][quant,_2,student][_3].','<b>',$failed,'</b>')
                .'</p>'
       );
     if ($passed) {      if ($passed) {
         $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');          $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".          $r->print(&Apache::loncommon::start_data_table()."\n".
Line 8236  sub checkscantron_results { Line 9499  sub checkscantron_results {
   
 sub verify_scantron_grading {  sub verify_scantron_grading {
     my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,      my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,
         $scantron_config,$lettdig,$numletts) = @_;          $scantron_config,$lettdig,$numletts,$randomorder,$randompick,
           $respnumlookup,$startline) = @_;
     my ($record,%expected,%startpos);      my ($record,%expected,%startpos);
     return ($counter,$record) if (!ref($resource));      return ($counter,$record) if (!ref($resource));
     return ($counter,$record) if (!$resource->is_problem());      return ($counter,$record) if (!$resource->is_problem());
Line 8245  sub verify_scantron_grading { Line 9509  sub verify_scantron_grading {
     foreach my $part_id (@{$partids}) {      foreach my $part_id (@{$partids}) {
         $counter ++;          $counter ++;
         $expected{$part_id} = 0;          $expected{$part_id} = 0;
         if ($env{"form.scantron.sub_bubblelines.$counter"}) {          my $respnum = $counter;
             my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"});          if ($randomorder || $randompick) {
               $respnum = $respnumlookup->{$counter};
               $startpos{$part_id} = $startline->{$counter} + 1;
           } else {
               $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};
           }
           if ($env{"form.scantron.sub_bubblelines.$respnum"}) {
               my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$respnum"});
             foreach my $item (@sub_lines) {              foreach my $item (@sub_lines) {
                 $expected{$part_id} += $item;                  $expected{$part_id} += $item;
             }              }
         } else {          } else {
             $expected{$part_id} = $env{"form.scantron.bubblelines.$counter"};              $expected{$part_id} = $env{"form.scantron.bubblelines.$respnum"};
         }          }
         $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};  
     }      }
     if ($symb) {      if ($symb) {
         my %recorded;          my %recorded;
Line 8349  sub verify_scantron_grading { Line 9619  sub verify_scantron_grading {
     return ($counter,$record);      return ($counter,$record);
 }  }
   
 sub letter_to_digits {   sub letter_to_digits {
     my %lettdig = (      my %lettdig = (
                     A => 1,                      A => 1,
                     B => 2,                      B => 2,
Line 8376  sub letter_to_digits { Line 9646  sub letter_to_digits {
   
 sub href_symb_cmd {  sub href_symb_cmd {
     my ($symb,$cmd)=@_;      my ($symb,$cmd)=@_;
     return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&command='.$cmd;      return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.$cmd;
 }  }
   
 sub grading_menu {  sub grading_menu {
Line 8384  sub grading_menu { Line 9654  sub grading_menu {
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),      my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),
                   'command'=>'individual',                    'command'=>'individual');
                   'gradingMenu'=>1,  
                   'showgrading'=>"yes");  
           
     my $url1a = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url1a = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
   
Line 8399  sub grading_menu { Line 9667  sub grading_menu {
     $fields{'command'}='all_for_one';      $fields{'command'}='all_for_one';
     my $url1d=&Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url1d=&Apache::lonhtmlcommon::build_url('grades/',\%fields);
   
       $fields{'command'}='downloadfilesselect';
       my $url1e=&Apache::lonhtmlcommon::build_url('grades/',\%fields);
   
     $fields{'command'} = 'csvform';      $fields{'command'} = 'csvform';
     my $url2 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url2 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
           
Line 8416  sub grading_menu { Line 9687  sub grading_menu {
                         { linktext => 'Select individual students to grade',                          { linktext => 'Select individual students to grade',
                     url => $url1a,                      url => $url1a,
                     permission => 'F',                      permission => 'F',
                     icon => 'edit-find-replace.png',                      icon => 'grade_students.png',
                     linktitle => 'Grade current resource for a selection of students.'                      linktitle => 'Grade current resource for a selection of students.'
                         },                           }, 
                         {       linktext => 'Grade ungraded submissions.',                          {       linktext => 'Grade ungraded submissions.',
                                 url => $url1b,                                  url => $url1b,
                                 permission => 'F',                                  permission => 'F',
                                 icon => 'edit-find-replace.png',                                  icon => 'ungrade_sub.png',
                                 linktitle => 'Grade all submissions that have not been graded yet.'                                  linktitle => 'Grade all submissions that have not been graded yet.'
                         },                          },
   
                         {       linktext => 'Grading table',                          {       linktext => 'Grading table',
                                 url => $url1c,                                  url => $url1c,
                                 permission => 'F',                                  permission => 'F',
                                 icon => 'edit-find-replace.png',                                  icon => 'grading_table.png',
                                 linktitle => 'Grade current resource for all students.'                                  linktitle => 'Grade current resource for all students.'
                         },                          },
                         {       linktext => 'Grade page/folder for one student',                          {       linktext => 'Grade page/folder for one student',
                                 url => $url1d,                                  url => $url1d,
                                 permission => 'F',                                  permission => 'F',
                                 icon => 'edit-find-replace.png',                                  icon => 'grade_PageFolder.png',
                                 linktitle => 'Grade all resources in current page/sequence/folder for one student.'                                  linktitle => 'Grade all resources in current page/sequence/folder for one student.'
                           },
                           {       linktext => 'Download submissions',
                                   url => $url1e,
                                   permission => 'F',
                                   icon => 'download_sub.png',
                                   linktitle => 'Download all students submissions.'
                         }]},                          }]},
                          { categorytitle=>'Automated Grading',                           { categorytitle=>'Automated Grading',
                items =>[                 items =>[
Line 8456  sub grading_menu { Line 9733  sub grading_menu {
                    { linktext => 'Grade/Manage/Review Bubblesheets',                     { linktext => 'Grade/Manage/Review Bubblesheets',
                     url => $url4,                      url => $url4,
                     permission => 'F',                      permission => 'F',
                     icon => 'stat.png',                      icon => 'bubblesheet.png',
                     linktitle => 'Grade scantron exams, upload/download scantron data files, and review previously graded scantron exams.'                      linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'
                    },                     },
                             {   linktext => 'Verify Receipt Number',                              {   linktext => 'Verify Receipt Number',
                                 url => $url5,                                  url => $url5,
                                 permission => 'F',                                  permission => 'F',
                                 icon => 'edit-find-replace.png',                                  icon => 'receipt_number.png',
                                 linktitle => 'Verify a system-generated receipt number for correct problem solution.'                                  linktitle => 'Verify a system-generated receipt number for correct problem solution.'
                             }                              }
   
Line 8473  sub grading_menu { Line 9750  sub grading_menu {
     my $Str;      my $Str;
     $Str .= '<form method="post" action="" name="gradingMenu">';      $Str .= '<form method="post" action="" name="gradingMenu">';
     $Str .= '<input type="hidden" name="command" value="" />'.      $Str .= '<input type="hidden" name="command" value="" />'.
     '<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="gradingMenu" value="1" />'."\n".  
  '<input type="hidden" name="showgrading" value="yes" />'."\n";  
   
     $Str .= &Apache::lonhtmlcommon::generate_menu(@menu);      $Str .= &Apache::lonhtmlcommon::generate_menu(@menu);
     return $Str;          return $Str;    
Line 8494  sub submit_options_sequence { Line 9769  sub submit_options_sequence {
     my $result;      my $result;
   
     $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="gradingMenu" value="1" />'."\n".      $result.=&selectfield(0).
         '<input type="hidden" name="showgrading" value="yes" />'."\n";  
   
     $result.='  
 <h2>  
   '.&mt('Grade page/folder for one student').'  
 </h2>'.  
             &selectfield(0).  
             '<input type="hidden" name="command" value="pickStudentPage" />              '<input type="hidden" name="command" value="pickStudentPage" />
             <div>              <div>
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
Line 8516  sub submit_options_table { Line 9784  sub submit_options_table {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     &commonJSfunctions($request);      &commonJSfunctions($request);
       my $is_tool = ($symb =~ /ext\.tool$/);
     my $result;      my $result;
   
     $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="gradingMenu" value="1" />'."\n".  
         '<input type="hidden" name="showgrading" value="yes" />'."\n";  
   
     $result.='      $result.=&selectfield(1,$is_tool).
 <h2>  
   '.&mt('Grading table').'  
 </h2>'.  
             &selectfield(0).  
             '<input type="hidden" name="command" value="viewgrades" />              '<input type="hidden" name="command" value="viewgrades" />
             <div>              <div>
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
Line 8537  sub submit_options_table { Line 9800  sub submit_options_table {
     return $result;      return $result;
 }  }
   
   sub submit_options_download {
       my ($request,$symb) = @_;
       if (!$symb) {return '';}
   
       my $is_tool = ($symb =~ /ext\.tool$/);
       &commonJSfunctions($request);
   
       my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
           '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
       $result.='
   <h2>
     '.&mt('Select Students for Which to Download Submissions').'
   </h2>'.&selectfield(1,$is_tool).'
                   <input type="hidden" name="command" value="downloadfileslink" /> 
                 <input type="submit" value="'.&mt('Next').' &rarr;" />
               </div>
             </div>
   
   
     </form>';
       return $result;
   }
   
 #--- Displays the submissions first page -------  #--- Displays the submissions first page -------
 sub submit_options {  sub submit_options {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
       my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my $result;      my $result;
   
     $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="gradingMenu" value="1" />'."\n".      $result.=&selectfield(1,$is_tool).'
  '<input type="hidden" name="showgrading" value="yes" />'."\n";  
   
     $result.='  
 <h2>  
   '.&mt('Select individual students to grade').'  
 </h2>'.&selectfield(1).'  
                 <input type="hidden" name="command" value="submission" />                   <input type="hidden" name="command" value="submission" /> 
       <input type="submit" value="'.&mt('Next').' &rarr;" />        <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
Line 8567  sub submit_options { Line 9846  sub submit_options {
 }  }
   
 sub selectfield {  sub selectfield {
    my ($full)=@_;     my ($full,$is_tool)=@_;
      my %options;
      if ($is_tool) {
          %options =
              (&transtatus_options,
               'select_form_order' => ['yes','incorrect','all']);
      } else {
          %options = 
              (&substatus_options,
               'select_form_order' => ['yes','queued','graded','incorrect','all']);
      }
    my $result='<div class="LC_columnSection">     my $result='<div class="LC_columnSection">
       
     <fieldset>      <fieldset>
Line 8591  sub selectfield { Line 9880  sub selectfield {
       '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'        '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'
     </fieldset>';      </fieldset>';
     if ($full) {      if ($full) {
        $result.='          my $heading = &mt('Submission Status');
           if ($is_tool) {
               $heading = &mt('Transaction Status');
           }
           $result.='
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Submission Status').'          '.$heading.'
       </legend>'.        </legend>'.
        &Apache::loncommon::select_form('all','submitonly',         &Apache::loncommon::select_form('all','submitonly',\%options).
           (&Apache::lonlocal::texthash(  
              'yes'       => 'with submissions',  
              'queued'    => 'in grading queue',  
              'graded'    => 'with ungraded submissions',  
              'incorrect' => 'with incorrect submissions',  
              'all'       => 'with any status'),  
              'select_form_order' => ['yes','queued','graded','incorrect','all'])).  
    '</fieldset>';     '</fieldset>';
     }      }
     $result.='</div><br />';      $result.='</div><br />';
     return $result;      return $result;
 }  }
   
   sub substatus_options {
       return &Apache::lonlocal::texthash(
                                         'yes'       => 'with submissions',
                                         'queued'    => 'in grading queue',
                                         'graded'    => 'with ungraded submissions',
                                         'incorrect' => 'with incorrect submissions',
                                         'all'       => 'with any status',
                                         );
   }
   
   sub transtatus_options {
       return &Apache::lonlocal::texthash(
                                          'yes'       => 'with score transactions',
                                          'incorrect' => 'with less than full credit',
                                          'all'       => 'with any status',
                                         );
   }
   
 sub reset_perm {  sub reset_perm {
     undef(%perm);      undef(%perm);
 }  }
Line 8632  sub init_perm { Line 9936  sub init_perm {
     }      }
 }  }
   
   sub init_old_essays {
       my ($symb,$apath,$adom,$aname) = @_;
       if ($symb ne '') {
           my %essays = &Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
           if (keys(%essays) > 0) {
               $old_essays{$symb} = \%essays;
           }
       }
       return;
   }
   
   sub reset_old_essays {
       undef(%old_essays);
   }
   
 sub gather_clicker_ids {  sub gather_clicker_ids {
     my %clicker_ids;      my %clicker_ids;
   
Line 8697  sub process_clicker { Line 10016  sub process_clicker {
     my ($r,$symb)=@_;      my ($r,$symb)=@_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $result=&checkforfile_js();      my $result=&checkforfile_js();
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";      $result.=&Apache::loncommon::start_data_table().
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";               &Apache::loncommon::start_data_table_header_row().
     $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource.').               '<th>'.&mt('Specify a file containing clicker information and set grading options.').'</th>'.
         '</b></td></tr>'."\n";               &Apache::loncommon::end_data_table_header_row().
     $result.='<tr bgcolor="#ffffe6"><td>'."\n";               &Apache::loncommon::start_data_table_row()."<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();
     &Apache::loncommon::restore_course_settings('grades_clicker',      &Apache::loncommon::restore_course_settings('grades_clicker',
Line 8718  sub process_clicker { Line 10037  sub process_clicker {
        }         }
     }      }
   
     my $upload=&mt("Upload File");      my $upload=&mt("Evaluate File");
     my $type=&mt("Type");      my $type=&mt("Type");
     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");
Line 8728  sub process_clicker { Line 10047  sub process_clicker {
     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',
    ('iclicker' => 'i>clicker',     {'iclicker' => 'i>clicker',
                                                     'interwrite' => 'interwrite PRS'));                                                      'interwrite' => 'interwrite PRS',
                                                       'turning' => 'Turning Technologies'});
     $symb = &Apache::lonenc::check_encrypt($symb);      $symb = &Apache::lonenc::check_encrypt($symb);
     $result.= &Apache::lonhtmlcommon::scripttag(<<ENDUPFORM);      $result.= &Apache::lonhtmlcommon::scripttag(<<ENDUPFORM);
 function sanitycheck() {  function sanitycheck() {
Line 8776  ENDUPFORM Line 10096  ENDUPFORM
 <input type="hidden" name="command" value="processclickerfile" />  <input type="hidden" name="command" value="processclickerfile" />
 <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><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onclick="sanitycheck()" />$attendance </label>  ENDUPFORM
       $result.='</td>'.&Apache::loncommon::end_data_table_row().
                        &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDGRADINGFORM);
         <label><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onclick="sanitycheck()" />$attendance </label>
 <br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onclick="sanitycheck()" />$personnel</label>  <br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onclick="sanitycheck()" />$personnel</label>
 <br /><label><input type="radio" name="gradingmechanism" value="specific"$checked{'specific'} onclick="sanitycheck()" />$specific </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" />
Line 8784  ENDUPFORM Line 10107  ENDUPFORM
 <br />&nbsp;&nbsp;&nbsp;  <br />&nbsp;&nbsp;&nbsp;
 <input type="text" name="givenanswer" size="50" />  <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>  ENDGRADINGFORM
            $result.='</td>'.&Apache::loncommon::end_data_table_row().
                        &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDPERCFORM);
         <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>
 <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />  <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 </form>'  </form>'
 ENDUPFORM  ENDPERCFORM
     $result.='</td></tr></table>'."\n".      $result.='</td>'.
              '</td></tr></table><br /><br />'."\n";               &Apache::loncommon::end_data_table_row().
                &Apache::loncommon::end_data_table();
     return $result;      return $result;
 }  }
   
Line 8814  sub process_clicker_file { Line 10141  sub process_clicker_file {
     if ($env{'form.gradingmechanism'} eq 'given') {      if ($env{'form.gradingmechanism'} eq 'given') {
         $env{'form.givenanswer'}=~s/^\s*//gs;          $env{'form.givenanswer'}=~s/^\s*//gs;
         $env{'form.givenanswer'}=~s/\s*$//gs;          $env{'form.givenanswer'}=~s/\s*$//gs;
         $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-]+/\,/g;          $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-\+]+/\,/g;
         $env{'form.givenanswer'}=uc($env{'form.givenanswer'});          $env{'form.givenanswer'}=uc($env{'form.givenanswer'});
         my @answers=split(/\,/,$env{'form.givenanswer'});          my @answers=split(/\,/,$env{'form.givenanswer'});
         $foundgiven=$#answers+1;          $foundgiven=$#answers+1;
Line 8853  sub process_clicker_file { Line 10180  sub process_clicker_file {
     $number++;      $number++;
  }   }
         $result.="</p>\n";          $result.="</p>\n";
  if ($number==0) {          if ($number==0) {
     $result.='<span class="LC_error">'.&mt('No IDs found to determine correct answer').'</span>';              $result .=
     return $result;                   &Apache::lonhtmlcommon::confirm_success(
  }                       &mt('No IDs found to determine correct answer'),1);
               return $result;
           }
     }      }
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
         $result.=&mt('[_1] Error: [_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.',          $result .=
      '<span class="LC_error">',              &Apache::lonhtmlcommon::confirm_success(
      '</span>',                  &mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',
      '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>');                          '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1);
         return $result;          return $result;
     }      }
   
Line 8870  sub process_clicker_file { Line 10199  sub process_clicker_file {
   
     $result.=&Apache::loncommon::studentbrowser_javascript();      $result.=&Apache::loncommon::studentbrowser_javascript();
     $symb = &Apache::lonenc::check_encrypt($symb);      $symb = &Apache::lonenc::check_encrypt($symb);
     my $heading=&mt('Scanning clicker file');      $result.=&Apache::loncommon::start_data_table().
     $result.=(<<ENDHEADER);               &Apache::loncommon::start_data_table_header_row().
 <br /><table width="100%" border="0"><tr><td bgcolor="#777777">               '<th>'.&mt('Evaluate clicker file').'</th>'.
 <table width="100%" border="0"><tr bgcolor="#e6ffff"><td>               &Apache::loncommon::end_data_table_header_row().
 <b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>               &Apache::loncommon::start_data_table_row().(<<ENDHEADER);
   <td>
 <form method="post" action="/adm/grades" name="clickeranalysis">  <form method="post" action="/adm/grades" name="clickeranalysis">
 <input type="hidden" name="symb" value="$symb" />  <input type="hidden" name="symb" value="$symb" />
 <input type="hidden" name="command" value="assignclickergrades" />  <input type="hidden" name="command" value="assignclickergrades" />
Line 8895  ENDHEADER Line 10225  ENDHEADER
     if ($env{'form.upfiletype'} eq 'interwrite') {      if ($env{'form.upfiletype'} eq 'interwrite') {
         ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
     }      }
       if ($env{'form.upfiletype'} eq 'turning') {
           ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);
       }
     $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.      $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.
              '<input type="hidden" name="number" value="'.$number.'" />'.               '<input type="hidden" name="number" value="'.$number.'" />'.
              &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',               &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',
Line 8922  ENDHEADER Line 10255  ENDHEADER
        } elsif ($clicker_ids{$id}) {         } elsif ($clicker_ids{$id}) {
           if ($clicker_ids{$id}=~/\,/) {            if ($clicker_ids{$id}=~/\,/) {
 # More than one user with the same clicker!  # More than one user with the same clicker!
              $result.="\n<hr />".&mt('Clicker registered more than once').": <tt>".$id."</tt><br />";               $result.="</td>".&Apache::loncommon::end_data_table_row().
                              &Apache::loncommon::start_data_table_row()."<td>".
                          &mt('Clicker registered more than once').": <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}.'" />'.
                            "<select name='multi".$id."'>";                             "<select name='multi".$id."'>";
              foreach my $reguser (sort(split(/\,/,$clicker_ids{$id}))) {               foreach my $reguser (sort(split(/\,/,$clicker_ids{$id}))) {
Line 8936  ENDHEADER Line 10271  ENDHEADER
              $student_count++;               $student_count++;
           }            }
        } else {         } else {
           $result.="\n<hr />".&mt('Unregistered Clicker')." <tt>".$id."</tt><br />";            $result.="</td>".&Apache::loncommon::end_data_table_row().
                              &Apache::loncommon::start_data_table_row()."<td>".
                       &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}.'" />'.
                    "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".                     "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                    "\n".&mt("Domain").": ".                     "\n".&mt("Domain").": ".
                    &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.                     &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
                    &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id);                     &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);
           $unknown_count++;            $unknown_count++;
        }         }
     }      }
Line 8949  ENDHEADER Line 10286  ENDHEADER
              &mt('Found [_1] registered and [_2] unregistered clickers.',$student_count,$unknown_count);               &mt('Found [_1] registered and [_2] unregistered clickers.',$student_count,$unknown_count);
     if (($env{'form.gradingmechanism'} ne 'attendance') && ($env{'form.gradingmechanism'} ne 'given')) {      if (($env{'form.gradingmechanism'} ne 'attendance') && ($env{'form.gradingmechanism'} ne 'given')) {
        if ($correct_count==0) {         if ($correct_count==0) {
           $errormsg.="Found no correct answers answers for grading!";            $errormsg.="Found no correct answers for grading!";
        } elsif ($correct_count>1) {         } elsif ($correct_count>1) {
           $result.='<br /><span class="LC_warning">'.&mt("Found [_1] entries for grading!",$correct_count).'</span>';            $result.='<br /><span class="LC_warning">'.&mt("Found [_1] entries for grading!",$correct_count).'</span>';
        }         }
Line 8962  ENDHEADER Line 10299  ENDHEADER
     } else {      } else {
        $result.='<br /><input type="submit" name="finalize" value="'.&mt('Finalize Grading').'" />';         $result.='<br /><input type="submit" name="finalize" value="'.&mt('Finalize Grading').'" />';
     }      }
     $result.='</form></td></tr></table>'."\n".      $result.='</form></td>'.
              '</td></tr></table><br /><br />'."\n";               &Apache::loncommon::end_data_table_row().
                &Apache::loncommon::end_data_table();
     return $result;      return $result;
 }  }
   
Line 8986  sub iclicker_eval { Line 10324  sub iclicker_eval {
     $id=~s/^[\#0]+//;      $id=~s/^[\#0]+//;
     for (my $i=0;$i<$number;$i++) {      for (my $i=0;$i<$number;$i++) {
  my $idx=3+$i*6;   my $idx=3+$i*6;
                   $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+]+//g;
  push(@idresponses,$entries[$idx]);   push(@idresponses,$entries[$idx]);
     }      }
     $$responses{$id}=join(',',@idresponses);      $$responses{$id}=join(',',@idresponses);
Line 9025  sub interwrite_eval { Line 10364  sub interwrite_eval {
     return ($errormsg,$number);      return ($errormsg,$number);
 }  }
   
   sub turning_eval {
       my ($questiontitles,$responses)=@_;
       my $number=0;
       my $errormsg='';
       foreach my $line (split(/[\n\r]/,$env{'form.upfile'})) {
           my %components=&Apache::loncommon::record_sep($line);
           my @entries=map {$components{$_}} (sort(keys(%components)));
           if ($#entries>$number) { $number=$#entries; }
           my $id=$entries[0];
           my @idresponses;
           $id=~s/^[\#0]+//;
           unless ($id) { next; }
           for (my $idx=1;$idx<=$#entries;$idx++) {
               $entries[$idx]=~s/\,/\;/g;
               $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+\;]+//g;
               push(@idresponses,$entries[$idx]);
           }
           $$responses{$id}=join(',',@idresponses);
       }
       for (my $i=1; $i<=$number; $i++) {
           $$questiontitles[$i]=&mt('Question [_1]',$i);
       }
       return ($errormsg,$number);
   }
   
   
 sub assign_clicker_grades {  sub assign_clicker_grades {
     my ($r,$symb)=@_;      my ($r,$symb)=@_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
Line 9037  sub assign_clicker_grades { Line 10402  sub assign_clicker_grades {
 # FIXME: This should probably look for the first handgradeable part  # FIXME: This should probably look for the first handgradeable part
     my $part=$$partlist[0];      my $part=$$partlist[0];
 # Start screen output  # Start screen output
     my $result='';      my $result=&Apache::loncommon::start_data_table().
                &Apache::loncommon::start_data_table_header_row().
     my $heading=&mt('Assigning grades based on clicker file');               '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
     $result.=(<<ENDHEADER);               &Apache::loncommon::end_data_table_header_row().
 <br /><table width="100%" border="0"><tr><td bgcolor="#777777">               &Apache::loncommon::start_data_table_row().'<td>';
 <table width="100%" border="0"><tr bgcolor="#e6ffff"><td>  
 <b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>  
 ENDHEADER  
 # Get correct result  # Get correct result
 # FIXME: Possibly need delimiter other than ":"  # FIXME: Possibly need delimiter other than ":"
     my @correct=();      my @correct=();
Line 9060  ENDHEADER Line 10422  ENDHEADER
                     $result.='<br /><span class="LC_warning">'.                      $result.='<br /><span class="LC_warning">'.
                              &mt('More than one correct result given for question "[_1]": [_2] versus [_3].',                               &mt('More than one correct result given for question "[_1]": [_2] versus [_3].',
                                  $env{'form.question:'.$i},$correct[$i],$input[$i]).'</span>';                                   $env{'form.question:'.$i},$correct[$i],$input[$i]).'</span>';
                  } elsif ($input[$i]) {                   } elsif (($input[$i]) || ($input[$i] eq '0')) {
                     $correct[$i]=$input[$i];                      $correct[$i]=$input[$i];
                  }                   }
              }               }
           }            }
        }         }
        for (my $i=0;$i<$number;$i++) {         for (my $i=0;$i<$number;$i++) {
           if (!$correct[$i]) {            if ((!$correct[$i]) && ($correct[$i] ne '0')) {
              $result.='<br /><span class="LC_error">'.               $result.='<br /><span class="LC_error">'.
                       &mt('No correct result given for question "[_1]"!',                        &mt('No correct result given for question "[_1]"!',
                           $env{'form.question:'.$i}).'</span>';                            $env{'form.question:'.$i}).'</span>';
           }            }
        }         }
        $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ($_?$_:'-') } @correct));         $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ((($_) || ($_ eq '0'))?$_:'-') } @correct));
     }      }
 # Start grading  # Start grading
     my $pcorrect=$env{'form.pcorrect'};      my $pcorrect=$env{'form.pcorrect'};
     my $pincorrect=$env{'form.pincorrect'};      my $pincorrect=$env{'form.pincorrect'};
     my $storecount=0;      my $storecount=0;
       my %users=();
     foreach my $key (keys(%env)) {      foreach my $key (keys(%env)) {
        my $user='';         my $user='';
        if ($key=~/^form\.student\:(.*)$/) {         if ($key=~/^form\.student\:(.*)$/) {
Line 9092  ENDHEADER Line 10455  ENDHEADER
              $user=$env{'form.multi'.$id};               $user=$env{'form.multi'.$id};
           }            }
        }         }
        if ($user) {          if ($user) {
             if ($users{$user}) {
                $result.='<br /><span class="LC_warning">'.
                         &mt('More than one entry found for [_1]!','<tt>'.$user.'</tt>').
                         '</span><br />';
             }
             $users{$user}=1; 
           my @answer=split(/\,/,$env{$key});            my @answer=split(/\,/,$env{$key});
           my $sum=0;            my $sum=0;
           my $realnumber=$number;            my $realnumber=$number;
           for (my $i=0;$i<$number;$i++) {            for (my $i=0;$i<$number;$i++) {
              if  ($correct[$i] eq '-') {               if  ($correct[$i] eq '-') {
                 $realnumber--;                  $realnumber--;
              } elsif ($answer[$i]) {               } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/))  {
                 if ($gradingmechanism eq 'attendance') {                  if ($gradingmechanism eq 'attendance') {
                    $sum+=$pcorrect;                     $sum+=$pcorrect;
                 } elsif ($correct[$i] eq '*') {                  } elsif ($correct[$i] eq '*') {
                    $sum+=$pcorrect;                     $sum+=$pcorrect;
                 } else {                  } else {
                    if ($answer[$i] eq $correct[$i]) {  # We actually grade if correct or not
                       $sum+=$pcorrect;                     my $increment=$pincorrect;
                    } else {  # Special case: numerical answer "0"
                       $sum+=$pincorrect;                     if ($correct[$i] eq '0') {
                         if ($answer[$i]=~/^[0\.]+$/) {
                            $increment=$pcorrect;
                         }
   # General numerical answer, both evaluate to something non-zero
                      } elsif ((1.0*$correct[$i]!=0) && (1.0*$answer[$i]!=0)) {
                         if (1.0*$correct[$i]==1.0*$answer[$i]) {
                            $increment=$pcorrect;
                         }
   # Must be just alphanumeric
                      } elsif ($answer[$i] eq $correct[$i]) {
                         $increment=$pcorrect;
                    }                     }
                      $sum+=$increment;
                 }                  }
              }               }
           }            }
Line 9132  ENDHEADER Line 10513  ENDHEADER
     }      }
 # We are done  # We are done
     $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).      $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).
              '</td></tr></table>'."\n".               '</td>'.
              '</td></tr></table><br /><br />'."\n";               &Apache::loncommon::end_data_table_row().
                &Apache::loncommon::end_data_table();
     return $result;      return $result;
 }  }
   
Line 9145  sub navmap_errormsg { Line 10527  sub navmap_errormsg {
 }  }
   
 sub startpage {  sub startpage {
     my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag) = @_;      my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js) = @_;
     unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});      if ($nomenu) {
     $r->print(&Apache::loncommon::start_page('Grading',undef,          $r->print(&Apache::loncommon::start_page("Student's Version",$js,{'only_body' => '1'}));
                                           {'bread_crumbs' => $crumbs}));      } else {
           unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
           $r->print(&Apache::loncommon::start_page('Grading',$js,
                                                    {'bread_crumbs' => $crumbs}));
           &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
       }
     unless ($nodisplayflag) {      unless ($nodisplayflag) {
        $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag));         $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp));
     }      }
 }  }
   
   sub select_problem {
       my ($r)=@_;
       $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');
       $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,undef,undef,1));
       $r->print('<input type="hidden" name="command" value="gradingmenu" />');
       $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');
   }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
     &reset_caches();      &reset_caches();
     if ($env{'browser.mathml'}) {      if ($request->header_only) {
  &Apache::loncommon::content_type($request,'text/xml');          &Apache::loncommon::content_type($request,'text/html');
     } else {          $request->send_http_header;
  &Apache::loncommon::content_type($request,'text/html');          return OK;
     }      }
     $request->send_http_header;  
     return '' if $request->header_only;  
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
   
 # see what command we need to execute  # see what command we need to execute
Line 9171  sub handler { Line 10564  sub handler {
     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];
   
       &init_perm();
       if (!$env{'request.course.id'}) {
           unless ((&Apache::lonnet::allowed('usc',$env{'request.role.domain'})) &&
                   ($command =~ /^scantronupload/)) {
               # Not in a course.
               $env{'user.error.msg'}="/adm/grades::vgr:0:0:Cannot display grades page outside course context";
               return HTTP_NOT_ACCEPTABLE;
           }
       } elsif (!%perm) {
           $request->internal_redirect('/adm/quickgrades');
           return OK;
       }
       &Apache::loncommon::content_type($request,'text/html');
       $request->send_http_header;
   
     if ($#commands > 0) {      if ($#commands > 0) {
  &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));   &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
     }      }
Line 9182  sub handler { Line 10590  sub handler {
        (my $url=$env{'form.url'}) =~ s-^https*://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;         (my $url=$env{'form.url'}) =~ s-^https*://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
        $symb=&Apache::lonnet::symbread($url);         $symb=&Apache::lonnet::symbread($url);
     }      }
     &Apache::lonenc::check_decrypt(\$symb);                                   &Apache::lonenc::check_decrypt(\$symb);
   
     $ssi_error = 0;      $ssi_error = 0;
     if ($symb eq '' && $command eq '') {      if (($symb eq '' || $command eq '') && ($env{'request.course.id'})) {
 #  #
 # Not called from a resource  # Not called from a resource, but inside a course
 #      #    
           &startpage($request,undef,[],1,1);
           &select_problem($request);
     } else {      } else {
  &init_perm();  
  if ($command eq 'submission' && $perm{'vgr'}) {   if ($command eq 'submission' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}]);              my ($stuvcurrent,$stuvdisp,$versionform,$js);
               if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
                   ($stuvcurrent,$stuvdisp,$versionform,$js) =
                       &choose_task_version_form($symb,$env{'form.student'},
                                                 $env{'form.userdom'});
               }
               &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js);
               if ($versionform) {
                   $request->print($versionform);
               }
               $request->print('<br clear="all" />');
     ($env{'form.student'} eq '' ? &listStudents($request,$symb) : &submission($request,0,0,$symb));      ($env{'form.student'} eq '' ? &listStudents($request,$symb) : &submission($request,0,0,$symb));
           } elsif ($command eq 'versionsub' && $perm{'vgr'}) {
               my ($stuvcurrent,$stuvdisp,$versionform,$js) =
                   &choose_task_version_form($symb,$env{'form.student'},
                                             $env{'form.userdom'},
                                             $env{'form.inhibitmenu'});
               &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,$stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$js);
               if ($versionform) {
                   $request->print($versionform);
               }
               $request->print('<br clear="all" />');
               $request->print(&show_previous_task_version($request,$symb));
  } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {   } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'}],1,1);                                         {href=>'',text=>'Select student'}],1,1);
Line 9212  sub handler { Line 10641  sub handler {
                                        {href=>'',text=>'Store grades'}],1,1);                                         {href=>'',text=>'Store grades'}],1,1);
     &updateGradeByPage($request,$symb);      &updateGradeByPage($request,$symb);
  } elsif ($command eq 'processGroup' && $perm{'vgr'}) {   } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
             &startpage($request,$symb);              &startpage($request,$symb,[{href=>'',text=>'...'},
                                          {href=>'',text=>'Modify grades'}]);
     &processGroup($request,$symb);      &processGroup($request,$symb);
  } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {   } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
             &startpage($request,$symb);              &startpage($request,$symb);
Line 9233  sub handler { Line 10663  sub handler {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},{href=>'', text=>"Modify grades"}]);              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},{href=>'', text=>"Modify grades"}]);
     $request->print(&viewgrades($request,$symb));      $request->print(&viewgrades($request,$symb));
  } elsif ($command eq 'handgrade' && $perm{'mgr'}) {   } elsif ($command eq 'handgrade' && $perm{'mgr'}) {
             &startpage($request,$symb);              &startpage($request,$symb,[{href=>'',text=>'...'},
                                          {href=>'',text=>'Store grades'}]);
     $request->print(&processHandGrade($request,$symb));      $request->print(&processHandGrade($request,$symb));
  } elsif ($command eq 'editgrades' && $perm{'mgr'}) {   } elsif ($command eq 'editgrades' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},
Line 9261  sub handler { Line 10692  sub handler {
                                        {href=>'', text=>'Store grades'}]);                                         {href=>'', text=>'Store grades'}]);
             $request->print(&assign_clicker_grades($request,$symb));              $request->print(&assign_clicker_grades($request,$symb));
  } elsif ($command eq 'csvform' && $perm{'mgr'}) {   } elsif ($command eq 'csvform' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $request->print(&upcsvScores_form($request,$symb));      $request->print(&upcsvScores_form($request,$symb));
  } elsif ($command eq 'csvupload' && $perm{'mgr'}) {   } elsif ($command eq 'csvupload' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $request->print(&csvupload($request,$symb));      $request->print(&csvupload($request,$symb));
  } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $request->print(&csvuploadmap($request,$symb));      $request->print(&csvuploadmap($request,$symb));
  } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {   } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {
     if ($env{'form.associate'} ne 'Reverse Association') {      if ($env{'form.associate'} ne 'Reverse Association') {
                 &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
  $request->print(&csvuploadoptions($request,$symb));   $request->print(&csvuploadoptions($request,$symb));
     } else {      } else {
  if ( $env{'form.upfile_associate'} ne 'reverse' ) {   if ( $env{'form.upfile_associate'} ne 'reverse' ) {
Line 9279  sub handler { Line 10710  sub handler {
  } else {   } else {
     $env{'form.upfile_associate'} = 'forward';      $env{'form.upfile_associate'} = 'forward';
  }   }
                 &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
  $request->print(&csvuploadmap($request,$symb));   $request->print(&csvuploadmap($request,$symb));
     }      }
  } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $request->print(&csvuploadassign($request,$symb));      $request->print(&csvuploadassign($request,$symb));
  } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
Line 9313  sub handler { Line 10744  sub handler {
      $request->print(&scantron_download_scantron_data($request,$symb));       $request->print(&scantron_download_scantron_data($request,$symb));
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {          } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&checkscantron_results($request,$symb));                   $request->print(&checkscantron_results($request,$symb));
           } elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}]);
               $request->print(&submit_options_download($request,$symb));
            } elsif ($command eq 'downloadfileslink' && $perm{'vgr'}) {
               &startpage($request,$symb,
      [{href=>&href_symb_cmd($symb,'downloadfilesselect'), text=>'Select which submissions to download'},
       {href=>'', text=>'Download submissions'}]);
               &submit_download_link($request,$symb);
  } elsif ($command) {   } elsif ($command) {
             &startpage($request,$symb);              &startpage($request,$symb,[{href=>'', text=>'Access denied'}]);
     $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');      $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');
  }   }
     }      }
     if ($ssi_error) {      if ($ssi_error) {
  &ssi_print_error($request);   &ssi_print_error($request);
     }      }
     $request->print(&Apache::loncommon::end_page());      if ($env{'form.inhibitmenu'}) {
           $request->print(&Apache::loncommon::end_page());
       } else {
           &Apache::lonquickgrades::endGradeScreen($request);
       }
     &reset_caches();      &reset_caches();
     return '';      return OK;
 }  }
   
 1;  1;
Line 9346  described at http://www.lon-capa.org. Line 10789  described at http://www.lon-capa.org.
 =head1 OVERVIEW  =head1 OVERVIEW
   
 Do an ssi with retries:  Do an ssi with retries:
 While I'd love to factor out this with the vesrion in lonprintout,  While I'd love to factor out this with the version 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  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).  I'm not quite ready to invent (e.g. an ssi_with_retry object).
   
Line 9357  At least the logic that drives this has Line 10800  At least the logic that drives this has
 ssi_with_retries - Does the server side include of a resource.  ssi_with_retries - Does the server side include of a resource.
                      if the ssi call returns an error we'll retry it up to                       if the ssi call returns an error we'll retry it up to
                      the number of times requested by the caller.                       the number of times requested by the caller.
                      If we still have a proble, no text is appended to the                       If we still have a problem, no text is appended to the
                      output and we set some global variables.                       output and we set some global variables.
                      to indicate to the caller an SSI error occurred.                         to indicate to the caller an SSI error occurred.  
                      All of this is supposed to deal with the issues described                       All of this is supposed to deal with the issues described
                      in LonCAPA BZ 5631 see:                       in LON-CAPA BZ 5631 see:
                      http://bugs.lon-capa.org/show_bug.cgi?id=5631                       http://bugs.lon-capa.org/show_bug.cgi?id=5631
                      by informing the user that this happened.                       by informing the user that this happened.
   
Line 9393  ssi_with_retries() Line 10836  ssi_with_retries()
   
 =over  =over
   
   =head1 Routines to display previous version of a Task for a specific student
   
   Tasks are graded pass/fail. Students who have yet to pass a particular Task
   can receive another opportunity. Access to tasks is slot-based. If a slot
   requires a proctor to check-in the student, a new version of the Task will
   be created when the student is checked in to the new opportunity.
   
   If a particular student has tried two or more versions of a particular task,
   the submission screen provides a user with vgr privileges (e.g., a Course
   Coordinator) the ability to display a previous version worked on by the
   student.  By default, the current version is displayed. If a previous version
   has been selected for display, submission data are only shown that pertain
   to that particular version, and the interface to submit grades is not shown.
   
   =over 4
   
   =item show_previous_task_version()
   
   Displays a specified version of a student's Task, as the student sees it.
   
   Inputs: 2
           request - request object
           symb    - unique symb for current instance of resource
   
   Output: None.
   
   Side Effects: calls &show_problem() to print version of Task, with
                 version contained in form item: $env{'form.previousversion'}
   
   =item choose_task_version_form()
   
   Displays a web form used to select which version of a student's view of a
   Task should be displayed.  Either launches a pop-up window, or replaces
   content in existing pop-up, or replaces page in main window.
   
   Inputs: 4
           symb    - unique symb for current instance of resource
           uname   - username of student
           udom    - domain of student
           nomenu  - 1 if display is in a pop-up window, and hence no menu
                     breadcrumbs etc., are displayed
   
   Output: 4
           current   - student's current version
           displayed - student's version being displayed
           result    - scalar containing HTML for web form used to switch to
                       a different version (or a link to close window, if pop-up).
           js        - javascript for processing selection in versions web form
   
   Side Effects: None.
   
   =item previous_display_javascript()
   
   Inputs: 2
           nomenu  - 1 if display is in a pop-up window, and hence no menu
                     breadcrumbs etc., are displayed.
           current - student's current version number.
   
   Output: 1
           js      - javascript for processing selection in versions web form.
   
   Side Effects: None.
   
   =back
   
   =head1 Routines to process bubblesheet data.
   
   =over 4
   
 =item scantron_get_correction() :   =item scantron_get_correction() : 
   
    Builds the interface screen to interact with the operator to fix a     Builds the interface screen to interact with the operator to fix a
Line 9419  ssi_with_retries() Line 10931  ssi_with_retries()
          - missingbubble - array ref of the bubble lines that have missing           - missingbubble - array ref of the bubble lines that have missing
                            bubble errors                             bubble errors
   
      $randomorder - True if exam folder has randomorder set
      $randompick  - True if exam folder has randompick set
      $respnumlookup - Reference to HASH mapping question numbers in bubble lines
                        for current line to question number used for same question
                        in "Master Seqence" (as seen by Course Coordinator).
      $startline   - Reference to hash where key is question number (0 is first)
                     and value is number of first bubble line for current student
                     or code-based randompick and/or randomorder.
   
   
   
 =item  scantron_get_maxbubble() :   =item  scantron_get_maxbubble() : 
   
    Arguments:     Arguments:
Line 9428  ssi_with_retries() Line 10951  ssi_with_retries()
        calling routine should trap the error condition and display the warning         calling routine should trap the error condition and display the warning
        found in &navmap_errormsg().         found in &navmap_errormsg().
   
          $scantron_config - Reference to bubblesheet format configuration hash.
   
    Returns the maximum number of bubble lines that are expected to     Returns the maximum number of bubble lines that are expected to
    occur. Does this by walking the selected sequence rendering the     occur. Does this by walking the selected sequence rendering the
    resource and then checking &Apache::lonxml::get_problem_counter()     resource and then checking &Apache::lonxml::get_problem_counter()
Line 9437  ssi_with_retries() Line 10962  ssi_with_retries()
    $env{'form.scantron.bubble_lines.n'},      $env{'form.scantron.bubble_lines.n'}, 
    $env{'form.scantron.first_bubble_line.n'} and     $env{'form.scantron.first_bubble_line.n'} and
    $env{"form.scantron.sub_bubblelines.n"}     $env{"form.scantron.sub_bubblelines.n"}
    which are the total number of bubble, lines, the number of bubble     which are the total number of bubble lines, the number of bubble
    lines for response n and number of the first bubble line for response n,     lines for response n and number of the first bubble line for response n,
    and a comma separated list of numbers of bubble lines for sub-questions     and a comma separated list of numbers of bubble lines for sub-questions
    (for optionresponse, matchresponse, and rankresponse items), for response n.       (for optionresponse, matchresponse, and rankresponse items), for response n.  
Line 9451  ssi_with_retries() Line 10976  ssi_with_retries()
   
 =item  scantron_process_students() :   =item  scantron_process_students() : 
   
    Routine that does the actual grading of the bubble sheet information.     Routine that does the actual grading of the bubblesheet information.
   
    The parsed scanline hash is added to %env      The parsed scanline hash is added to %env 
   
Line 9471  ssi_with_retries() Line 10996  ssi_with_retries()
   
 =item  scantron_upload_scantron_data() :  =item  scantron_upload_scantron_data() :
   
     Creates the screen for adding a new bubble sheet data file to a course.      Creates the screen for adding a new bubblesheet data file to a course.
   
 =item  scantron_upload_scantron_data_save() :   =item  scantron_upload_scantron_data_save() : 
   
Line 9485  ssi_with_retries() Line 11010  ssi_with_retries()
 =item  scantron_download_scantron_data() :   =item  scantron_download_scantron_data() : 
   
    Shows a list of the three internal files (original, corrected,     Shows a list of the three internal files (original, corrected,
    skipped) for a specific bubble sheet data file that exists in the     skipped) for a specific bubblesheet data file that exists in the
    course.     course.
   
 =item  scantron_validate_ID() :   =item  scantron_validate_ID() : 
Line 9496  ssi_with_retries() Line 11021  ssi_with_retries()
 =item navmap_errormsg() :  =item navmap_errormsg() :
   
    Returns HTML mark-up inside a <div></div> with a link to re-initialize the course.     Returns HTML mark-up inside a <div></div> with a link to re-initialize the course.
    Should be called whenever the request to instantiate a navmap object fails.       Should be called whenever the request to instantiate a navmap object fails.
   
   =back
   
 =back  =back
   

Removed from v.1.617  
changed lines
  Added in v.1.749


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