Diff for /loncom/homework/grades.pm between versions 1.576 and 1.596.2.12.2.53

version 1.576, 2009/05/25 11:12:13 version 1.596.2.12.2.53, 2020/09/08 15:48: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::bridgetask();
   use Apache::lontexconvert();
   use HTML::Parser();
   use File::MMagic;
 use String::Similarity;  use String::Similarity;
 use LONCAPA;  use LONCAPA;
   
Line 51  use POSIX qw(floor); Line 56  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 96  sub ssi_print_error { Line 102  sub ssi_print_error {
   
 #  #
 # --- Retrieve the parts from the metadata file.---  # --- Retrieve the parts from the metadata file.---
   # Returns an array of everything that the resources stores away
   #
   
 sub getpartlist {  sub getpartlist {
     my ($symb) = @_;      my ($symb,$errorref) = @_;
   
     my $navmap   = Apache::lonnavmaps::navmap->new();      my $navmap   = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           if (ref($errorref)) { 
               $$errorref = 'navmap';
               return;
           }
       }
     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();
Line 114  sub getpartlist { Line 129  sub getpartlist {
     return @stores;      return @stores;
 }  }
   
 # --- Get the symbolic name of a problem and the url  
 sub get_symb {  
     my ($request,$silent) = @_;  
     (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;  
     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));  
     if ($symb eq '') {   
  if (!$silent) {  
     $request->print("Unable to handle ambiguous references:$url:.");  
     return ();  
  }  
     }  
     &Apache::lonenc::check_decrypt(\$symb);  
     return ($symb);  
 }  
   
 #--- Format fullname, username:domain if different for display  #--- Format fullname, username:domain if different for display
 #--- Use anywhere where the student names are listed  #--- Use anywhere where the student names are listed
 sub nameUserString {  sub nameUserString {
Line 143  sub nameUserString { Line 143  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. ---
   #--- Count responseIDs, essayresponse items, and dropbox items ---
   #--- Sets response_error pointer to "1" if navmaps object broken ---
 sub response_type {  sub response_type {
     my ($symb) = shift;      my ($symb,$response_error) = @_;
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           if (ref($response_error)) {
               $$response_error = 1;
           }
           return;
       }
     my $res = $navmap->getBySymb($symb);      my $res = $navmap->getBySymb($symb);
       unless (ref($res)) {
           $$response_error = 1;
           return;
       }
     my $partlist = $res->parts();      my $partlist = $res->parts();
       my ($numresp,$numessay,$numdropbox) = (0,0,0);
     my %vPart =       my %vPart = 
  map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));   map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
     my (%response_types,%handgrade);      my (%response_types,%handgrade);
Line 158  sub response_type { Line 171  sub response_type {
  my @types = $res->responseType($part);   my @types = $res->responseType($part);
  my @ids = $res->responseIds($part);   my @ids = $res->responseIds($part);
  for (my $i=0; $i < scalar(@ids); $i++) {   for (my $i=0; $i < scalar(@ids); $i++) {
               $numresp ++;
     $response_types{$part}{$ids[$i]} = $types[$i];      $response_types{$part}{$ids[$i]} = $types[$i];
               if ($types[$i] eq 'essay') {
                   $numessay ++;
                   if (&Apache::lonnet::EXT("resource.$part".'_'.$ids[$i].".uploadedfiletypes",$symb)) {
                       $numdropbox ++;
                   }
               }
     $handgrade{$part.'_'.$ids[$i]} =       $handgrade{$part.'_'.$ids[$i]} = 
  &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].   &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
      '.handgrade',$symb);       '.handgrade',$symb);
  }   }
     }      }
     return ($partlist,\%handgrade,\%response_types);      return ($partlist,\%handgrade,\%response_types,$numresp,$numessay,$numdropbox);
 }  }
   
 sub flatten_responseType {  sub flatten_responseType {
Line 183  sub get_display_part { Line 203  sub get_display_part {
     my ($partID,$symb)=@_;      my ($partID,$symb)=@_;
     my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);      my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
     if (defined($display) and $display ne '') {      if (defined($display) and $display ne '') {
  $display.= " (<span class=\"LC_internal_info\">id $partID</span>)";          $display.= ' (<span class="LC_internal_info">'
                     .&mt('Part ID: [_1]',$partID).'</span>)';
     } else {      } else {
  $display=$partID;   $display=$partID;
     }      }
     return $display;      return $display;
 }  }
   
 #--- Show resource title  #--- Show parts and response type
 #--- and parts and response type  
 sub showResourceInfo {  sub showResourceInfo {
     my ($symb,$probTitle,$checkboxes) = @_;      my ($symb,$partlist,$responseType,$formname,$checkboxes,$uploads) = @_;
     my $col=3;      unless ((ref($partlist) eq 'ARRAY') && (ref($responseType) eq 'HASH')) {
     if ($checkboxes) { $col=4; }          return '<br clear="all">';
     my $result = '<h3>'.&mt('Current Resource').': '.$probTitle.'</h3>'."\n";      }
     $result .='<table border="0">';      my $coltitle = &mt('Problem Part Shown');
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      if ($checkboxes) {
     my %resptype = ();          $coltitle = &mt('Problem Part');
     my $hdgrade='no';      } else {
           my $checkedparts = 0;
           foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
               if (grep(/^\Q$partid\E$/,@{$partlist})) {
                   $checkedparts ++;
               }
           }
           if ($checkedparts == scalar(@{$partlist})) {
               return '<br clear="all">';
           }
           if ($uploads) {
               $coltitle = &mt('Problem Part Selected');
           }
       }
       my $result = '<div class="LC_left_float" style="display:inline-block;">';
       if ($checkboxes) {
           my $legend = &mt('Parts to display');
           if ($uploads) {
               $legend = &mt('Part(s) with dropbox');
           }
           $result .= '<fieldset style="display:inline-block;"><legend>'.$legend.'</legend>'.
                      '<span class="LC_nobreak">'.
                      '<label><input type="radio" name="chooseparts" value="0" onclick="toggleParts('."'$formname'".');" checked="checked" />'.
                      &mt('All parts').'</label>'.('&nbsp;'x2).
                      '<label><input type="radio" name="chooseparts" value="1" onclick="toggleParts('."'$formname'".');" />'.
                      &mt('Selected parts').'</label></span>'.
                      '<div id="LC_partselector" style="display:none">';
       }
       $result .= &Apache::loncommon::start_data_table()
                 .&Apache::loncommon::start_data_table_header_row();
       if ($checkboxes) {
           $result .= '<th>'.&mt('Display?').'</th>';
       }
       $result .= '<th>'.$coltitle.'</th>'
                 .'<th>'.&mt('Res. ID').'</th>'
                 .'<th>'.&mt('Type').'</th>'
                 .&Apache::loncommon::end_data_table_header_row();
     my %partsseen;      my %partsseen;
     foreach my $partID (sort(keys(%$responseType))) {      foreach my $partID (sort(keys(%$responseType))) {
  foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {          foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {
     my $handgrade=$$handgrade{$partID.'_'.$resID};              my $responsetype = $responseType->{$partID}->{$resID};
     my $responsetype = $responseType->{$partID}->{$resID};              if ($uploads) {
     $hdgrade = $handgrade if ($handgrade eq 'yes');                  next unless ($responsetype eq 'essay');
     $result.='<tr>';                  next unless (&Apache::lonnet::EXT("resource.$partID".'_'."$resID.uploadedfiletypes",$symb));
     if ($checkboxes) {              }
  if (exists($partsseen{$partID})) {              my $display_part=&get_display_part($partID,$symb);
     $result.="<td>&nbsp;</td>";              if (exists($partsseen{$partID})) {
  } else {                  $result.=&Apache::loncommon::continue_data_table_row();
     $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='checked' /></td>";              } else {
  }                  $partsseen{$partID}=scalar(keys(%{$responseType->{$partID}}));
  $partsseen{$partID}=1;                  $result.=&Apache::loncommon::start_data_table_row().
     }                           '<td rowspan="'.$partsseen{$partID}.'" style="vertical-align:middle">';
     my $display_part=&get_display_part($partID,$symb);                  if ($checkboxes) {
     $result.='<td><b>'.&mt('Part').': </b>'.$display_part.                      $result.='<input type="checkbox" name="vPart" checked="checked" value="'.$partID.'" /></td>'.
                 ' <span class="LC_internal_info">'.$resID.'</span></td>'.                               '<td rowspan="'.$partsseen{$partID}.'" style="vertical-align:middle">'.$display_part.'</td>';
  '<td><b>'.&mt('Type').': </b>'.$responsetype.'</td></tr>';                  } else {
 #    '<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td></tr>';                      $result.=$display_part.'</td>';
  }                  }
               }
               $result.='<td>'.'<span class="LC_internal_info">'.$resID.'</span></td>'
                       .'<td>'.&mt($responsetype).'</td>'
                       .&Apache::loncommon::end_data_table_row();
           }
       }
       $result.=&Apache::loncommon::end_data_table();
       if ($checkboxes) {
           $result .= '</div></fieldset>';
       }
       $result .= '</div><div style="padding:0;clear:both;margin:0;border:0"></div>';
       if (!keys(%partsseen)) {
           $result = '';
           if ($uploads) {
               return '<div style="padding:0;clear:both;margin:0;border:0"></div>'.
                      '<p class="LC_info">'.
                       &mt('No dropbox items or essayresponse items with uploadedfiletypes set.').
                      '</p>';
           } else {
               return '<br clear="all" />';
           }
       }  
       return $result;
   }
   
   sub part_selector_js {
       my $js = <<"END";
   function toggleParts(formname) {
       if (document.getElementById('LC_partselector')) {
           var index = '';
           if (document.forms.length) {
               for (var i=0; i<document.forms.length; i++) {
                   if (document.forms[i].name == formname) {
                       index = i;
                       break;
                   }
               }
           }
           if ((index != '') && (document.forms[index].elements['chooseparts'].length > 1)) {
               for (var i=0; i<document.forms[index].elements['chooseparts'].length; i++) {
                   if (document.forms[index].elements['chooseparts'][i].checked) {
                      var val = document.forms[index].elements['chooseparts'][i].value;
                       if (document.forms[index].elements['chooseparts'][i].value == 1) {
                           document.getElementById('LC_partselector').style.display = 'block';
                       } else {
                           document.getElementById('LC_partselector').style.display = 'none';
                       }
                   }
               }
           }
     }      }
     $result.='</table>'."\n";  }
     return $result,$responseType,$hdgrade,$partlist,$handgrade;  END
       return &Apache::lonhtmlcommon::scripttag($js);
 }  }
   
 sub reset_caches {  sub reset_caches {
     &reset_analyze_cache();      &reset_analyze_cache();
     &reset_perm();      &reset_perm();
       &reset_old_essays();
 }  }
   
 {  {
Line 242  sub reset_caches { Line 350  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 271  sub reset_caches { Line 384  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 286  sub reset_caches { Line 408  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 305  sub reset_caches { Line 427  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 313  sub reset_caches { Line 435  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 336  sub reset_caches { Line 467  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 353  sub cleanRecord { Line 486  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 375  sub cleanRecord { Line 510  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 396  sub cleanRecord { Line 533  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 '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 410  sub cleanRecord { Line 547  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;          $answer = &Apache::lontexconvert::msgtexconverted($answer);
  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 447  sub cleanRecord { Line 585  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
 sub commonJSfunctions {  sub commonJSfunctions {
     my $request = shift;      my $request = shift;
     $request->print(<<COMMONJSFUNCTIONS);      $request->print(&Apache::lonhtmlcommon::scripttag(<<COMMONJSFUNCTIONS));
 <script type="text/javascript" language="javascript">  
     function radioSelection(radioButton) {      function radioSelection(radioButton) {
  var selection=null;   var selection=null;
  if (radioButton.length > 1) {   if (radioButton.length > 1) {
Line 487  sub commonJSfunctions { Line 626  sub commonJSfunctions {
     return selectOne.value;      return selectOne.value;
  }   }
     }      }
 </script>  
 COMMONJSFUNCTIONS  COMMONJSFUNCTIONS
 }  }
   
 #--- Dumps the class list with usernames,list of sections,  #--- Dumps the class list with usernames,list of sections,
 #--- section, ids and fullnames for each user.  #--- section, ids and fullnames for each user.
 sub getclasslist {  sub getclasslist {
     my ($getsec,$filterlist,$getgroup) = @_;      my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus) = @_;
     my @getsec;      my @getsec;
     my @getgroup;      my @getgroup;
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
Line 522  sub getclasslist { Line 660  sub getclasslist {
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
       my ($cdom,$cnum,$partlist);
       if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
           $cdom = $env{"course.$env{'request.course.id'}.domain"};
           $cnum = $env{"course.$env{'request.course.id'}.num"};
           my $res_error;
           ($partlist) = &response_type($symb,\$res_error);
       }
     foreach my $student (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
         my $end      =           my $end      = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_END()];              $classlist->{$student}->[&Apache::loncoursedata::CL_END()];
Line 538  sub getclasslist { Line 683  sub getclasslist {
         my $group   =           my $group   = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];              $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
  # filter students according to status selected   # filter students according to status selected
  if ($filterlist && (!($stu_status =~ /Any/))) {   if ($filterbyaccstatus && (!($stu_status =~ /Any/))) {
     if (!($stu_status =~ $status)) {      if (!($stu_status =~ $status)) {
  delete($classlist->{$student});   delete($classlist->{$student});
  next;   next;
Line 555  sub getclasslist { Line 700  sub getclasslist {
                }                  } 
         }          }
            if (($grp eq 'none') && !$group) {             if (($grp eq 'none') && !$group) {
                $exclude = 0;             $exclude = 0;
         }          }
     }      }
     if ($exclude) {      if ($exclude) {
         delete($classlist->{$student});          delete($classlist->{$student});
    next;
     }      }
  }   }
           if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
               my $udom =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
               my $uname =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
               if (($symb ne '') && ($udom ne '') && ($uname ne '')) {
                   if ($submitonly eq 'queued') {
                       my %queue_status =
                           &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
                                                                   $udom,$uname);
                       if (!defined($queue_status{'gradingqueue'})) {
                           delete($classlist->{$student});
                           next;
                       }
                   } else {
                       my (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);
                       my $submitted = 0;
                       my $graded = 0;
                       my $incorrect = 0;
                       foreach (keys(%status)) {
                           $submitted = 1 if ($status{$_} ne 'nothing');
                           $graded = 1 if ($status{$_} =~ /^ungraded/);
                           $incorrect = 1 if ($status{$_} =~ /^incorrect/);
   
                           my ($foo,$partid,$foo1) = split(/\./,$_);
                           if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
                               $submitted = 0;
                           }
                       }
                       if (!$submitted && ($submitonly eq 'yes' ||
                                           $submitonly eq 'incorrect' ||
                                           $submitonly eq 'graded')) {
                           delete($classlist->{$student});
                           next;
                       } elsif (!$graded && ($submitonly eq 'graded')) {
                           delete($classlist->{$student});
                           next;
                       } elsif (!$incorrect && $submitonly eq 'incorrect') {
                           delete($classlist->{$student});
                           next;
                       }
                   }
               }
           }
  $section = ($section ne '' ? $section : 'none');   $section = ($section ne '' ? $section : 'none');
  if (&canview($section)) {   if (&canview($section)) {
     if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
Line 576  sub getclasslist { Line 766  sub getclasslist {
     delete($classlist->{$student});      delete($classlist->{$student});
  }   }
     }      }
     my %seen = ();  
     my @sections = sort(keys(%sections));      my @sections = sort(keys(%sections));
     return ($classlist,\@sections,\%fullnames);      return ($classlist,\@sections,\%fullnames);
 }  }
Line 592  sub canmodify { Line 781  sub canmodify {
  #can modify the requested section   #can modify the requested section
  return 1;   return 1;
     } else {      } else {
  # can't modify the request section   # can't modify the requested section
  return 0;   return 0;
     }      }
  }   }
Line 605  sub canview { Line 794  sub canview {
     my ($sec)=@_;      my ($sec)=@_;
     if ($perm{'vgr'}) {      if ($perm{'vgr'}) {
  if (!defined($perm{'vgr_section'})) {   if (!defined($perm{'vgr_section'})) {
     # can modify whole class      # can view whole class
     return 1;      return 1;
  } else {   } else {
     if ($sec eq $perm{'vgr_section'}) {      if ($sec eq $perm{'vgr_section'}) {
  #can modify the requested section   #can view the requested section
  return 1;   return 1;
     } else {      } else {
  # can't modify the request section   # can't view the requested section
  return 0;   return 0;
     }      }
  }   }
     }      }
     #can't modify      #can't view
     return 0;      return 0;
 }  }
   
Line 642  sub student_gradeStatus { Line 831  sub student_gradeStatus {
 sub jscriptNform {  sub jscriptNform {
     my ($symb) = @_;      my ($symb) = @_;
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $jscript='<script type="text/javascript" language="javascript">'."\n".      my $jscript= &Apache::lonhtmlcommon::scripttag(
  '    function viewOneStudent(user,domain) {'."\n".   '    function viewOneStudent(user,domain) {'."\n".
  ' document.onestudent.student.value = user;'."\n".   ' document.onestudent.student.value = user;'."\n".
  ' document.onestudent.userdom.value = domain;'."\n".   ' document.onestudent.userdom.value = domain;'."\n".
  ' document.onestudent.submit();'."\n".   ' document.onestudent.submit();'."\n".
  '    }'."\n".   '    }'."\n".
  '</script>'."\n";   "\n");
     $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".      $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".  
  '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n".  
  '<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".   '<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".
  '<input type="hidden" name="command" value="submission" />'."\n".   '<input type="hidden" name="command" value="submission" />'."\n".
  '<input type="hidden" name="student" value="" />'."\n".   '<input type="hidden" name="student" value="" />'."\n".
Line 697  sub compute_points { Line 884  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 705  sub most_similar { Line 896  sub most_similar {
   
 # ignore empty submissions (occuring when only files are sent)  # ignore empty submissions (occuring when only files are sent)
   
     unless ($uessay=~/\w+/) { return ''; }      unless ($uessay=~/\w+/s) { return ''; }
   
 # these will be returned. Do not care if not at least 50 percent similar  # these will be returned. Do not care if not at least 50 percent similar
     my $limit=0.6;      my $limit=0.6;
Line 714  sub most_similar { Line 905  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 728  sub most_similar { Line 919  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 742  sub most_similar { Line 933  sub most_similar {
   
 #------------------------------------ Receipt Verification Routines  #------------------------------------ Receipt Verification Routines
 #  #
   
   sub initialverifyreceipt {
      my ($request,$symb) = @_;
      &commonJSfunctions($request);
      return '<form name="gradingMenu" action=""><input type="submit" value="'.&mt('Verify Receipt Number.').'" />'.
           &Apache::lonnet::recprefix($env{'request.course.id'}).
           '-<input type="text" name="receipt" size="4" />'.
           '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
           '<input type="hidden" name="command" value="verify" />'.
           "</form>\n";
   }
   
 #--- Check whether a receipt number is valid.---  #--- Check whether a receipt number is valid.---
 sub verifyreceipt {  sub verifyreceipt {
     my $request  = shift;      my ($request,$symb) = @_;
   
     my $courseid = $env{'request.course.id'};      my $courseid = $env{'request.course.id'};
     my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.      my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.
  $env{'form.receipt'};   $env{'form.receipt'};
     $receipt     =~ s/[^\-\d]//g;      $receipt     =~ s/[^\-\d]//g;
     my ($symb)   = &get_symb($request);  
   
     my $title.=      my $title =
  '<h3><span class="LC_info">'.   '<h3><span class="LC_info">'.
  &mt('Verifying  Receipt No. [_1]',$receipt).   &mt('Verifying Receipt Number [_1]',$receipt).
  '</span></h3>'."\n".   '</span></h3>'."\n";
  '<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}).  
  '</h4>'."\n";  
   
     my ($string,$contents,$matches) = ('','',0);      my ($string,$contents,$matches) = ('','',0);
     my (undef,undef,$fullname) = &getclasslist('all','0');      my (undef,undef,$fullname) = &getclasslist('all','0');
Line 766  sub verifyreceipt { Line 966  sub verifyreceipt {
     if ($env{"course.$courseid.receiptalg"} eq 'receipt2' ||      if ($env{"course.$courseid.receiptalg"} eq 'receipt2' ||
  $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; }   $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; }
     my $parts=['0'];      my $parts=['0'];
     if ($receiptparts) { ($parts)=&response_type($symb); }      if ($receiptparts) {
           my $res_error; 
           ($parts)=&response_type($symb,\$res_error);
           if ($res_error) {
               return &navmap_errormsg();
           } 
       }
           
     my $header =       my $header = 
  &Apache::loncommon::start_data_table().   &Apache::loncommon::start_data_table().
Line 808  sub verifyreceipt { Line 1014  sub verifyreceipt {
  }   }
     }      }
     if ($matches == 0) {      if ($matches == 0) {
  $string = $title.&mt('No match found for the above receipt.');          $string = $title
                    .'<p class="LC_warning">'
                    .&mt('No match found for the above receipt number.')
                    .'</p>';
     } else {      } else {
  $string = &jscriptNform($symb).$title.   $string = &jscriptNform($symb).$title.
     '<p>'.      '<p>'.
     &mt('The above receipt matches the following [numerate,_1,student].',$matches).      &mt('The above receipt number matches the following [quant,_1,student].',$matches).
     '</p>'.      '</p>'.
     $header.      $header.
     $contents.      $contents.
     &Apache::loncommon::end_data_table()."\n";      &Apache::loncommon::end_data_table()."\n";
     }      }
     return $string.&show_grading_menu_form($symb);      return $string;
 }  }
   
 #--- This is called by a number of programs.  #--- This is called by a number of programs.
Line 826  sub verifyreceipt { Line 1035  sub verifyreceipt {
 #--- Also called directly when one clicks on the subm button   #--- Also called directly when one clicks on the subm button 
 #    on the problem page.  #    on the problem page.
 sub listStudents {  sub listStudents {
     my ($request) = shift;      my ($request,$symb,$submitonly,$divforres) = @_;
   
     my ($symb) = &get_symb($request);  
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};      my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
     my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};      my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
     my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};      unless ($submitonly) {
     my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';          $submitonly = $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
     $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ?       }
  &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};  
   
     my $result='<h3><span class="LC_info">&nbsp;'  
  .&mt("$viewgrade Submissions for a Student or a Group of Students")  
  .'</span></h3>';  
   
     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));      my $result='';
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
   
       my $table;
       if (ref($partlist) eq 'ARRAY') {
           if (scalar(@$partlist) > 1 ) {
               $table = &showResourceInfo($symb,$partlist,$responseType,'gradesub',1);
           } elsif ($divforres) {
               $table = '<div style="padding:0;clear:both;margin:0;border:0"></div>';
           } else {
               $table = '<br clear="all" />';
           }
       }
   
     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.',
      );       );
     $request->print(<<LISTJAVASCRIPT);      &js_escape(\%js_lt);
 <script type="text/javascript" language="javascript">      $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));
     function checkSelect(checkBox) {      function checkSelect(checkBox) {
  var ctr=0;   var ctr=0;
  var sense="";   var sense="";
Line 859  sub listStudents { Line 1075  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 878  sub listStudents { Line 1094  sub listStudents {
  formname.command.value = 'submission';   formname.command.value = 'submission';
  formname.submit();   formname.submit();
     }      }
 </script>  
 LISTJAVASCRIPT  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".$table;   "\n".$table;
   
     $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();      $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
                   .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"                    .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
Line 901  LISTJAVASCRIPT Line 1114  LISTJAVASCRIPT
                   .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"                    .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure();
   
     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;
     $submission_options.=      my %optiontext = &Apache::lonlocal::texthash (
  '<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> '.&mt('last submission only').' </label>'."\n".                            lastonly => 'last submission',
  '<label><input type="radio" name="lastSub" value="last" /> '.&mt('last submission &amp; parts info').' </label>'."\n".                            last     => 'last submission with details',
  '<label><input type="radio" name="lastSub" value="datesub" /> '.&mt('by dates and submissions').' </label>'."\n".                            datesub  => 'all submissions',
  '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').'</label>';                            all      => 'all submissions with details',
                         );
       my $submission_options =
           '<span class="LC_nobreak">'.
           '<label><input type="radio" name="lastSub" value="lastonly" /> '.
           $optiontext{'lastonly'}.' </label></span>'."\n".
           '<span class="LC_nobreak">'.
           '<label><input type="radio" name="lastSub" value="last" /> '.
           $optiontext{'last'}.' </label></span>'."\n".
           '<span class="LC_nobreak">'.
           '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.
           $optiontext{'datesub'}.'</label></span>'."\n".
           '<span class="LC_nobreak">'.
           '<label><input type="radio" name="lastSub" value="all" /> '.
           $optiontext{'all'}.'</label></span>';
       my ($compmsg,$nocompmsg);
       $nocompmsg = ' checked="checked"';
       if ($numessay) {
           $compmsg = $nocompmsg;
           $nocompmsg = '';
       }
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))
                   .$submission_options                    .$submission_options
                     .&Apache::lonhtmlcommon::row_closure()
                     .&Apache::lonhtmlcommon::row_title(&mt('Send Messages'))
                     .'<span class="LC_nobreak">'
                     .'<label><input type="radio" name="compmsg" value="0"'.$nocompmsg.' />'
                     .&mt('No').('&nbsp;'x2).'</label>'
                     .'<label><input type="radio" name="compmsg" value="1"'.$compmsg.' />'
                     .&mt('Yes').('&nbsp;'x2).'</label>'
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure();
   
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
Line 924  LISTJAVASCRIPT Line 1159  LISTJAVASCRIPT
                   .'<option value=".5">'.&mt('Half Points').'</option>'                    .'<option value=".5">'.&mt('Half Points').'</option>'
                   .'<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();  
   
     $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="saveState"   value="'.$env{'form.saveState'}.'" />'."\n".  
  '<input type="hidden" name="probTitle"   value="'.$env{'form.probTitle'}.'" />'."\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.Status'})) {
     if (exists($env{'form.gradingMenu'}) && 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 {
         $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))          $gradeTable .= &Apache::lonhtmlcommon::row_closure()
                         .&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();  
     }      }
       if ($numessay) {
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))          $gradeTable .= &Apache::lonhtmlcommon::row_closure()
                   .'<input type="checkbox" name="checkPlag" checked="checked" />'                        .&Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
                   .&Apache::lonhtmlcommon::row_closure(1)                        .'<input type="checkbox" name="checkPlag" checked="checked" />';
       }
       $gradeTable .= &Apache::lonhtmlcommon::row_closure(1)
                   .&Apache::lonhtmlcommon::end_pick_box();                    .&Apache::lonhtmlcommon::end_pick_box();
   
     $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"                    .&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.")."\n"
                   .'<input type="hidden" name="command" value="processGroup" />'                    .'<input type="hidden" name="command" value="processGroup" />'
                   .'</p>';                    .'</p>';
   
 # checkall buttons  # checkall buttons
     $gradeTable.=&check_script('gradesub', 'stuinfo');      $gradeTable.=&check_script('gradesub', 'stuinfo');
     $gradeTable.='<input type="button" '."\n".      $gradeTable.='<input type="button" '."\n".
  'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".          'onclick="javascript:checkSelect(this.form.stuinfo);" '."\n".
  'value="'.&mt('Next').' &rarr;" /> <br />'."\n";          'value="'.&mt('Next').' &rarr;" /> <br />'."\n";
     $gradeTable.=&check_buttons();      $gradeTable.=&check_buttons();
     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup);      my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup);
     $gradeTable.= &Apache::loncommon::start_data_table().      $gradeTable.= &Apache::loncommon::start_data_table().
Line 969  LISTJAVASCRIPT Line 1199  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 1007  LISTJAVASCRIPT Line 1235  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 1050  LISTJAVASCRIPT Line 1276  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 1064  LISTJAVASCRIPT Line 1290  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 1301  LISTJAVASCRIPT
     }      }
   
     $gradeTable.=&Apache::loncommon::end_data_table()."\n".      $gradeTable.=&Apache::loncommon::end_data_table()."\n".
  '<input type="button" '.          '<input type="button" '.
  'onClick="javascript:checkSelect(this.form.stuinfo);" '.          'onclick="javascript:checkSelect(this.form.stuinfo);" '.
  'value="'.&mt('Next').' &rarr;" /></form>'."\n";          'value="'.&mt('Next').' &rarr;" /></form>'."\n";
     if ($ctr == 0) {      if ($ctr == 0) {
  my $num_students=(scalar(keys(%$fullname)));   my $num_students=(scalar(keys(%$fullname)));
  if ($num_students eq 0) {   if ($num_students eq 0) {
Line 1090  LISTJAVASCRIPT Line 1314  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 />';
  }   }
     } elsif ($ctr == 1) {      } elsif ($ctr == 1) {
  $gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/;   $gradeTable =~ s/type="checkbox"/type="checkbox" checked="checked"/;
     }      }
     $gradeTable.=&show_grading_menu_form($symb);  
     $request->print($gradeTable);      $request->print($gradeTable);
     return '';      return '';
 }  }
Line 1105  LISTJAVASCRIPT Line 1328  LISTJAVASCRIPT
 #---- Called from the listStudents routine  #---- Called from the listStudents routine
   
 sub check_script {  sub check_script {
     my ($form, $type)=@_;      my ($form,$type) = @_;
     my $chkallscript='<script type="text/javascript">      my $chkallscript = &Apache::lonhtmlcommon::scripttag('
     function checkall() {      function checkall() {
         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {          for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
             ele = document.forms.'.$form.'.elements[i];              ele = document.forms.'.$form.'.elements[i];
Line 1137  sub check_script { Line 1360  sub check_script {
         }          }
     }      }
   
 </script>'."\n";  '."\n");
     return $chkallscript;      return $chkallscript;
 }  }
   
Line 1151  sub check_buttons { Line 1374  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 1161  sub processGroup { Line 1384  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 1175  sub processGroup { Line 1398  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 = ');
     $request->print(<<SUBJAVASCRIPT);      &js_escape(\$alertmsg);
 <script type="text/javascript" language="javascript">      $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];
  var radioButton = formname["RADVAL"+id];   var radioButton = formname["RADVAL"+id];
Line 1294  sub sub_page_js { Line 1517  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 1345  sub sub_page_js { Line 1559  sub sub_page_js {
   
  formname.submit();   formname.submit();
     }      }
 </script>  
 SUBJAVASCRIPT  SUBJAVASCRIPT
 }  }
   
 #--- javascript for essay type problem --  #--- javascript for grading message center
 sub sub_page_kw_js {  sub sub_grademessage_js {
     my $request = shift;      my $request = shift;
     my $iconpath = $request->dir_config('lonIconsURL');      my $iconpath = $request->dir_config('lonIconsURL');
     &commonJSfunctions($request);      &commonJSfunctions($request);
   
     my $inner_js_msg_central=<<INNERJS;      my $inner_js_msg_central= (<<INNERJS);
     <script text="text/javascript">  <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 1393  sub sub_page_kw_js { Line 1606  sub sub_page_kw_js {
       self.close()        self.close()
   
     }      }
     </script>  
 INNERJS  
   
     my $inner_js_highlight_central=<<INNERJS;  
  <script type="text/javascript">  
     function updateChoice(flag) {  
       opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);  
       opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);  
       opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);  
       opener.document.SCORE.refresh.value = "on";  
       if (opener.document.SCORE.keywords.value!=""){  
          opener.document.SCORE.submit();  
       }  
       self.close()  
     }  
 </script>  </script>
 INNERJS  INNERJS
   
     my $start_page_msg_central =       my $start_page_msg_central =
         &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,          &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,
        {'js_ready'  => 1,         {'js_ready'  => 1,
  'only_body' => 1,   'only_body' => 1,
  'bgcolor'   =>'#FFFFFF',});   'bgcolor'   =>'#FFFFFF',});
     my $end_page_msg_central =       my $end_page_msg_central =
  &Apache::loncommon::end_page({'js_ready' => 1});   &Apache::loncommon::end_page({'js_ready' => 1});
   
   
     my $start_page_highlight_central =   
         &Apache::loncommon::start_page('Highlight Central',  
        $inner_js_highlight_central,  
        {'js_ready'  => 1,  
  'only_body' => 1,  
  'bgcolor'   =>'#FFFFFF',});  
     my $end_page_highlight_central =   
  &Apache::loncommon::end_page({'js_ready' => 1});  
   
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();      my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;      $docopen=~s/^document\.//;
     my $alertmsg = &mt('Please select a word or group of words from document and then click this link.');  
     $request->print(<<SUBJAVASCRIPT);  
 <script type="text/javascript" language="javascript">  
   
 //===================== Show list of keywords ====================      my %html_js_lt = &Apache::lonlocal::texthash(
   function keywords(formname) {                  comp => 'Compose Message for: ',
     var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);                  incl => 'Include',
     if (nret==null) return;                  type => 'Type',
     formname.keywords.value = nret;                  subj => 'Subject',
                   mesa => 'Message',
     if (formname.keywords.value != "") {                  new  => 'New',
  formname.refresh.value = "on";                  save => 'Save',
  formname.submit();                  canc => 'Cancel',
     }               );
     return;      &html_escape(\%html_js_lt);
   }      &js_escape(\%html_js_lt);
       $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
   
 //===================== Script to view submitted by ==================  //===================== Script to view submitted by ==================
   function viewSubmitter(submitter) {    function viewSubmitter(submitter) {
Line 1457  INNERJS Line 1644  INNERJS
     return;      return;
   }    }
   
 //===================== Script to add keyword(s) ==================  
   function getSel() {  
     if (document.getSelection) txt = document.getSelection();  
     else if (document.selection) txt = document.selection.createRange().text;  
     else return;  
     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");  
     if (cleantxt=="") {  
  alert("$alertmsg");  
  return;  
     }  
     var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);  
     if (nret==null) return;  
     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;  
     if (document.SCORE.keywords.value != "") {  
  document.SCORE.refresh.value = "on";  
  document.SCORE.submit();  
     }  
     return;  
   }  
   
 //====================== Script for composing message ==============  //====================== Script for composing message ==============
    // preload images     // preload images
    img1 = new Image();     img1 = new Image();
Line 1531  INNERJS Line 1698  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 1549  INNERJS Line 1714  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("<\\/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();
 }  }
   
   SUBJAVASCRIPT
   }
   
   #--- javascript for essay type problem --
   sub sub_page_kw_js {
       my $request = shift;
   
       unless ($env{'form.compmsg'}) {
           &commonJSfunctions($request);
       }
   
       my $inner_js_highlight_central= (<<INNERJS);
   <script type="text/javascript">
       function updateChoice(flag) {
         opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
         opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
         opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);
         opener.document.SCORE.refresh.value = "on";
         if (opener.document.SCORE.keywords.value!=""){
            opener.document.SCORE.submit();
         }
         self.close()
       }
   </script>
   INNERJS
   
       my $start_page_highlight_central =
           &Apache::loncommon::start_page('Highlight Central',
                                          $inner_js_highlight_central,
                                          {'js_ready'  => 1,
                                           'only_body' => 1,
                                           'bgcolor'   =>'#FFFFFF',});
       my $end_page_highlight_central =
           &Apache::loncommon::end_page({'js_ready' => 1});
   
       my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
       $docopen=~s/^document\.//;
   
       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(
                   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));
   
   //===================== Show list of keywords ====================
     function keywords(formname) {
       var nret = prompt("$js_lt{'keyw'}",formname.keywords.value);
       if (nret==null) return;
       formname.keywords.value = nret;
   
       if (formname.keywords.value != "") {
           formname.refresh.value = "on";
           formname.submit();
       }
       return;
     }
   
   //===================== Script to add keyword(s) ==================
     function getSel() {
       if (document.getSelection) txt = document.getSelection();
       else if (document.selection) txt = document.selection.createRange().text;
       else return;
       if (typeof(txt) != 'string') {
           txt = String(txt);
       }
       var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
       if (cleantxt=="") {
           alert("$js_lt{'plse'}");
           return;
       }
       var nret = prompt("$js_lt{'adds'}",cleantxt);
       if (nret==null) return;
       document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
       if (document.SCORE.keywords.value != "") {
           document.SCORE.refresh.value = "on";
           document.SCORE.submit();
       }
       return;
     }
   
 //====================== Script for keyword highlight options ==============  //====================== Script for keyword highlight options ==============
   function kwhighlight() {    function kwhighlight() {
     var kwclr    = document.SCORE.kwclr.value;      var kwclr    = document.SCORE.kwclr.value;
Line 1598  INNERJS Line 1862  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 1634  INNERJS Line 1907  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();
   }    }
   
 </script>  
 SUBJAVASCRIPT  SUBJAVASCRIPT
 }  }
   
Line 1677  sub get_increment { Line 1947  sub get_increment {
     return $increment;      return $increment;
 }  }
   
   sub gradeBox_start {
       return (
           &Apache::loncommon::start_data_table()
          .&Apache::loncommon::start_data_table_header_row()
          .'<th>'.&mt('Part').'</th>'
          .'<th>'.&mt('Points').'</th>'
          .'<th>&nbsp;</th>'
          .'<th>'.&mt('Assign Grade').'</th>'
          .'<th>'.&mt('Weight').'</th>'
          .'<th>'.&mt('Grade Status').'</th>'
          .&Apache::loncommon::end_data_table_header_row()
       );
   }
   
   sub gradeBox_end {
       return (
           &Apache::loncommon::end_data_table()
       );
   }
 #--- displays the grading box, used in essay type problem and grading by page/sequence  #--- displays the grading box, used in essay type problem and grading by page/sequence
 sub gradeBox {  sub gradeBox {
     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;      my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
Line 1688  sub gradeBox { Line 1977  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 1696  sub gradeBox { Line 1985  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.='<table border="0"><tr>';      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 1704  sub gradeBox { Line 1993  sub gradeBox {
     my $radio.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across      my $radio.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
     while ($thisweight<=$wgt) {      while ($thisweight<=$wgt) {
  $radio.= '<td><span class="LC_nobreak"><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.   $radio.= '<td><span class="LC_nobreak"><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
     'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.          'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
     $thisweight.')" value="'.$thisweight.'" '.      $thisweight.')" value="'.$thisweight.'" '.
     ($score eq $thisweight ? 'checked="checked"':'').' /> '.$thisweight."</label></span></td>\n";      ($score eq $thisweight ? 'checked="checked"':'').' /> '.$thisweight."</label></span></td>\n";
  $radio.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');   $radio.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
Line 1715  sub gradeBox { Line 2004  sub gradeBox {
   
     my $line.='<input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.      my $line.='<input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
  ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.   ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
  'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.   'onchange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
  $wgt.')" /></td>'."\n";   $wgt.')" /></td>'."\n";
     $line.='<td>/'.$wgt.' '.$wgtmsg.      $line.='<td>/'.$wgt.' '.$wgtmsg.
  ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').   ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
  ' </td><td><b>'.&mt('Grade Status').':</b>'."\n";   ' </td>'."\n";
     $line.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.      $line.='<td><select name="GD_SEL'.$counter.'_'.$partid.'" '.
  'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";   'onchange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {      if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
  $line.='<option></option>'.   $line.='<option></option>'.
     '<option value="excused" selected="selected">'.&mt('excused').'</option>';      '<option value="excused" selected="selected">'.&mt('excused').'</option>';
Line 1732  sub gradeBox { Line 2021  sub gradeBox {
     $line.='<option value="reset status">'.&mt('reset status').'</option></select>'."\n";      $line.='<option value="reset status">'.&mt('reset status').'</option></select>'."\n";
   
   
  #&mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line);  
     $result .=       $result .= 
     '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.      '<td>'.$data_WGT.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';
           $result.=&Apache::loncommon::end_data_table_row();
     $result.='</tr></table>'."\n";      $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 1745  sub gradeBox { Line 2033  sub gradeBox {
         $$record{'resource.'.$partid.'.tries'}.'" />'."\n".          $$record{'resource.'.$partid.'.tries'}.'" />'."\n".
         '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.          '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.
         $aggtries.'" />'."\n";          $aggtries.'" />'."\n";
     $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record);      my $res_error;
       $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error);
       $result.='</td>'.&Apache::loncommon::end_data_table_row();
       if ($res_error) {
           return &navmap_errormsg();
       }
     return $result;      return $result;
 }  }
   
 sub handback_box {  sub handback_box {
     my ($symb,$uname,$udom,$counter,$partid,$record) = @_;      my ($symb,$uname,$udom,$counter,$partid,$record,$res_error_pointer) = @_;
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,$res_error_pointer);
       return unless ($numessay);
     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 1765  sub handback_box { Line 2059  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) = &file_name_version_ext($file_disp);
            $file_disp = "$name.$ext";             $file_disp = "$name.$ext";
Line 1775  sub handback_box { Line 2070  sub handback_box {
            $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 1812  sub show_problem { Line 2110  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_grade_show_problem_header">'.          '<div class="LC_Box">'
  &mt('View of the problem').         .'<h3 class="LC_hcell">'.$renderheading.'</h3>'
  '</div><div class="LC_grade_show_problem_problem">'.         .$rendered
  $rendered.         .'</div>';
  '</div>';  
     $companswer=      $companswer=
  '<div class="LC_grade_show_problem_header">'.          '<div class="LC_Box">'
  &mt('Correct answer').         .'<h3 class="LC_hcell">'.$answerheading.'</h3>'
  '</div><div class="LC_grade_show_problem_problem">'.         .$companswer
  $companswer.         .'</div>';
  '</div>';  
     my $result;      my $result;
     if ($mode eq 'both') {      if ($mode eq 'both') {
  $result=$rendered.$companswer;          $result=$rendered.$companswer;
     } elsif ($mode eq 'text') {      } elsif ($mode eq 'text') {
  $result=$rendered;          $result=$rendered;
     } elsif ($mode eq 'answer') {      } elsif ($mode eq 'answer') {
  $result=$companswer;          $result=$companswer;
     }      }
     $result='<div class="LC_grade_show_problem">'.$result.'</div>';  
     return $result;      return $result;
 }  }
   
 sub files_exist {  sub files_exist {
     my ($r, $symb) = @_;      my ($r, $symb) = @_;
     my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');
   
     foreach my $student (@students) {      foreach my $student (@students) {
         my ($uname,$udom,$fullname) = split(/:/,$student);          my ($uname,$udom,$fullname) = split(/:/,$student);
         my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},          my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
Line 1858  sub files_exist { Line 2165  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 1870  sub download_all_link { Line 2181  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 ''; }
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay,$numdropbox) =
           &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&mt('An error occurred retrieving response types'));
           return;
       }
       unless ($numessay) {
           $request->print(&mt('No essayresponse items found'));
           return;
       }
       my @chosenparts = &Apache::loncommon::get_env_multiple('form.vPart');
       if (@chosenparts) {
           $request->print(&showResourceInfo($symb,$partlist,$responseType,
                                             undef,undef,1));
       }
       if ($numessay) {
           my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
           my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
           my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
           (undef,undef,my $fullname) = &getclasslist($getsec,1,$getgroup,$symb,$submitonly,1);
           if (ref($fullname) eq 'HASH') {
               my @students = map { $_.':'.$fullname->{$_} } (keys(%{$fullname}));
               if (@students) {
                   @{$env{'form.stuinfo'}} = @students;
                   if ($numdropbox) {
                       &download_all_link($request,$symb);
                   } else {
                       $request->print(&mt('No essayrespose items with dropbox found'));
                   }
   # FIXME Need a mechanism to download essays, i.e., if $numessay > $numdropbox
   # Needs to omit user's identity if resource instance is for an anonymous survey.
               } else {
                   $request->print(&mt('No students match the criteria you selected'));
               }
           } else {
               $request->print(&mt('Could not retrieve student information'));
           }
       } else {
           $request->print(&mt('No essayresponse items found'));
       }
       return;
 }  }
   
 sub build_section_inputs {  sub build_section_inputs {
Line 1888  sub build_section_inputs { Line 2246  sub build_section_inputs {
   
 # --------------------------- show submissions of a student, option to grade   # --------------------------- show submissions of a student, option to grade 
 sub submission {  sub submission {
     my ($request,$counter,$total) = @_;      my ($request,$counter,$total,$symb,$divforres,$calledby) = @_;
     my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});      my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});
     $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';      $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
     my $symb = &get_symb($request);   
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
       my $probtitle=&Apache::lonnet::gettitle($symb);
       my ($essayurl,%coursedesc_by_cid);
   
     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.').
  $request->print(&show_grading_menu_form($symb));              ' '.&mt('([_1] in section [_2] in course id [_3])',
                           $uname.':'.$udom,$usec,$env{'request.course.id'}).
               '</span>');
  return;   return;
     }      }
   
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) =
           &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           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'; }      if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
     if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }      if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
       if (($numessay) && ($calledby eq 'submission') && (!exists($env{'form.compmsg'}))) {
           $env{'form.compmsg'} = 1;
       }
     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) {
           my @chosenparts = &Apache::loncommon::get_env_multiple('form.vPart');
           if (@chosenparts) {
               $request->print(&showResourceInfo($symb,$partlist,$responseType,'gradesub'));
           } elsif ($divforres) {
               $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
           } else {
               $request->print('<br clear="all" />');
           }
  &sub_page_js($request);   &sub_page_js($request);
  &sub_page_kw_js($request) if ($env{'form.handgrade'} eq 'yes');          &sub_grademessage_js($request) if ($env{'form.compmsg'});
  $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ?    &sub_page_kw_js($request) if ($numessay);
     &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};  
  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>'."\n".  
  '<h4>&nbsp;'.&mt('<b>Resource: </b> [_1]',$env{'form.probTitle'}).'</h4>'."\n");  
   
  # 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 1940  sub submission { Line 2314  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   
         # 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 '' && $numessay) || ($env{'form.compmsg'})) {
     %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'});
    }
    # kwclr is the only variable that is guaranteed not to be blank
    # if this subroutine has been called once.
    if ($env{'form.kwclr'} eq '' && $numessay) {
     my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};      my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
     $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';      $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
     $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';      $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
     $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';      $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
     $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';      $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
     $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ?    }
  $keyhash{$symb.'_subject'} : $env{'form.probTitle'};   if ($env{'form.compmsg'}) {
       $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ?
    $keyhash{$symb.'_subject'} : $probtitle;
     $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';      $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
  }   }
   
  my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};   my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};
  my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));   my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
  $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".   $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".
  '<input type="hidden" name="command"    value="handgrade" />'."\n".   '<input type="hidden" name="command"    value="handgrade" />'."\n".
  '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".  
  '<input type="hidden" name="Status"     value="'.$stu_status.'" />'."\n".   '<input type="hidden" name="Status"     value="'.$stu_status.'" />'."\n".
  '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".   '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".
  '<input type="hidden" name="probTitle"  value="'.$env{'form.probTitle'}.'" />'."\n".  
  '<input type="hidden" name="refresh"    value="off" />'."\n".   '<input type="hidden" name="refresh"    value="off" />'."\n".
  '<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".
    '<input type="hidden" name="compmsg"    value="'.$env{'form.compmsg'}.'" />'."\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.compmsg'}) {
       $request->print('<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".
       '<input type="hidden" name="shownSub" value="0" />'."\n".
       '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");
    }
    if ($numessay) {
     $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".
     '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n".      '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n");
     '<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".  
     '<input type="hidden" name="shownSub" value="0" />'."\n".  
     '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");  
     foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {  
  $request->print('<input type="hidden" name="vPart" value="'.$partid.'" />'."\n");  
     }  
  }   }
   
  my ($cts,$prnmsg) = (1,'');   my ($cts,$prnmsg) = (1,'');
  while ($cts <= $env{'form.savemsgN'}) {   while ($cts <= $env{'form.savemsgN'}) {
     $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.      $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
Line 2003  sub submission { Line 2377  sub submission {
  }   }
  $request->print($prnmsg);   $request->print($prnmsg);
   
  if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') {   if ($numessay) {
   
               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
 #  #
             my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);              (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
     my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);              if ($essayurl eq 'lib/templates/simpleproblem.problem') {
     $apath=&escape($apath);                  my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     $apath=~s/\W/\_/gs;                  my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
     %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);                  if ($cdom ne '' && $cnum ne '') {
                       my ($map,$id,$res) = &Apache::lonnet::decode_symb($symb);
                       if ($map =~ m{^\Quploaded/$cdom/$cnum/\E(default(?:|_\d+)\.(?:sequence|page))$}) {
                           my $apath = $1.'_'.$id;
                           $apath=~s/\W/\_/gs;
                           &init_old_essays($symb,$apath,$cdom,$cnum);
                       }
                   }
               } else {
           my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
           $apath=&escape($apath);
           $apath=~s/\W/\_/gs;
                   &init_old_essays($symb,$apath,$adom,$aname);
               }
         }          }
     }      }
   
 # This is where output for one specific student would start  # This is where output for one specific student would start
     my $add_class = ($counter%2) ? 'LC_grade_show_user_odd_row' : '';      my $add_class = ($counter%2) ? ' LC_grade_show_user_odd_row' : '';
     $request->print("\n\n".      $request->print(
                     '<div class="LC_grade_show_user '.$add_class.'">'.          "\n\n"
     '<div class="LC_grade_user_name">'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'</div>'.         .'<div class="LC_grade_show_user'.$add_class.'">'
     '<div class="LC_grade_show_user_body">'."\n");         .'<h2>'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'</h2>'
          ."\n"
       );
   
       # Show additional functions if allowed
       if ($perm{'vgr'}) {
           $request->print(
               &Apache::loncommon::track_student_link(
                   'View recent activity',
                   $uname,$udom,'check')
              .' '
           );
       }
       if ($perm{'opa'}) {
           $request->print(
               &Apache::loncommon::pprmlink(
                   &mt('Set/Change parameters'),
                   $uname,$udom,$symb,'check'));
       }
   
       # Show Problem
     if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {      if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {
  my $mode;   my $mode;
  if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {   if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {
Line 2046  KEYWORDS Line 2465  KEYWORDS
     }      }
   
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
     my ($partlist,$handgrade,$responseType) = &response_type($symb);  
   
     # Display student info      # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));      $request->print(($counter == 0 ? '' : '<br />'));
     my $result='<div class="LC_grade_submissions">';  
       
     $result.='<div class="LC_grade_submissions_header">';  
     $result.= &mt('Submissions');  
     $result.='<input type="hidden" name="name'.$counter.  
  '" value="'.$env{'form.fullname'}.'" />'."\n";  
     if ($env{'form.handgrade'} eq 'no') {  
  $result.='<span class="LC_grade_check_note">'.  
     &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)."</span>\n";  
   
       my $result='<div class="LC_Box">'
                 .'<h3 class="LC_hcell">'.&mt('Submissions').'</h3>';
       $result.='<input type="hidden" name="name'.$counter.
                '" value="'.$env{'form.fullname'}.'" />'."\n";
       if ($numresp > $numessay) {
           $result.='<p class="LC_info">'
                   .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
                   ."</p>\n";
     }      }
   
       # If any part of the problem is an essayresponse, 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 ($numessay) {
  (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");
     $request->print('</div>'."\n");  
     # print student answer/submission      # print student answer/submission
     # Options are (1) Handgaded submission only      # Options are (1) Last submission only
     #             (2) Last submission, includes submission that is not handgraded       #             (2) Last submission (with detailed information for that submission)
     #                  (for multi-response type part)      #             (3) All transactions (by date)
     #             (3) Last submission plus the parts info      #             (4) The whole record (with detailed information for all transactions)
     #             (4) The whole record for this student  
     if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {  
  my ($string,$timestamp)= &get_last_submission(\%record);  
   
  my $lastsubonly;  
   
  if ($$timestamp eq '') {      my ($string,$timestamp)= &get_last_submission(\%record);
     $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>';   
  } else {  
     $lastsubonly = '<div class="LC_grade_submissions_body"> <b>Date Submitted:</b> '.$$timestamp."\n";  
   
     my %seenparts;      my $lastsubonly;
     my @part_response_id = &flatten_responseType($responseType);  
     foreach my $part (@part_response_id) {      if ($$timestamp eq '') {
  next if ($env{'form.lastSub'} eq 'hdgrade'           $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>'; 
  && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');      } else {
           $lastsubonly =
  my ($partid,$respid) = @{ $part };              '<div class="LC_grade_submissions_body">'
  my $display_part=&get_display_part($partid,$symb);             .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";
  if ($env{"form.$uname:$udom:$partid:submitted_by"}) {  
     if (exists($seenparts{$partid})) { next; }   my %seenparts;
     $seenparts{$partid}=1;   my @part_response_id = &flatten_responseType($responseType);
     my $submitby='<b>Part:</b> '.$display_part.   foreach my $part (@part_response_id) {
  ' <b>Collaborative submission by:</b> '.      my ($partid,$respid) = @{ $part };
  '<a href="javascript:viewSubmitter(\''.      my $display_part=&get_display_part($partid,$symb);
  $env{"form.$uname:$udom:$partid:submitted_by"}.      if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
  '\');" target="_self">'.   if (exists($seenparts{$partid})) { next; }
  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';   $seenparts{$partid}=1;
     $request->print($submitby);                  $request->print(
     next;                      '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
  }                      ' <b>'.&mt('Collaborative submission by: [_1]',
  my $responsetype = $responseType->{$partid}->{$respid};                                 '<a href="javascript:viewSubmitter(\''.
  if (!exists($record{"resource.$partid.$respid.submission"})) {                                 $env{"form.$uname:$udom:$partid:submitted_by"}.
     $lastsubonly.="\n".'<div class="LC_grade_submission_part"><b>Part:</b> '.                                 '\');" target="_self">'.
  $display_part.' <span class="LC_internal_info">( ID '.$respid.                                 $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
  ' )</span>&nbsp; &nbsp;'.                      '<br />');
  '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';   next;
     next;      }
  }      my $responsetype = $responseType->{$partid}->{$respid};
  foreach my $submission (@$string) {      if (!exists($record{"resource.$partid.$respid.submission"})) {
     my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);                  $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
     if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }                      '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
     my ($ressub,$subval) = split(/:/,$submission,2);                      ' <span class="LC_internal_info">'.
     # Similarity check                      '('.&mt('Response ID: [_1]',$respid).')'.
     my $similar='';                      '</span>&nbsp; &nbsp;'.
     if($env{'form.checkPlag'}){              '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
  my ($oname,$odom,$ocrsid,$oessay,$osim)=   next;
     &most_similar($uname,$udom,$subval,\%old_essays);      }
  if ($osim) {      foreach my $submission (@$string) {
     $osim=int($osim*100.0);   my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
     my %old_course_desc =    if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
  &Apache::lonnet::coursedescription($ocrsid,   my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
    {'one_time' => 1});   # Similarity check
                   my $similar='';
     $similar="<hr /><h3><span class=\"LC_warning\">".                  my ($type,$trial,$rndseed);
  &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',                  if ($hide eq 'rand') {
     $osim,                      $type = 'randomizetry';
     &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',                      $trial = $record{"resource.$partid.tries"};
     $old_course_desc{'description'},                      $rndseed = $record{"resource.$partid.rndseed"};
     $old_course_desc{'num'},                  }
     $old_course_desc{'domain'}).   if ($env{'form.checkPlag'}) {
  '</span></h3><blockquote><i>'.      my ($oname,$odom,$ocrsid,$oessay,$osim)=
  &keywords_highlight($oessay).          &most_similar($uname,$udom,$symb,$subval);
  '</i></blockquote><hr />';      if ($osim) {
  }          $osim=int($osim*100.0);
                           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 />';
                               if ($essayurl eq 'lib/templates/simpleproblem.problem') {
                                   $similar .= '<h3><span class="LC_warning">'.
                                               &mt('Essay is [_1]% similar to an essay by [_2]',
                                                   $osim,
                                                   &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                               '</span></h3>';
                               } elsif ($ocrsid ne '') {
                                   my %old_course_desc;
                                   if (ref($coursedesc_by_cid{$ocrsid}) eq 'HASH') {
                                       %old_course_desc = %{$coursedesc_by_cid{$ocrsid}};
                                   } else {
                                       my $args;
                                       if ($ocrsid ne $env{'request.course.id'}) {
                                           $args = {'one_time' => 1};
                                       }
                                       %old_course_desc =
                                           &Apache::lonnet::coursedescription($ocrsid,$args);
                                       $coursedesc_by_cid{$ocrsid} = \%old_course_desc;
                                   }
                                   $similar .=
                                       '<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{'num'},
           $old_course_desc{'domain'}).
       '</span></h3>';
                               } else {
                                   $similar .=
                                       '<h3><span class="LC_warning">'.
                                       &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',
                                           $osim,
                                           &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                       '</span></h3>';
                               }
                               $similar .= '<blockquote><i>'.
                                           &keywords_highlight($oessay).
                                           '</i></blockquote><hr />';
           }
                       }
                   }
    my $order=&get_order($partid,$respid,$symb,$uname,$udom,
                                        undef,$type,$trial,$rndseed);
                   if (($env{'form.lastSub'} eq 'lastonly') ||
                       ($env{'form.lastSub'} eq 'datesub')  ||
                       ($env{'form.lastSub'} =~ /^(last|all)$/)) {
       my $display_part=&get_display_part($partid,$symb);
                       $lastsubonly.='<div class="LC_grade_submission_part">'.
                           '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                           ' <span class="LC_internal_info">'.
                           '('.&mt('Response ID: [_1]',$respid).')'.
                           '</span>&nbsp; &nbsp;';
       my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
       if (@$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 {
                                   $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
                               }
                               $lastsubonly .= '</span>';
                               foreach my $file (@$files) {
                                   &Apache::lonnet::allowuploaded('/adm/grades',$file);
                                   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
                               }
                           }
    $lastsubonly.='<br />';
     }      }
     my $order=&get_order($partid,$respid,$symb,$uname,$udom);                      if ($hide eq 'anon') {
     if ($env{'form.lastSub'} eq 'lastonly' ||                           $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; 
  ($env{'form.lastSub'} eq 'hdgrade' &&                       } else {
  $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {                          $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
  my $display_part=&get_display_part($partid,$symb);                          if ($draft) {
  $lastsubonly.='<div class="LC_grade_submission_part"><b>Part:</b> '.                              $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
     $display_part.' <span class="LC_internal_info">( ID '.$respid.                          }
     ' )</span>&nbsp; &nbsp;';                          $subval =
  my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);  
  if (@$files) {  
     $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />';  
     my $file_counter = 0;  
     foreach my $file (@$files) {  
         $file_counter++;  
  &Apache::lonnet::allowuploaded('/adm/grades',$file);  
  $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" /> '.$file.'</a>';  
     }  
     $lastsubonly.='<br />';  
  }  
  $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.  
     &cleanRecord($subval,$responsetype,$symb,$partid,      &cleanRecord($subval,$responsetype,$symb,$partid,
  $respid,\%record,$order,undef,$uname,$udom);   $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
  if ($similar) {$lastsubonly.="<br /><br />$similar\n";}                          if ($responsetype eq 'essay') {
  $lastsubonly.='</div>';                              $subval =~ s{\n}{<br />}g;
     }                          }
                           $lastsubonly.=$subval."\n";
                       }
                       if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
       $lastsubonly.='</div>';
  }   }
     }      }
     $lastsubonly.='</div>'."\n";  
  }   }
  $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);
       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)$/) {      }
       if ($env{'form.lastSub'} =~ /^(last|all)$/) {
           my $identifier = (&canmodify($usec)? $counter : '');
  $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,   $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\',\''          return;
     .$counter.'\');" target="_self" /> &nbsp;'."\n" if (&canmodify($usec));  
  $toGrade.='</div>'."\n";  
  if (($env{'form.command'} eq 'submission') ||   
     ($env{'form.command'} eq 'processGroup' && $counter == $total)) {  
     $toGrade.='</form>'.&show_grading_menu_form($symb);   
  }  
  $request->print($toGrade);  
  return;  
     } else {      } else {
  $request->print('</div>'."\n");   $request->print('</div>'."\n");
     }      }
   
     # essay grading message center      # grading message center
     if ($env{'form.handgrade'} eq 'yes') {  
  my $result='<div class="LC_grade_message_center">';      if ($env{'form.compmsg'}) {
               my $result='<div class="LC_Box">'.
  $result.='<div class="LC_grade_message_center_header">'.                     '<h3 class="LC_hcell">'.&mt('Send Message').'</h3>'.
     &mt('Send Message').'</div><div class="LC_grade_message_center_body">';                     '<div class="LC_grade_message_center_body">';
  my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});          my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
  my $msgfor = $givenn.' '.$lastname;          my $msgfor = $givenn.' '.$lastname;
  if (scalar(@$col_fullnames) > 0) {          if (scalar(@$col_fullnames) > 0) {
     my $lastone = pop(@$col_fullnames);              my $lastone = pop(@$col_fullnames);
     $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';              $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';
  }          }
  $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript          $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
  $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".          $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
     '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";                   '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
  $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.           '&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>';           '</div></div>';
  $request->print($result);          $request->print($result);
     }      }
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id = &flatten_responseType($responseType);
     $request->print('<div class="LC_grade_assign">'.      $request->print(
               '<div class="LC_Box">'
     '<div class="LC_grade_assign_header">'.         .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'
     &mt('Assign Grades').'</div>'.      );
     '<div class="LC_grade_assign_body">');      $request->print(&gradeBox_start());
     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} > 0);   next if ($seen{$partid} > 0);
  $seen{$partid}++;   $seen{$partid}++;
  next if ($$handgrade{$part_resp} ne 'yes'   
  && $env{'form.lastSub'} eq 'hdgrade');  
  push(@partlist,$partid);   push(@partlist,$partid);
  push(@gradePartRespid,$partid.'.'.$respid);   push(@gradePartRespid,$partid.'.'.$respid);
  $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));   $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
     }      }
     $request->print('</div></div>');      $request->print(&gradeBox_end()); # </div>
       $request->print('</div>');
   
     $request->print('<div class="LC_grade_info_links">');      $request->print('<div class="LC_grade_info_links">');
     if ($perm{'vgr'}) {  
  $request->print(  
     &Apache::loncommon::track_student_link(&mt('View recent activity'),  
    $uname,$udom,'check'));  
     }  
     if ($perm{'opa'}) {  
  $request->print(  
     &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),  
  $uname,$udom,$symb,'check'));  
     }  
     $request->print('</div>');      $request->print('</div>');
   
     $result='<input type="hidden" name="partlist'.$counter.      $result='<input type="hidden" name="partlist'.$counter.
Line 2280  KEYWORDS Line 2740  KEYWORDS
   
 # Done with printing info for one student  # Done with printing info for one student
   
     $request->print('</div>');#LC_grade_show_user_body  
     $request->print('</div>');#LC_grade_show_user      $request->print('</div>');#LC_grade_show_user
   
   
     # print end of form      # print end of form
     if ($counter == $total) {      if ($counter == $total) {
  my $endform='<table border="0"><tr><td>'."\n";          my $endform='<br /><hr /><table border="0"><tr><td>'."\n";
  $endform.='<input type="button" value="'.&mt('Save &amp; Next').'" '.   $endform.='<input type="button" value="'.&mt('Save &amp; Next').'" '.
     'onClick="javascript:checksubmit(this.form,\'Save & Next\','.      'onclick="javascript:checksubmit(this.form,\'Save & Next\','.
     $total.','.scalar(@partlist).');" target="_self" /> &nbsp;'."\n";      $total.','.scalar(@partlist).');" target="_self" /> &nbsp;'."\n";
  my $ntstu ='<select name="NTSTU">'.   my $ntstu ='<select name="NTSTU">'.
     '<option>1</option><option>2</option>'.      '<option>1</option><option>2</option>'.
Line 2296  KEYWORDS Line 2755  KEYWORDS
     '<option>7</option><option>10</option></select>'."\n";      '<option>7</option><option>10</option></select>'."\n";
  my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');   my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
  $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;   $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
  $endform.=&mt('[quant,_1,student]',$ntstu);          $endform.=&mt('[_1]student(s)',$ntstu);
  $endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.   $endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.
     'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".      'onclick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
     '<input type="button" value="'.&mt('Next').'" '.      '<input type="button" value="'.&mt('Next').'" '.
     'onClick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';      'onclick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';
  $endform.=&mt('(Next and Previous (student) do not save the scores.)')."\n" ;          $endform.='<span class="LC_warning">'.
                     &mt('(Next and Previous (student) do not save the scores.)').
                     '</span>'."\n" ;
         $endform.="<input type='hidden' value='".&get_increment().          $endform.="<input type='hidden' value='".&get_increment().
             "' name='increment' />";              "' name='increment' />";
  $endform.='</td></tr></table></form>';   $endform.='</td></tr></table></form>';
  $endform.=&show_grading_menu_form($symb);  
  $request->print($endform);   $request->print($endform);
     }      }
     return '';      return '';
Line 2324  sub check_collaborators { Line 2784  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 2340  sub check_collaborators { Line 2800  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 2370  sub check_collaborators { Line 2830  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)=@_;
     my (@string,$timestamp);      my (@string,$timestamp,%lasthidden);
     if ($$returnhash{'version'}) {      if ($$returnhash{'version'}) {
  my %lasthash=();   my %lasthash=();
  my ($version);   my ($version);
Line 2382  sub get_last_submission { Line 2842  sub get_last_submission {
     &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});      &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
     }      }
  }   }
           my (%typeparts,%randombytry);
           my $showsurv = 
               &Apache::lonnet::allowed('vas',$env{'request.course.id'});
           foreach my $key (sort(keys(%lasthash))) {
               if ($key =~ /\.type$/) {
                   if (($lasthash{$key} eq 'anonsurvey') || 
                       ($lasthash{$key} eq 'anonsurveycred') ||
                       ($lasthash{$key} eq 'randomizetry')) {
                       my ($ign,@parts) = split(/\./,$key);
                       pop(@parts);
                       my $id = join('.',@parts);
                       if ($lasthash{$key} eq 'randomizetry') {
                           $randombytry{$ign.'.'.$id} = $lasthash{$key};
                       } else {
                           unless ($showsurv) {
                               $typeparts{$ign.'.'.$id} = $lasthash{$key};
                           }
                       }
                       delete($lasthash{$key});
                   }
               }
           }
           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;
               if (@hidden) {
                   foreach my $id (@hidden) {
                       if ($key =~ /^\Q$id\E/) {
                           $hide = 'anon';
                           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, $draft.$lasthash{$key}));                  ref($lasthash{$key}) eq 'ARRAY' ?
                       join(',', @{$lasthash{$key}}) : $lasthash{$key}) ));
  }   }
     }      }
     if (!@string) {      if (!@string) {
Line 2411  sub keywords_highlight { Line 2914  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) = shift;      my ($request,$symb) = @_;
     my $symb   = &get_symb($request);  
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     my $button = $env{'form.gradeOpt'};      my $button = $env{'form.gradeOpt'};
     my $ngrade = $env{'form.NCT'};      my $ngrade = $env{'form.NCT'};
Line 2426  sub processHandGrade { Line 3109  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 2454  sub processHandGrade { Line 3147  sub processHandGrade {
  if ($env{'form.withgrades'.$ctr}) {   if ($env{'form.withgrades'.$ctr}) {
     $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;      $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
     $messagetail = " for <a href=\"".      $messagetail = " for <a href=\"".
                    $feedurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";                     $feedurl."?symb=$showsymb\">$restitle</a>";
  }   }
  $msgstatus =    $msgstatus = 
                     &Apache::lonmsg::user_normal_msg($uname,$udom,$subject,                      &Apache::lonmsg::user_normal_msg($uname,$udom,$subject,
Line 2463  sub processHandGrade { Line 3156  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 2482  sub processHandGrade { Line 3175  sub processHandGrade {
       $udom);        $udom);
     if ($env{'form.withgrades'.$ctr}) {      if ($env{'form.withgrades'.$ctr}) {
  $messagetail = " for <a href=\"".   $messagetail = " for <a href=\"".
                                     $baseurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";                                      $baseurl."?symb=$showsymb\">$restitle</a>";
     }      }
     $msgstatus =       $msgstatus = 
  &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);   &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
Line 2494  sub processHandGrade { Line 3187  sub processHandGrade {
  }   }
     }      }
   
     if ($env{'form.handgrade'} eq 'yes') {      my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           return;
       }
   
       my %keyhash = ();
       if ($numessay) {
  # 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 = ();  
  $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;   $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
  $env{'form.keywords'}           =~ s/^\s+|\s+$//;   $env{'form.keywords'}           =~ s/^\s+|\s+$//g;
  my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));   my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
  $env{'form.keywords'} = join(' ',@keywords);   $env{'form.keywords'} = join(' ',@keywords);
  $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};   $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};
Line 2507  sub processHandGrade { Line 3207  sub processHandGrade {
  $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};   $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};
  $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};   $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};
  $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};   $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
       }
   
       if ($env{'form.compmsg'}) {
  # message center - Order of message gets changed. Blank line is eliminated.   # message center - Order of message gets changed. Blank line is eliminated.
  # New messages are saved in env for the next student.   # New messages are saved in env for the next student.
  # All messages are saved in nohist_handgrade.db   # All messages are saved in nohist_handgrade.db
Line 2530  sub processHandGrade { Line 3232  sub processHandGrade {
  }   }
  $env{'form.savemsgN'} = --$idx;   $env{'form.savemsgN'} = --$idx;
  $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};   $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
       }
       if (($numessay) || ($env{'form.compmsg'})) {
  my $putresult = &Apache::lonnet::put   my $putresult = &Apache::lonnet::put
     ('nohist_handgrade',\%keyhash,$cdom,$cnum);      ('nohist_handgrade',\%keyhash,$cdom,$cnum);
     }      }
   
     # Called by Save & Refresh from Highlight Attribute Window      # Called by Save & Refresh from Highlight Attribute Window
     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');      my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
     if ($env{'form.refresh'} eq 'on') {      if ($env{'form.refresh'} eq 'on') {
Line 2547  sub processHandGrade { Line 3252  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 2593  sub processHandGrade { Line 3288  sub processHandGrade {
     }      }
     $ctr = 0;      $ctr = 0;
     @parsedlist = reverse @parsedlist if ($button eq 'Previous');      @parsedlist = reverse @parsedlist if ($button eq 'Previous');
     my ($partlist) = &response_type($symb);  
     foreach my $student (@parsedlist) {      foreach my $student (@parsedlist) {
  my $submitonly=$env{'form.submitonly'};   my $submitonly=$env{'form.submitonly'};
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
Line 2639  sub processHandGrade { Line 3333  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";  
  $the_end.=&show_grading_menu_form($symb);  
  $request->print($the_end);   $request->print($the_end);
     }      }
     return '';      return '';
Line 2662  sub saveHandGrade { Line 3353  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 2767  sub saveHandGrade { Line 3463  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 2791  sub check_and_remove_from_queue { Line 3517  sub check_and_remove_from_queue {
 sub handback_files {  sub handback_files {
     my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;      my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;
     my $portfolio_root = '/userfiles/portfolio';      my $portfolio_root = '/userfiles/portfolio';
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my $res_error;
       my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print('<br />'.&navmap_errormsg().'<br />');
           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)) {
               for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {
                 # if multiple files are uploaded names will be 'returndoc2','returndoc3'                  # if multiple files are uploaded names will be 'returndoc2','returndoc3'
                 my $file_counter = 1;   if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
  my $file_msg;                      my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};
                 while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {  
                     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);          &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 = &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 2958  sub version_portfiles { Line 3704  sub version_portfiles {
                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);                  my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
  my ($answer_name,$answer_ver,$answer_ext) =   my ($answer_name,$answer_ver,$answer_ext) =
     &file_name_version_ext($answer_file);      &file_name_version_ext($answer_file);
                 my $getpropath = 1;                      my $getpropath = 1;
                 my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$directory,$domain,$stu_name,$getpropath);                  my ($dir_list,$listerror) =
                 my $version = &get_next_version($answer_name, $answer_ext, \@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);                  my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);
                 if ($new_answer ne 'problem getting file') {                  if ($new_answer ne 'problem getting file') {
                     push(@versioned_portfiles, $directory.$new_answer);                      push(@versioned_portfiles, $directory.$new_answer);
Line 2979  sub version_portfiles { Line 3727  sub version_portfiles {
 sub get_next_version {  sub get_next_version {
     my ($answer_name, $answer_ext, $dir_list) = @_;      my ($answer_name, $answer_ext, $dir_list) = @_;
     my $version;      my $version;
     foreach my $row (@$dir_list) {      if (ref($dir_list) eq 'ARRAY') {
         my ($file) = split(/\&/,$row,2);          foreach my $row (@{$dir_list}) {
         my ($file_name,$file_version,$file_ext) =              my ($file) = split(/\&/,$row,2);
     &file_name_version_ext($file);              my ($file_name,$file_version,$file_ext) =
         if (($file_name eq $answer_name) &&           &file_name_version_ext($file);
     ($file_ext eq $answer_ext)) {              if (($file_name eq $answer_name) && 
                 # gets here if filename and extension match, regardless of version          ($file_ext eq $answer_ext)) {
                   # gets here if filename and extension match, 
                   # regardless of version
                 if ($file_version ne '') {                  if ($file_version ne '') {
                 # a versioned file is found  so save it for later                      # a versioned file is found  so save it for later
                 if ($file_version > $version) {                      if ($file_version > $version) {
     $version = $file_version;          $version = $file_version;
                       }
         }          }
             }              }
         }          }
     }       }
     $version ++;      $version ++;
     return($version);      return($version);
 }  }
Line 3040  sub viewgrades_js { Line 3791  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 = ');
     $request->print(<<VIEWJAVASCRIPT);      &js_escape(\$alertmsg);
 <script type="text/javascript" language="javascript">      $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];
  var textbox = document.classgrade["TEXTVAL_"+partid];   var textbox = document.classgrade["TEXTVAL_"+partid];
Line 3202  sub viewgrades_js { Line 3953  sub viewgrades_js {
  }   }
     }      }
   
 </script>  
 VIEWJAVASCRIPT  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) = shift;      my ($request,$symb) = @_;
     &viewgrades_js($request);      &viewgrades_js($request);
   
     my ($symb) = &get_symb($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, 
     #thus invalidate the cache      #thus invalidate the cache
     &Apache::lonnet::devalidatecourseresdata(      &Apache::lonnet::devalidatecourseresdata(
Line 3220  sub viewgrades { Line 3969  sub viewgrades {
     &Apache::lonnet::clear_EXT_cache_status();      &Apache::lonnet::clear_EXT_cache_status();
   
     my $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>';      my $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>';
     $result.='<h4>'.&mt('<b>Current Resource: </b>[_1]',$env{'form.probTitle'}).'</h4>'."\n";  
   
     #view individual student submission form - called using Javascript viewOneStudent      #view individual student submission form - called using Javascript viewOneStudent
     $result.=&jscriptNform($symb);      $result.=&jscriptNform($symb);
Line 3231  sub viewgrades { Line 3979  sub viewgrades {
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="command" value="editgrades" />'."\n".   '<input type="hidden" name="command" value="editgrades" />'."\n".
  &build_section_inputs().   &build_section_inputs().
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".  
  '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".   '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".
  '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\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);
           }
     }      }
     $result.= '<h3>'.$common_header.'</h3>'.&Apache::loncommon::start_data_table();      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 {
           $result.= '<h3>'.$common_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>'; 
       }
       $result .= &Apache::loncommon::start_data_table();
     #radio buttons/text box for assigning points for a section or class.      #radio buttons/text box for assigning points for a section or class.
     #handles different parts of a problem      #handles different parts of a problem
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my $res_error;
       my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
       if ($res_error) {
           return &navmap_errormsg();
       }
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     my %seen = ();      my %seen = ();
Line 3260  sub viewgrades { Line 4058  sub viewgrades {
  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 $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 3276  sub viewgrades { Line 4073  sub viewgrades {
  }   }
  $radio.='</tr></table>';   $radio.='</tr></table>';
  my $line = '<input type="text" name="TEXTVAL_'.   my $line = '<input type="text" name="TEXTVAL_'.
     $partid.'" size="4" '.'onChange="javascript:writePoint(\''.      $partid.'" size="4" '.'onchange="javascript:writePoint(\''.
  $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.   $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
     $weight{$partid}.' '.&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.'" '.
           'onchange="javascript:writeRadText(\''.$partid.'\','.
  $weight{$partid}.')"> '.   $weight{$partid}.')"> '.
     '<option selected="selected"> </option>'.      '<option selected="selected"> </option>'.
     '<option value="excused">'.&mt('excused').'</option>'.      '<option value="excused">'.&mt('excused').'</option>'.
Line 3294  sub viewgrades { Line 4092  sub viewgrades {
   
  $result.=   $result.=
     &Apache::loncommon::start_data_table_row()."\n".      &Apache::loncommon::start_data_table_row()."\n".
     '<td><b>'.&mt('Part').':</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points').':</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.      '<td><b>'.&mt('Part:').'</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points:').'</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
     &Apache::loncommon::end_data_table_row()."\n";      &Apache::loncommon::end_data_table_row()."\n";
  $ctsparts++;   $ctsparts++;
     }      }
     $result.=&Apache::loncommon::end_data_table()."\n".      $result.=&Apache::loncommon::end_data_table()."\n".
  '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';   '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
     $result.='<input type="button" value="'.&mt('Revert to Default').'" '.      $result.='<input type="button" value="'.&mt('Revert to Default').'" '.
  'onClick="javascript:resetEntry('.$ctsparts.');" />';   'onclick="javascript:resetEntry('.$ctsparts.');" />';
   
     #table listing all the students in a section/class      #table listing all the students in a section/class
     #header of table      #header of table
     $result.= '<h3>'.$specific_header.'</h3>'.      if ($env{'form.submitonly'} eq 'all') { 
               &Apache::loncommon::start_data_table().          $result.= '<h3>'.$specific_header.'</h3>';
       } else {
           $result.= '<h3>'.$specific_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</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";
     my (@parts) = sort(&getpartlist($symb));      my $partserror;
       my (@parts) = sort(&getpartlist($symb,\$partserror));
       if ($partserror) {
           return &navmap_errormsg();
       }
     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) {
Line 3320  sub viewgrades { Line 4126  sub viewgrades {
  if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }   if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
  my ($partid) = &split_part_type($part);   my ($partid) = &split_part_type($part);
         push(@partids,$partid);          push(@partids,$partid);
   #
   # 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 3345  sub viewgrades { Line 4154  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 3354  sub viewgrades { Line 4163  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);
     }      }
     $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 />';
     }      }
     $result.=&show_grading_menu_form($symb);  
     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) = @_;
     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 3413  sub viewstudentgrade { Line 4328  sub viewstudentgrade {
  'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";   'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
     $result.='<input type="text" name="'.      $result.='<input type="text" name="'.
  'GD_'.$student.'_'.$part.'_awarded" '.   'GD_'.$student.'_'.$part.'_awarded" '.
  'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.                  'onchange="javascript:changeSelect(\''.$part.'\',\''.$student.
  '\')" value="'.$pts.'" size="4" /></td>'."\n";   '\')" value="'.$pts.'" size="4" /></td>'."\n";
  } elsif ($type eq 'solved') {   } elsif ($type eq 'solved') {
     my ($status,$foo)=split(/_/,$score,2);      my ($status,$foo)=split(/_/,$score,2);
Line 3422  sub viewstudentgrade { Line 4337  sub viewstudentgrade {
  $part.'_solved_s" value="'.$status.'" />'."\n";   $part.'_solved_s" value="'.$status.'" />'."\n";
     $result.='&nbsp;<select name="'.      $result.='&nbsp;<select name="'.
  'GD_'.$student.'_'.$part.'_solved" '.   'GD_'.$student.'_'.$part.'_solved" '.
  'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";                  'onchange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
     $result.= (($status eq 'excused') ? '<option> </option><option selected="selected" value="excused">'.&mt('excused').'</option>'       $result.= (($status eq 'excused') ? '<option> </option><option selected="selected" value="excused">'.&mt('excused').'</option>' 
  : '<option selected="selected"> </option><option value="excused">'.&mt('excused').'</option>')."\n";   : '<option selected="selected"> </option><option value="excused">'.&mt('excused').'</option>')."\n";
     $result.='<option value="reset status">'.&mt('reset status').'</option>';      $result.='<option value="reset status">'.&mt('reset status').'</option>';
Line 3443  sub viewstudentgrade { Line 4358  sub viewstudentgrade {
 #--- change scores for all the students in a section/class  #--- change scores for all the students in a section/class
 #    record does not get update if unchanged  #    record does not get update if unchanged
 sub editgrades {  sub editgrades {
     my ($request) = @_;      my ($request,$symb) = @_;
   
     my $symb=&get_symb($request);  
     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>';
     $title.='<h4>'.&mt('<b>Current Resource: </b>[_1]',$env{'form.probTitle'}).'</h4>'."\n";      $title.='<h4><b>'.&mt('Section:').'</b> '.$section_display.'</h4>'."\n";
     $title.='<h4>'.&mt('<b>Section: </b>[_1]',$section_display).'</h4>'."\n";  
   
     my $result= &Apache::loncommon::start_data_table().      my $result= &Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
Line 3460  sub editgrades { Line 4373  sub editgrades {
     'incorrect'=>'incorrect_by_override',      'incorrect'=>'incorrect_by_override',
     'excused'  =>'excused',      'excused'  =>'excused',
     'ungraded' =>'ungraded_attempted',      'ungraded' =>'ungraded_attempted',
                       'credited' =>'credit_attempted',
     'nothing'  => '',      'nothing'  => '',
     );      );
     my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');      my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
Line 3469  sub editgrades { Line 4383  sub editgrades {
     my %columns = ();      my %columns = ();
     my ($i,$ctr,$count,$rec_update) = (0,0,0,0);      my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
   
     my (@parts) = sort(&getpartlist($symb));      my $partserror;
       my (@parts) = sort(&getpartlist($symb,\$partserror));
       if ($partserror) {
           return &navmap_errormsg();
       }
     my $header;      my $header;
     while ($ctr < $env{'form.totalparts'}) {      while ($ctr < $env{'form.totalparts'}) {
  my $partid = $env{'form.partid_'.$ctr};   my $partid = $env{'form.partid_'.$ctr};
Line 3478  sub editgrades { Line 4396  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 3494  sub editgrades { Line 4413  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 3509  sub editgrades { Line 4429  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];          my $canmodify = &canmodify($usec);
  if (!&canmodify($usec)) {          my $line = '<td'.($canmodify?'':' colspan="2"').'>'.
     my $numcols=scalar(@partid)*4+2;                     &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
     push(@noupdate,          if (!$canmodify) {
  $line."<td colspan=\"$numcols\"><span class=\"LC_warning\">".              push(@noupdate,
  &mt('Not allowed to modify student')."</span></td></tr>");                   $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".
     next;                   &mt('Not allowed to modify student')."</span></td>");
  }              next;
           }
         my %aggregate = ();          my %aggregate = ();
         my $aggregateflag = 0;          my $aggregateflag = 0;
  $user=~s/:/_/; # colon doen't work in javascript for names   $user=~s/:/_/; # colon doen't work in javascript for names
Line 3637  sub editgrades { Line 4557  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 3651  sub editgrades { Line 4570  sub editgrades {
  &Apache::loncommon::end_data_table_row();   &Apache::loncommon::end_data_table_row();
  }   }
     }      }
     $result .= &Apache::loncommon::end_data_table().      $result .= &Apache::loncommon::end_data_table();
  &show_grading_menu_form($symb);  
     my $msg = '<p><b>'.      my $msg = '<p><b>'.
  &mt('Number of records updated = [_1] for [quant,_2,student].',   &mt('Number of records updated = [_1] for [quant,_2,student].',
     $rec_update,$count).'</b><br />'.      $rec_update,$count).'</b><br />'.
Line 3682  sub split_part_type { Line 4600  sub split_part_type {
 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 or the student/employee 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;
Line 3722  ENDPICK Line 4642  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 or the student/employee 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;
Line 3765  sub csvuploadmap_header { Line 4687  sub csvuploadmap_header {
  $javascript=&csvupload_javascript_forward_associate();   $javascript=&csvupload_javascript_forward_associate();
     }      }
   
     my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});  
     my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');      my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
     my $ignore=&mt('Ignore First Line');      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>  <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" />
Line 3788  to this page if the data selected is ins Line 4706  to this page if the data selected is ins
 <input type="hidden" name="upfile_associate"   <input type="hidden" name="upfile_associate" 
                                        value="$env{'form.upfile_associate'}" />                                         value="$env{'form.upfile_associate'}" />
 <input type="hidden" name="symb"       value="$symb" />  <input type="hidden" name="symb"       value="$symb" />
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  
 <input type="hidden" name="probTitle"  value="$env{'form.probTitle'}" />  
 <input type="hidden" name="command"    value="csvuploadoptions" />  <input type="hidden" name="command"    value="csvuploadoptions" />
 <hr />  <hr />
 <script type="text/javascript" language="Javascript">  
 $javascript  
 </script>  
 ENDPICK  ENDPICK
       $request->print(&Apache::lonhtmlcommon::scripttag($javascript));
     return '';      return '';
   
 }  }
   
 sub csvupload_fields {  sub csvupload_fields {
     my ($symb) = @_;      my ($symb,$errorref) = @_;
     my (@parts) = &getpartlist($symb);      my (@parts) = &getpartlist($symb,$errorref);
       if (ref($errorref)) {
           if ($$errorref) {
               return;
           }
       }
   
     my @fields=(['ID','Student/Employee ID'],      my @fields=(['ID','Student/Employee ID'],
  ['username','Student Username'],   ['username','Student Username'],
  ['domain','Student Domain']);   ['domain','Student Domain']);
Line 3823  sub csvupload_fields { Line 4743  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.');
     my $result =<<CSVFORMJS;      &js_escape(\$alertmsg);
 <script type="text/javascript" language="javascript">      my $result = &Apache::lonhtmlcommon::scripttag(<<CSVFORMJS);
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
     alert("$alertmsg");      alert("$alertmsg");
Line 3843  sub checkforfile_js { Line 4764  sub checkforfile_js {
  }   }
  formname.submit();   formname.submit();
     }      }
     </script>  
 CSVFORMJS  CSVFORMJS
     return $result;      return $result;
 }  }
   
 sub upcsvScores_form {  sub upcsvScores_form {
     my ($request) = shift;      my ($request,$symb) = @_;
     my ($symb)=&get_symb($request);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $result=&checkforfile_js();      my $result=&checkforfile_js();
     $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);      $result.=&Apache::loncommon::start_data_table().
     my ($table) = &showResourceInfo($symb,$env{'form.probTitle'});               &Apache::loncommon::start_data_table_header_row().
     $result.=$table;               '<th>'.&mt('Specify a file containing the class scores for current resource.').'</th>'.
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";               &Apache::loncommon::end_data_table_header_row().
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";               &Apache::loncommon::start_data_table_row().'<td>';
     $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource.').  
  '</b></td></tr>'."\n";  
     $result.='<tr bgcolor=#ffffe6><td>'."\n";  
     my $upload=&mt("Upload Scores");      my $upload=&mt("Upload Scores");
     my $upfile_select=&Apache::loncommon::upfile_select_html();      my $upfile_select=&Apache::loncommon::upfile_select_html();
     my $ignore=&mt('Ignore First Line');      my $ignore=&mt('Ignore First Line');
Line 3869  sub upcsvScores_form { Line 4785  sub upcsvScores_form {
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <input type="hidden" name="symb" value="$symb" />  <input type="hidden" name="symb" value="$symb" />
 <input type="hidden" name="command" value="csvuploadmap" />  <input type="hidden" name="command" value="csvuploadmap" />
 <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />  
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  
 $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>  <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().
     $result.=&show_grading_menu_form($symb);              &Apache::loncommon::end_data_table();
     return $result;      return $result;
 }  }
   
   
 sub csvuploadmap {  sub csvuploadmap {
     my ($request)= @_;      my ($request,$symb) = @_;
     my ($symb)=&get_symb($request);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my $datatoken;      my $datatoken;
     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); }      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) {
  my @fields=&csvupload_fields($symb);          my $fieldserror;
    my @fields=&csvupload_fields($symb,\$fieldserror);
           if ($fieldserror) {
               $request->print(&navmap_errormsg());
               return;
           }
  if ($env{'form.upfile_associate'} eq 'reverse') {   if ($env{'form.upfile_associate'} eq 'reverse') {
     &Apache::loncommon::csv_print_samples($request,\@records);      &Apache::loncommon::csv_print_samples($request,\@records);
     $i=&Apache::loncommon::csv_print_select_table($request,\@records,      $i=&Apache::loncommon::csv_print_select_table($request,\@records,
Line 3924  sub csvuploadmap { Line 4843  sub csvuploadmap {
  }   }
     }      }
     &csvuploadmap_footer($request,$i,$keyfields);      &csvuploadmap_footer($request,$i,$keyfields);
     $request->print(&show_grading_menu_form($symb));  
   
     return '';      return '';
 }  }
   
 sub csvuploadoptions {  sub csvuploadoptions {
     my ($request)= @_;      my ($request,$symb)= @_;
     my ($symb)=&get_symb($request);      my $overwrite=&mt('Overwrite any existing score');
     my $checked=(($env{'form.noFirstLine'})?'1':'0');      my $checked=(($env{'form.noFirstLine'})?'1':'0');
     my $ignore=&mt('Ignore First Line');      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 3967  ENDPICK Line 4876  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");
     $request->print(&show_grading_menu_form($symb));  
     return '';      return '';
 }  }
   
Line 3991  sub get_fields { Line 4899  sub get_fields {
 }  }
   
 sub csvuploadassign {  sub csvuploadassign {
     my ($request)= @_;      my ($request,$symb) = @_;
     my ($symb)=&get_symb($request);  
     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); }      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 4052  sub csvuploadassign { Line 4962  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 4071  sub csvuploadassign { Line 4984  sub csvuploadassign {
  $grades{$store_key}=$entries{$fields{$dest}};   $grades{$store_key}=$entries{$fields{$dest}};
     }      }
  }   }
  if (! %grades) {    if (! %grades) {
            push(@skipped,&mt("[_1]: no data to save","$username:$domain"));              push(@skipped,&mt("[_1]: no data to save","$username:$domain")); 
         } else {          } else {
    $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";     $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
Line 4079  sub csvuploadassign { Line 4992  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('.');
   # 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);
    } else {     } 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]",
Line 4090  sub csvuploadassign { Line 5009  sub csvuploadassign {
         }          }
     }      }
     $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 4099  sub csvuploadassign { Line 5022  sub csvuploadassign {
  $request->print(join(', ',@notallowed));   $request->print(join(', ',@notallowed));
     }      }
     $request->print("<br />\n");      $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb));  
     return $error_msg;      return $error_msg;
 }  }
 #------------- end of section for handling csv file upload ---------  #------------- end of section for handling csv file upload ---------
Line 4110  sub csvuploadassign { Line 5032  sub csvuploadassign {
 #  #
 #--- Select a page/sequence and a student to grade  #--- Select a page/sequence and a student to grade
 sub pickStudentPage {  sub pickStudentPage {
     my ($request) = shift;      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.');
     $request->print(<<LISTJAVASCRIPT);      &js_escape(\$alertmsg);
 <script type="text/javascript" language="javascript">      $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));
   
 function checkPickOne(formname) {  function checkPickOne(formname) {
     if (radioSelection(formname.student) == null) {      if (radioSelection(formname.student) == null) {
Line 4127  function checkPickOne(formname) { Line 5049  function checkPickOne(formname) {
     formname.submit();      formname.submit();
 }  }
   
 </script>  
 LISTJAVASCRIPT  LISTJAVASCRIPT
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my ($symb) = &get_symb($request);  
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};      my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
       my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
   
     my $result='<h3><span class="LC_info">&nbsp;'.      my $result='<h3><span class="LC_info">&nbsp;'.
  &mt('Manual Grading by Page or Sequence').'</span></h3>';   &mt('Manual Grading by Page or Sequence').'</span></h3>';
   
     $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";      $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
     my ($titles,$symbx) = &getSymbMap();      my $map_error;
       my ($titles,$symbx) = &getSymbMap($map_error);
       if ($map_error) {
           $request->print(&navmap_errormsg());
           return; 
       }
     my ($curpage) =&Apache::lonnet::decode_symb($symb);       my ($curpage) =&Apache::lonnet::decode_symb($symb); 
 #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb);   #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
 #    my $type=($curpage =~ /\.(page|sequence)/);  #    my $type=($curpage =~ /\.(page|sequence)/);
     my $select = '<select name="selectpage">'."\n";  
     my $ctr=0;  
     foreach (@$titles) {  
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);  
  $select.='<option value="'.$ctr.'" '.  
     ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').  
     '>'.$showtitle.'</option>'."\n";  
  $ctr++;  
     }  
     $select.= '</select>';  
     $result.='&nbsp;<b>'.&mt('Problems from').':</b> '.$select."<br />\n";  
   
     $ctr=0;      # Collection of hidden fields
       my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";   $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
Line 4165  LISTJAVASCRIPT Line 5082  LISTJAVASCRIPT
     $result.='<input type="hidden" name="page" />'."\n".      $result.='<input type="hidden" name="page" />'."\n".
  '<input type="hidden" name="title" />'."\n";   '<input type="hidden" name="title" />'."\n";
   
     my $options =  
  '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".  
  '<label><input type="radio" name="vProb" value="yes" /> '.&mt('yes').' </label>'."<br />\n";  
     $result.='&nbsp;<b>'.&mt('View Problem Text').': </b>'.$options;  
   
     $options =  
  '<label><input type="radio" name="lastSub" value="none" /> '.&mt('none').' </label>'."\n".  
  '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.&mt('by dates and submissions').'</label>'."\n".  
  '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').' </label>'."\n";  
     $result.='&nbsp;<b>'.&mt('Submissions').': </b>'.$options;  
       
     $result.=&build_section_inputs();      $result.=&build_section_inputs();
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".      $result.='<input type="hidden" name="Status"  value="'.$stu_status.'" />'."\n".
  '<input type="hidden" name="command" value="displayPage" />'."\n".          '<input type="hidden" name="command" value="displayPage" />'."\n".
  '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".          '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";  
   
     $result.='&nbsp;<b>'.&mt('Use CODE').': </b> <input type="text" name="CODE" value="" /> <br />'."\n";      # Show grading options
       $result.=&Apache::lonhtmlcommon::start_pick_box();
       my $select = '<select name="selectpage">'."\n";
       $ctr=0;
       foreach (@$titles) {
           my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
           $select.='<option value="'.$ctr.'"'.
               ($$symbx{$_} =~ /$curpage$/ ? ' selected="selected"' : '').
               '>'.$showtitle.'</option>'."\n";
           $ctr++;
       }
       $select.= '</select>';
   
     $result.='&nbsp;<input type="button" '.      $result.=
  'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";          &Apache::lonhtmlcommon::row_title(&mt('Problems from'))
          .$select
          .&Apache::lonhtmlcommon::row_closure();
   
       $result.=
           &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
          .'<label><input type="radio" name="vProb" value="no"'
              .' checked="checked" /> '.&mt('no').' </label>'."\n"
          .'<label><input type="radio" name="vProb" value="yes" />'
              .&mt('yes').'</label>'."\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.=
           &Apache::lonhtmlcommon::row_title(&mt('Use CODE'))
          .'<input type="text" name="CODE" value="" />'
          .&Apache::lonhtmlcommon::row_closure(1)
          .&Apache::lonhtmlcommon::end_pick_box();
   
       # Show list of students to select for grading
       $result.='<br /><input type="button" '.
                'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";
   
     $request->print($result);      $request->print($result);
   
Line 4199  LISTJAVASCRIPT Line 5145  LISTJAVASCRIPT
  '<th>'.&nameUserString('header').'</th>'.   '<th>'.&nameUserString('header').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     
     my (undef,undef,$fullname) = &getclasslist($getsec,'1');      my (undef,undef,$fullname) = &getclasslist($getsec,'1',$getgroup);
     my $ptr = 1;      my $ptr = 1;
     foreach my $student (sort       foreach my $student (sort 
  {   {
Line 4225  LISTJAVASCRIPT Line 5171  LISTJAVASCRIPT
     }      }
     $studentTable.=&Apache::loncommon::end_data_table()."\n";      $studentTable.=&Apache::loncommon::end_data_table()."\n";
     $studentTable.='<input type="button" '.      $studentTable.='<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /></form>'."\n";                     'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /></form>'."\n";
   
     $studentTable.=&show_grading_menu_form($symb);  
     $request->print($studentTable);      $request->print($studentTable);
   
     return '';      return '';
 }  }
   
 sub getSymbMap {  sub getSymbMap {
       my ($map_error) = @_;
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           if (ref($map_error)) {
               $$map_error = 'navmap';
           }
           return;
       }
     my %symbx = ();      my %symbx = ();
     my @titles = ();      my @titles = ();
     my $minder = 0;      my $minder = 0;
Line 4258  sub getSymbMap { Line 5209  sub getSymbMap {
 #  #
 #--- Displays a page/sequence w/wo problems, w/wo submissions  #--- Displays a page/sequence w/wo problems, w/wo submissions
 sub displayPage {  sub displayPage {
     my ($request) = shift;      my ($request,$symb) = @_;
   
     my ($symb) = &get_symb($request);  
     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 4277  sub displayPage { Line 5226  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(
  $request->print(&show_grading_menu_form($symb));              '<span class="LC_warning">'.
  return;              &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 4294  sub displayPage { Line 5246  sub displayPage {
     $request->print($result);      $request->print($result);
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $request->print(&navmap_errormsg());
           return;
       }
     my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});      my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {      if (!$map) {
  $request->print('<span class="LC_warning">'.&mt('Unable to view requested sequence. ([_1])',$resUrl).'</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to view requested sequence. ([_1])',$resUrl).'</span>');
  $request->print(&show_grading_menu_form($symb));  
  return;    return; 
     }      }
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
Line 4311  sub displayPage { Line 5266  sub displayPage {
  '<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".   '<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".
  '<input type="hidden" name="title"   value="'.$env{'form.title'}.'" />'."\n".   '<input type="hidden" name="title"   value="'.$env{'form.title'}.'" />'."\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="overRideScore" value="no" />'."\n".   '<input type="hidden" name="overRideScore" value="no" />'."\n";
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n";  
   
     if (defined($env{'form.CODE'})) {      if (defined($env{'form.CODE'})) {
  $studentTable.=   $studentTable.=
Line 4321  sub displayPage { Line 5275  sub displayPage {
     my $checkIcon = '<img alt="'.&mt('Check Mark').      my $checkIcon = '<img alt="'.&mt('Check Mark').
  '" src="'.&Apache::loncommon::lonhttpdurl($request->dir_config('lonIconsURL').'/check.gif').'" height="16" border="0" />';   '" src="'.&Apache::loncommon::lonhttpdurl($request->dir_config('lonIconsURL').'/check.gif').'" height="16" border="0" />';
   
     $studentTable.='&nbsp;'.&mt('<b>Note:</b> Problems graded correct by the computer are marked with a [_1] symbol.',$checkIcon)."\n".      $studentTable.='&nbsp;<span class="LC_info">'.
           &mt('Problems graded correct by the computer are marked with a [_1] symbol.',$checkIcon).
           '</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 4344  sub displayPage { Line 5300  sub displayPage {
  &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">';
Line 4386  sub displayPage { Line 5342  sub displayPage {
  }   }
     } 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)) {
               $studentTable.=&gradeBox_start();
  foreach my $partid (@{$parts}) {   foreach my $partid (@{$parts}) {
     $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);      $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
     $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";      $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
     $question++;      $question++;
  }   }
               $studentTable.=&gradeBox_end();
  $prob++;   $prob++;
     }      }
     $studentTable.='</td></tr>';      $studentTable.='</td></tr>';
Line 4405  sub displayPage { Line 5365  sub displayPage {
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
   
     $studentTable.='</table>'."\n".      $studentTable.=
  '<input type="button" value="'.&mt('Save').'" '.          '</table>'."\n".
  'onClick="javascript:checkSubmitPage(this.form,'.$question.');" />'.          '<input type="button" value="'.&mt('Save').'" '.
  '</form>'."\n";          'onclick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
     $studentTable.=&show_grading_menu_form($symb);          '</form>'."\n";
     $request->print($studentTable);      $request->print($studentTable);
   
     return '';      return '';
Line 4424  sub displaySubByDates { Line 5384  sub displaySubByDates {
  &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>':'').
           ($isTask?'<th>'.&mt('Version').'</th>':'').
  '<th>'.&mt('Submission').'</th>'.   '<th>'.&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();
Line 4437  sub displaySubByDates { Line 5398  sub displaySubByDates {
   
     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 4451  sub displaySubByDates { Line 5415  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 @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)              my ($hidden,$type);
             : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));              $type = $$record{$version.':resource.'.$partid.'.type'};
                   if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                   $hidden = 1;
               }
       my @matchKey;
               if ($isTask) {
                   @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@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 '') {
                       
     my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)      my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));                 : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
     $displaySub[0].='<b>'.&mt('Part:').'</b>&nbsp;'.$display_part.'&nbsp;';                      $displaySub[0].='<span class="LC_nobreak">';
     $displaySub[0].='<span class="LC_internal_info">('.&mt('ID').'&nbsp;'.                      $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
  $responseId.')</span>&nbsp;<b>';                                     .' <span class="LC_internal_info">'
     if ($$record{"$where.$partid.tries"} eq '') {                                     .'('.&mt('Response ID: [_1]',$responseId).')'
  $displaySub[0].=&mt('Trial&nbsp;not&nbsp;counted');                                     .'</span>'
     } else {                                     .' <b>';
  $displaySub[0].=&mt('Trial&nbsp;[_1]',                      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"});      $$record{"$where.$partid.tries"});
     }                              if (($rndseed ne '')  && ($lastrndseed{$partid} ne '')) {
     my $responseType=($isTask ? 'Task'                                  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>&nbsp; '.          $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
  &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />';          $displaySub[0].='&nbsp; '.
       &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
                       }
  }   }
     }      }
     if (exists($$record{"$where.$partid.checkedin"})) {      if (exists($$record{"$where.$partid.checkedin"})) {
Line 4524  sub displaySubByDates { Line 5519  sub displaySubByDates {
 }  }
   
 sub updateGradeByPage {  sub updateGradeByPage {
     my ($request) = shift;      my ($request,$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"};
Line 4535  sub updateGradeByPage { Line 5530  sub updateGradeByPage {
     my $usec=$classlist->{$env{'form.student'}}[5];      my $usec=$classlist->{$env{'form.student'}}[5];
     if (!&canmodify($usec)) {      if (!&canmodify($usec)) {
  $request->print('<span class="LC_warning">'.&mt('Unable to modify requested student ([_1])',$env{'form.student'}).'</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to modify requested student ([_1])',$env{'form.student'}).'</span>');
  $request->print(&show_grading_menu_form($env{'form.symb'}));  
  return;   return;
     }      }
     my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';      my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';
Line 4544  sub updateGradeByPage { Line 5538  sub updateGradeByPage {
   
     $request->print($result);      $request->print($result);
   
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $request->print(&navmap_errormsg());
           return;
       }
     my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});      my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {      if (!$map) {
  $request->print('<span class="LC_warning">'.&mt('Unable to grade requested sequence ([_1]).',$resUrl).'</span>');   $request->print('<span class="LC_warning">'.&mt('Unable to grade requested sequence ([_1]).',$resUrl).'</span>');
  my ($symb)=&get_symb($request);  
  $request->print(&show_grading_menu_form($symb));  
  return;    return; 
     }      }
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
Line 4567  sub updateGradeByPage { Line 5564  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 4580  sub updateGradeByPage { Line 5577  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 4588  sub updateGradeByPage { Line 5585  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 4676  sub updateGradeByPage { Line 5679  sub updateGradeByPage {
     }      }
   
     $studentTable.=&Apache::loncommon::end_data_table();      $studentTable.=&Apache::loncommon::end_data_table();
     $studentTable.=&show_grading_menu_form($env{'form.symb'});  
     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 4689  sub updateGradeByPage { Line 5694  sub updateGradeByPage {
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
   
 #--------------------Scantron Grading-----------------------------------  #-------------------- Bubblesheet (Scantron) Grading -------------------
 #  #
 #------ start of section for handling grading by page/sequence ---------  #------ start of section for handling grading by page/sequence ---------
   
Line 4703  sub updateGradeByPage { Line 5708  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 4724  username:domain. Line 5729  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 4754  the homework problem. Line 5759  the homework problem.
   
 sub defaultFormData {  sub defaultFormData {
     my ($symb)=@_;      my ($symb)=@_;
     return '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".      return '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />';
      '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".  
      '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";  
 }  }
   
   
Line 4767  sub defaultFormData { Line 5770  sub defaultFormData {
    Return html dropdown of possible sequences to grade     Return html dropdown of possible sequences to grade
     
  Arguments:   Arguments:
    $symb - $symb of the current resource      $symb - $symb of the current resource
      $map_error - ref to scalar which will container error if
                   $navmap object is unavailable in &getSymbMap().
   
 =cut  =cut
   
 sub getSequenceDropDown {  sub getSequenceDropDown {
     my ($symb)=@_;      my ($symb,$map_error)=@_;
     my $result='<select name="selectpage">'."\n";      my $result='<select name="selectpage">'."\n";
     my ($titles,$symbx) = &getSymbMap();      my ($titles,$symbx) = &getSymbMap($map_error);
       if (ref($map_error)) {
           return if ($$map_error);
       }
     my ($curpage)=&Apache::lonnet::decode_symb($symb);       my ($curpage)=&Apache::lonnet::decode_symb($symb); 
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
Line 4800  my %subdivided_bubble_lines;       # no. Line 5808  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 4813  sub save_bubble_lines { Line 5826  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 4828  sub restore_bubble_lines { Line 5846  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 4862  sub scantron_filenames { Line 5864  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 4908  sub scantron_uploads { Line 5912  sub scantron_uploads {
 sub scantron_scantab {  sub scantron_scantab {
     my $result='<select name="scantron_format">'."\n";      my $result='<select name="scantron_format">'."\n";
     $result.='<option></option>'."\n";      $result.='<option></option>'."\n";
     my @lines = &get_scantronformat_file();      my @lines = &Apache::lonnet::get_scantronformat_file();
     if (@lines > 0) {      if (@lines > 0) {
         foreach my $line (@lines) {          foreach my $line (@lines) {
             next if (($line =~ /^\#/) || ($line eq ''));              next if (($line =~ /^\#/) || ($line eq ''));
Line 4920  sub scantron_scantab { Line 5924  sub scantron_scantab {
     return $result;      return $result;
 }  }
   
 =pod  
   
 =item get_scantronformat_file  
   
   Returns an array containing lines from the scantron format file for  
   the domain of the course.  
   
   If a url for a custom.tab file is listed in domain's configuration.db,   
   lines are from this file.  
   
   Otherwise, if a default.tab has been published in RES space by the   
   domainconfig user, lines are from this file.  
   
   Otherwise, fall back to getting lines from the legacy file on the  
   local server:  /home/httpd/lonTabs/default_scantronformat.tab      
   
 =cut  
   
 sub get_scantronformat_file {  
     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};  
     my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$cdom);  
     my $gottab = 0;  
     my @lines;  
     if (ref($domconfig{'scantron'}) eq 'HASH') {  
         if ($domconfig{'scantron'}{'scantronformat'} ne '') {  
             my $formatfile = &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.$domconfig{'scantron'}{'scantronformat'});  
             if ($formatfile ne '-1') {  
                 @lines = split("\n",$formatfile,-1);  
                 $gottab = 1;  
             }  
         }  
     }  
     if (!$gottab) {  
         my $confname = $cdom.'-domainconfig';  
         my $default = $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cdom.'/'.$confname.'/default.tab';  
         my $formatfile =  &Apache::lonnet::getfile($default);  
         if ($formatfile ne '-1') {  
             @lines = split("\n",$formatfile,-1);  
             $gottab = 1;  
         }  
     }  
     if (!$gottab) {  
         my @domains = &Apache::lonnet::current_machine_domains();  
         if (grep(/^\Q$cdom\E$/,@domains)) {  
             my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');  
             @lines = <$fh>;  
             close($fh);  
         } else {  
             my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab');  
             @lines = <$fh>;  
             close($fh);  
         }  
     }  
     return @lines;  
 }  
   
 =pod   =pod 
   
 =item scantron_CODElist  =item scantron_CODElist
Line 5023  sub scantron_CODEunique { Line 5971  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 5037  sub scantron_CODEunique { Line 5985  sub scantron_CODEunique {
 =cut  =cut
   
 sub scantron_selectphase {  sub scantron_selectphase {
     my ($r,$file2grade) = @_;      my ($r,$file2grade,$symb) = @_;
     my ($symb)=&get_symb($r);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $sequence_selector=&getSequenceDropDown($symb);      my $map_error;
       my $sequence_selector=&getSequenceDropDown($symb,\$map_error);
       if ($map_error) {
           $r->print('<br />'.&navmap_errormsg().'<br />');
           return;
       }
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $grading_menu_button=&show_grading_menu_form($symb);  
     my $file_selector=&scantron_uploads($file2grade);      my $file_selector=&scantron_uploads($file2grade);
     my $format_selector=&scantron_scantab();      my $format_selector=&scantron_scantab();
     my $CODE_selector=&scantron_CODElist();      my $CODE_selector=&scantron_CODElist();
Line 5051  sub scantron_selectphase { Line 6002  sub scantron_selectphase {
   
     $ssi_error = 0;      $ssi_error = 0;
   
       if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
           &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
   
           # Chunk of form to prompt for a scantron file upload.
   
           $r->print('
       <br />');
           my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
           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);
           my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);
           $r->print(&Apache::lonhtmlcommon::scripttag('
       function checkUpload(formname) {
           if (formname.upfile.value == "") {
               alert("'.$alertmsg.'");
               return false;
           }
           formname.submit();
       }'."\n".$formatjs));
           $r->print('
                 <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
                   '.$default_form_data.'
                   <input name="courseid" type="hidden" value="'.$cnum.'" />
                   <input name="domainid" type="hidden" value="'.$cdom.'" />
                   <input name="command" value="scantronupload_save" type="hidden" />
                 '.&Apache::loncommon::start_data_table('LC_scantron_action').'
                 '.&Apache::loncommon::start_data_table_header_row().'
                   <th>
                   &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
                   </th>
                 '.&Apache::loncommon::end_data_table_header_row().'
                 '.&Apache::loncommon::start_data_table_row().'
               <td>
                   '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");
           if ($formatoptions) {
               $r->print('</td>
                    '.&Apache::loncommon::end_data_table_row().'
                    '.&Apache::loncommon::start_data_table_row().'
                    <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'
                    </td>
                    '.&Apache::loncommon::end_data_table_row().'
                    '.&Apache::loncommon::start_data_table_row().'
                    <td>'
               );
           } else {
               $r->print(' <br />');
           }
           $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
                 </td>
                '.&Apache::loncommon::end_data_table_row().'
                '.&Apache::loncommon::end_data_table().'
                </form>'
           );
   
       }
   
     # Chunk of form to prompt for a file to grade and how:      # Chunk of form to prompt for a file to grade and how:
   
     $result.= '      $result.= '
Line 5098  sub scantron_selectphase { Line 6106  sub scantron_selectphase {
         
     $r->print($result);      $r->print($result);
   
     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||  
         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {  
   
  # Chunk of form to prompt for a scantron file upload.  
   
         $r->print('  
     <br />  
     '.&Apache::loncommon::start_data_table('LC_scantron_action').'  
        '.&Apache::loncommon::start_data_table_header_row().'  
             <th>  
               &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'  
             </th>  
        '.&Apache::loncommon::end_data_table_header_row().'  
        '.&Apache::loncommon::start_data_table_row().'  
             <td>  
 ');  
     my $default_form_data=&defaultFormData(&get_symb($r,1));  
     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};  
     my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};  
     $r->print('  
               <script type="text/javascript" language="javascript">  
     function checkUpload(formname) {  
  if (formname.upfile.value == "") {  
     alert("'.&mt('Please use the browse button to select a file from your local directory.').'");  
     return false;  
  }  
  formname.submit();  
     }  
               </script>  
   
               <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">  
                 '.$default_form_data.'  
                 <input name="courseid" type="hidden" value="'.$cnum.'" />  
                 <input name="domainid" type="hidden" value="'.$cdom.'" />  
                 <input name="command" value="scantronupload_save" type="hidden" />  
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'  
                 <br />  
                 <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />  
               </form>  
 ');  
   
         $r->print('  
             </td>  
        '.&Apache::loncommon::end_data_table_row().'  
        '.&Apache::loncommon::end_data_table().'  
 ');  
     }  
   
     # Chunk of the form that prompts to view a scoring office file,      # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.      # corrected file, skipped records in a file.
   
Line 5172  sub scantron_selectphase { Line 6132  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 5204  sub scantron_selectphase { Line 6164  sub scantron_selectphase {
               &Apache::loncommon::end_data_table_row()."\n".                &Apache::loncommon::end_data_table_row()."\n".
               &Apache::loncommon::end_data_table()."\n".                &Apache::loncommon::end_data_table()."\n".
               '</form><br />');                '</form><br />');
     $r->print($grading_menu_button);  
     return;      return;
 }  }
   
 =pod  
   
 =item get_scantron_config  
   
    Parse and return the scantron configuration line selected as a  
    hash of configuration file fields.  
   
  Arguments:  
     which - the name of the configuration to parse from the file.  
   
   
  Returns:  
             If the named configuration is not in the file, an empty  
             hash is returned.  
     a hash with the fields  
       name         - internal name for the this configuration setup  
       description  - text to display to operator that describes this config  
       CODElocation - if 0 or the string 'none'  
                           - no CODE exists for this config  
                      if -1 || the string 'letter'  
                           - a CODE exists for this config and is  
                             a string of letters  
                      Unsupported value (but planned for future support)  
                           if a positive integer  
                                - The CODE exists as the first n items from  
                                  the question section of the form  
                           if the string 'number'  
                                - The CODE exists for this config and is  
                                  a string of numbers  
       CODEstart   - (only matter if a CODE exists) column in the line where  
                      the CODE starts  
       CODElength  - length of the CODE  
       IDstart     - column where the student/employee ID starts  
       IDlength    - length of the student/employee ID info  
       Qstart      - column where the information from the bubbled  
                     'questions' start  
       Qlength     - number of columns comprising a single bubble line from  
                     the sheet. (usually either 1 or 10)  
       Qon         - either a single character representing the character used  
                     to signal a bubble was chosen in the positional setup, or  
                     the string 'letter' if the letter of the chosen bubble is  
                     in the final, or 'number' if a number representing the  
                     chosen bubble is in the file (1->A 0->J)  
       Qoff        - the character used to represent that a bubble was  
                     left blank  
       PaperID     - if the scanning process generates a unique number for each  
                     sheet scanned the column that this ID number starts in  
       PaperIDlength - number of columns that comprise the unique ID number  
                       for the sheet of paper  
       FirstName   - column that the first name starts in  
       FirstNameLength - number of columns that the first name spans  
    
       LastName    - column that the last name starts in  
       LastNameLength - number of columns that the last name spans  
   
 =cut  
   
 sub get_scantron_config {  
     my ($which) = @_;  
     my @lines = &get_scantronformat_file();  
     my %config;  
     #FIXME probably should move to XML it has already gotten a bit much now  
     foreach my $line (@lines) {  
  my ($name,$descrip)=split(/:/,$line);  
  if ($name ne $which ) { next; }  
  chomp($line);  
  my @config=split(/:/,$line);  
  $config{'name'}=$config[0];  
  $config{'description'}=$config[1];  
  $config{'CODElocation'}=$config[2];  
  $config{'CODEstart'}=$config[3];  
  $config{'CODElength'}=$config[4];  
  $config{'IDstart'}=$config[5];  
  $config{'IDlength'}=$config[6];  
  $config{'Qstart'}=$config[7];  
   $config{'Qlength'}=$config[8];  
  $config{'Qoff'}=$config[9];  
  $config{'Qon'}=$config[10];  
  $config{'PaperID'}=$config[11];  
  $config{'PaperIDlength'}=$config[12];  
  $config{'FirstName'}=$config[13];  
  $config{'FirstNamelength'}=$config[14];  
  $config{'LastName'}=$config[15];  
  $config{'LastNamelength'}=$config[16];  
  last;  
     }  
     return %config;  
 }  
   
 =pod   =pod 
   
 =item username_to_idmap  =item username_to_idmap
Line 5320  sub username_to_idmap { Line 6190  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 5333  sub username_to_idmap { Line 6212  sub username_to_idmap {
    Process a requested correction to a scanline.     Process a requested correction to a scanline.
   
   Arguments:    Arguments:
     $scantron_config   - hash from &get_scantron_config()      $scantron_config   - hash from &Apache::lonnet::get_scantron_config()
     $scan_data         - hash of correction information       $scan_data         - hash of correction information 
                           (see &scantron_getfile())                            (see &scantron_getfile())
     $line              - existing scanline      $line              - existing scanline
Line 5503  sub digits_to_letters { Line 6382  sub digits_to_letters {
                        (see scantron_getfile for more information)                         (see scantron_getfile for more information)
     just_header      - True if should not process question answers but only      just_header      - True if should not process question answers but only
                        the stuff to the left of the answers.                         the stuff to the left of the answers.
       randomorder      - True if randomorder in use
       randompick       - True if randompick in use
       sequence         - Exam folder URL
       master_seq       - Ref to array containing symbs in exam folder
       symb_to_resource - Ref to hash of symbs for resources in exam folder
                          (corresponding values are resource objects)
       partids_by_symb  - Ref to hash of symb -> array ref of partIDs
       orderedforcode   - Ref to hash of arrays. keys are CODEs and values
                          are refs to an array of resource objects, ordered
                          according to order used for CODE, when randomorder
                          and or randompick are in use.
       respnumlookup    - Ref to hash mapping question numbers in bubble lines
                          for current line to question number used for same question
                           in "Master Sequence" (as seen by Course Coordinator).
       startline        - Ref to hash where key is question number (0 is first)
                          and value is number of first bubble line for current 
                          student or code-based randompick and/or randomorder.
       totalref         - Ref of scalar used to score total number of bubble
                          lines needed for responses in a scan line (used when
                          randompick in use. 
   
  Returns:   Returns:
    Hash containing the result of parsing the scanline     Hash containing the result of parsing the scanline
   
Line 5547  sub digits_to_letters { Line 6447  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 5588  sub scantron_parse_scanline { Line 6488  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 5600  sub scantron_parse_scanline { Line 6519  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 5615  sub scantron_parse_scanline { Line 6539  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 5627  sub scantron_parse_scanline { Line 6554  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 5639  sub scantron_parse_scanline { Line 6568  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 5660  sub scantron_validator_lettnum { Line 6646  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 5720  sub scantron_validator_lettnum { Line 6710  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 5762  sub scantron_validator_positional { Line 6753  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 5900  sub scantron_filter { Line 6895  sub scantron_filter {
   
 sub scantron_process_corrections {  sub scantron_process_corrections {
     my ($r) = @_;      my ($r) = @_;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     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 $which=$env{'form.scantron_line'};      my $which=$env{'form.scantron_line'};
Line 5949  sub scantron_process_corrections { Line 6944  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 6041  sub remember_current_skipped { Line 7040  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 6063  sub check_for_error { Line 7062  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=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $CODElist;      my $CODElist;
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $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 6104  sub scantron_warning_screen { Line 7108  sub scantron_warning_screen {
 =cut  =cut
   
 sub scantron_do_warning {  sub scantron_do_warning {
     my ($r)=@_;      my ($r,$symb)=@_;
     my ($symb)=&get_symb($r);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     $r->print(&scantron_form_start().$default_form_data);      $r->print(&scantron_form_start().$default_form_data);
     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" />
 ');  ');
     }      }
     $r->print("</form><br />".&show_grading_menu_form($symb));      $r->print("</form><br />");
     return '';      return '';
 }  }
   
Line 6167  SCANTRONFORM Line 7171  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 6177  SCANTRONFORM Line 7183  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 6186  SCANTRONFORM Line 7192  SCANTRONFORM
 =cut  =cut
   
 sub scantron_validate_file {  sub scantron_validate_file {
     my ($r) = @_;      my ($r,$symb) = @_;
     my ($symb)=&get_symb($r);  
     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 6214  sub scantron_validate_file { Line 7219  sub scantron_validate_file {
     $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();      $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $max_bubble=&scantron_get_maxbubble();      my $nav_error;
       my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
       my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
       if ($nav_error) {
           $r->print(&navmap_errormsg());
           return '';
       }
     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 6233  sub scantron_validate_file { Line 7247  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 6240  sub scantron_validate_file { Line 7255  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 6250  sub scantron_validate_file { Line 7265  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 6262  sub scantron_validate_file { Line 7277  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 6274  sub scantron_validate_file { Line 7289  sub scantron_validate_file {
     $r->print(" ".&mt("this scanline saving it for later."));      $r->print(" ".&mt("this scanline saving it for later."));
  }   }
     }      }
     $r->print(" </form><br />".&show_grading_menu_form($symb));      $r->print(" </form><br />");
     return '';      return '';
 }  }
   
Line 6283  sub scantron_validate_file { Line 7298  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 6308  sub scantron_remove_file { Line 7323  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 6345  sub scantron_remove_scan_data { Line 7360  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 6445  sub lonnet_putfile { Line 7460  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 6539  sub get_todo_count { Line 7554  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 6626  sub scantron_validate_sequence { Line 7641  sub scantron_validate_sequence {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
   
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           return (1,$currentphase);
       }
     my (undef,undef,$sequence)=      my (undef,undef,$sequence)=
  &Apache::lonnet::decode_symb($env{'form.selectpage'});   &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
Line 6637  sub scantron_validate_sequence { Line 7656  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'
                      .' exam mode. Grading these resources currently may not'
                      .' work correctly.')
                  .'</p>'
               );
     return (1,$currentphase);      return (1,$currentphase);
  }   }
     }      }
Line 6655  sub scantron_validate_ID { Line 7679  sub scantron_validate_ID {
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
       
     &scantron_get_maxbubble(); # parse needs the bubble_lines.. array.      my $nav_error;
       &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble_lines.. array.
       if ($nav_error) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
     my %found=('ids'=>{},'usernames'=>{});      my %found=('ids'=>{},'usernames'=>{});
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
Line 6713  sub scantron_validate_ID { Line 7742  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 6741  sub scantron_get_correction { Line 7780  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 6754  sub scantron_get_correction { Line 7793  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 6794  sub scantron_get_correction { Line 7834  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 />");
  }   }
   
  $r->print(<<ENDSCRIPT);   $r->print(&Apache::lonhtmlcommon::scripttag(<<ENDSCRIPT));
 <script type="text/javascript">  
 function change_radio(field) {  function change_radio(field) {
     var slct=document.scantronupload.scantron_CODE_resolution;      var slct=document.scantronupload.scantron_CODE_resolution;
     var i;      var i;
Line 6809  function change_radio(field) { Line 7848  function change_radio(field) {
         if (slct[i].value==field) { slct[i].checked=true; }          if (slct[i].value==field) { slct[i].checked=true; }
     }      }
 }  }
 </script>  
 ENDSCRIPT  ENDSCRIPT
  my $href="/adm/pickcode?".   my $href="/adm/pickcode?".
    "form=".&escape("scantronupload").     "form=".&escape("scantronupload").
Line 6834  ENDSCRIPT Line 7872  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 6847  ENDSCRIPT Line 7886  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 6861  ENDSCRIPT Line 7902  ENDSCRIPT
  # 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 6881  sub verify_bubbles_checked { Line 7925  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.");
     my $output = (<<ENDSCRIPT);      &js_escape(\$warning);
 <script type="text/javascript">      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");
     var need_bubble_count = 0;      var need_bubble_count = 0;
Line 6905  function verify_bubble_radio(form) { Line 7949  function verify_bubble_radio(form) {
     }      }
     form.submit();       form.submit(); 
 }  }
 </script>  
 ENDSCRIPT  ENDSCRIPT
     return $output;      return $output;
 }  }
Line 6920  used to fill in the scantron_questions f Line 7963  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 6934  sub questions_to_line_list { Line 7985  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 6943  sub questions_to_line_list { Line 8002  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 6966  for multi and missing bubble cases). Line 8033  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 6990  for multi and missing bubble cases). Line 8065  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 7006  sub prompt_for_corrections { Line 8092  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 7044  sub prompt_for_corrections { Line 8139  sub prompt_for_corrections {
   
  Arguments:   Arguments:
     $r           - Apache request object      $r           - Apache request object
     $scan_config - hash from &get_scantron_config()      $scan_config - hash from &Apache::lonnet::get_scantron_config()
     $line        - Number of the line being displayed.      $line        - Number of the line being displayed.
     $questionnum - Question number (may include subquestion)      $questionnum - Question number (may include subquestion)
     $error       - Type of error.      $error       - Type of error.
Line 7057  sub scantron_bubble_selector { Line 8152  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 7196  sub get_codes { Line 8303  sub get_codes {
   
 sub scantron_validate_CODE {  sub scantron_validate_CODE {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
  $scantron_config{'CODElength'}) {   $scantron_config{'CODElength'}) {
Line 7211  sub scantron_validate_CODE { Line 8318  sub scantron_validate_CODE {
   
     my %allcodes=&get_codes();      my %allcodes=&get_codes();
   
     &scantron_get_maxbubble(); # parse needs the lines per response array.      my $nav_error;
       &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the lines per response array.
       if ($nav_error) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
Line 7261  sub scantron_validate_doublebubble { Line 8373  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=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     &scantron_get_maxbubble(); # parse needs the bubble line array.  
       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;
       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) {
           $r->print(&navmap_errormsg());
           return(1,$currentphase);
       }
   
     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=&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 7283  sub scantron_validate_doublebubble { Line 8435  sub scantron_validate_doublebubble {
   
   
 sub scantron_get_maxbubble {  sub scantron_get_maxbubble {
       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 7293  sub scantron_get_maxbubble { Line 8446  sub scantron_get_maxbubble {
  &Apache::lonnet::decode_symb($env{'form.selectpage'});   &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           if (ref($nav_error)) {
               $$nav_error = 1;
           }
           return;
       }
     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 7306  sub scantron_get_maxbubble { Line 8466  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 7339  sub scantron_get_maxbubble { Line 8502  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 7352  sub scantron_get_maxbubble { Line 8516  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 7373  sub scantron_get_maxbubble { Line 8538  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=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $max_bubble=&scantron_get_maxbubble();  
       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;
       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) {
           $r->print(&navmap_errormsg());
           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 7397  sub scantron_validate_missingbubbles { Line 8616  sub scantron_validate_missingbubbles {
  foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {   foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {
             my $lastbubble;              my $lastbubble;
             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};
                my $subcount = 1;                      $first = $startline{$question-1};
                while ($subcount<$subquestion) {                  } else {
                    $first += $subans[$subcount-1];                      $responsenum = $question-1;
                    $subcount ++;                      $first = $first_bubble_line{$responsenum};
                }                  }
                my $count = $subans[$subquestion-1];                  if (!defined($first)) { next; }
                $lastbubble = $first + $count;                  my @subans = split(/,/,$subdivided_bubble_lines{$responsenum});
                   my $subcount = 1;
                   while ($subcount<$subquestion) {
                       $first += $subans[$subcount-1];
                       $subcount ++;
                   }
                   my $count = $subans[$subquestion-1];
                   $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 7426  sub scantron_validate_missingbubbles { Line 8662  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=&Apache::lonnet::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) = @_;      my ($r,$symb) = @_;
   
     my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});      my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
     my ($symb)=&get_symb($r);  
     if (!$symb) {      if (!$symb) {
  return '';   return '';
     }      }
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::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);
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           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);
       if (ref($map)) {
           $randomorder = $map->randomorder();
           $randompick = $map->randompick();
       } else {
           $r->print(&navmap_errormsg());
           return '';
       }
       my $nav_error;
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     my (%grader_partids_by_symb,%grader_randomlists_by_symb);      if ($randomorder || $randompick) {
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,          $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
                             \%grader_randomlists_by_symb);          if ($nav_error) {
     foreach my $resource (@resources) {              $r->print(&navmap_errormsg());
         my $ressymb = $resource->symb();              return '';
         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'};  
             }  
         }          }
     }      }
       &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 7474  SCANTRONFORM Line 8752  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;
   
     &scantron_get_maxbubble(); # Need the bubble lines array to parse.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
       if ($nav_error) {
           $r->print(&navmap_errormsg());
           return '';
       }
   
     # If an ssi failed in scantron_get_maxbubble, put an error message out to      # If an ssi failed in scantron_get_maxbubble, put an error message out to
     # the user and return.      # the user and return.
Line 7492  SCANTRONFORM Line 8771  SCANTRONFORM
     if ($ssi_error) {      if ($ssi_error) {
  $r->print("</form>");   $r->print("</form>");
  &ssi_print_error($r);   &ssi_print_error($r);
  $r->print(&show_grading_menu_form($symb));  
         &Apache::lonnet::remove_lock($lock);          &Apache::lonnet::remove_lock($lock);
  return ''; # Dunno why the other returns return '' rather than just returning.   return ''; # Dunno why the other returns return '' rather than just returning.
     }      }
   
     my %lettdig = &letter_to_digits();      my %lettdig = &Apache::lonnet::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 7506  SCANTRONFORM Line 8785  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 7523  SCANTRONFORM Line 8808  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 %partids_by_symb;          my $scancode;
         foreach my $resource (@resources) {          if ((exists($scan_record->{'scantron.CODE'})) &&
             my $ressymb = $resource->symb();              (&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);
           foreach my $resource (@mapresources) {
               my $ressymb;
               if (ref($resource)) {
                   $ressymb = $resource->symb();
               } else {
                   $res_error = 1;
                   last;
               }
             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};
             }              }
         }          }
   
           if ($res_error) {
               &scantron_add_delay(\@delayqueue,$line,
                                   'An error occurred while grading student '.$uname,2);
               next;
           }
   
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
   &Apache::lonnet::appenv($scan_record);    &Apache::lonnet::appenv($scan_record);
   
Line 7545  SCANTRONFORM Line 8864  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);
             $r->print(&show_grading_menu_form($symb));  
             &Apache::lonnet::remove_lock($lock);              &Apache::lonnet::remove_lock($lock);
             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);
                     $r->print(&show_grading_menu_form($symb));  
                     &Apache::lonnet::remove_lock($lock);                      &Apache::lonnet::remove_lock($lock);
                     delete($completedstudents{$uname});                      delete($completedstudents{$uname});
                     return '';                      return '';
                 }                  }
                 $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 7615  SCANTRONFORM Line 8946  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 7642  SCANTRONFORM Line 8973  SCANTRONFORM
 #    $r->print("<p>took $lasttime</p>");  #    $r->print("<p>took $lasttime</p>");
   
     $r->print("</form>");      $r->print("</form>");
     $r->print(&show_grading_menu_form($symb));  
     return '';      return '';
 }  }
   
 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 7667  sub graders_resources_pass { Line 8999  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 7681  sub grade_student_bubbles { Line 9072  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 7699  sub grade_student_bubbles { Line 9100  sub grade_student_bubbles {
 }  }
   
 sub scantron_upload_scantron_data {  sub scantron_upload_scantron_data {
     my ($r)=@_;      my ($r,$symb) = @_;
     my $dom = $env{'request.role.domain'};      my $dom = $env{'request.role.domain'};
       my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($dom);
     my $domdesc = &Apache::lonnet::domain($dom,'description');      my $domdesc = &Apache::lonnet::domain($dom,'description');
     $r->print(&Apache::loncommon::coursebrowser_javascript($dom));      $r->print(&Apache::loncommon::coursebrowser_javascript($dom));
     my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',      my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
   'domainid',    'domainid',
   'coursename',$dom);    'coursename',$dom);
     my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.      my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.
                        ('&nbsp'x2).&mt('(shows course personnel)');                          ('&nbsp'x2).&mt('(shows course personnel)');
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData($symb);
     $r->print('      my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
 <script type="text/javascript" language="javascript">      &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.");
       &js_escape(\$nocourseid_alert);
       $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("'.$nofile_alert.'");
     return false;      return false;
  }   }
         if (formname.courseid.value == "") {          if (formname.courseid.value == "") {
             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.').'");              alert("'.$nocourseid_alert.'");
             return false;              return false;
         }          }
  formname.submit();   formname.submit();
Line 7737  sub scantron_upload_scantron_data { Line 9142  sub scantron_upload_scantron_data {
         return;          return;
     }      }
   
 </script>      '.$formatjs.'
   '));
 <h3>'.&mt('Send scanned bubblesheet data to a course').'</h3>      $r->print('
   <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 7752  sub scantron_upload_scantron_data { Line 9158  sub scantron_upload_scantron_data {
   &Apache::lonhtmlcommon::row_closure().    &Apache::lonhtmlcommon::row_closure().
   &Apache::lonhtmlcommon::row_title(&mt('Domain')).    &Apache::lonhtmlcommon::row_title(&mt('Domain')).
   '<input name="domainid" type="hidden" />'.$domdesc.    '<input name="domainid" type="hidden" />'.$domdesc.
   &Apache::lonhtmlcommon::row_closure().    &Apache::lonhtmlcommon::row_closure());
       if ($formatoptions) {
           $r->print(&Apache::lonhtmlcommon::row_title($formattitle).$formatoptions.
                     &Apache::lonhtmlcommon::row_closure());
       }
       $r->print(
   &Apache::lonhtmlcommon::row_title(&mt('File to upload')).    &Apache::lonhtmlcommon::row_title(&mt('File to upload')).
   '<input type="file" name="upfile" size="50" />'.    '<input type="file" name="upfile" size="50" />'.
   &Apache::lonhtmlcommon::row_closure(1).    &Apache::lonhtmlcommon::row_closure(1).
   &Apache::lonhtmlcommon::end_pick_box().'<br />    &Apache::lonhtmlcommon::end_pick_box().'<br />
   
 <input name="command" value="scantronupload_save" type="hidden" />  <input name="command" value="scantronupload_save" type="hidden" />
 <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />  <input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
 </form>  </form>
 ');  ');
     return '';      return '';
 }  }
   
   sub scantron_upload_dataformat {
       my ($dom) = @_;
       my ($formatoptions,$formattitle,$formatjs);
       $formatjs = <<'END';
   function toggleScantab(form) {
      return;
   }
   END
       my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$dom);
       if (ref($domconfig{'scantron'}) eq 'HASH') {
           if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
               if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {
                   if (($domconfig{'scantron'}{'config'}{'dat'}) &&
                       (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {
                       if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                           if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
                               my ($onclick,$formatextra,$singleline);
                               my @lines = &Apache::lonnet::get_scantronformat_file();
                               my $count = 0;
                               foreach my $line (@lines) {
                                   next if ($line =~ /^#/);
                                   $singleline = $line;
                                   $count ++;
                               }
                               if ($count > 1) {
                                   $formatextra = '<div style="display:none" id="bubbletype">'.
                                                  '<span class="LC_nobreak">'.
                                                  &mt('Bubblesheet type:').'&nbsp;'.
                                                  &scantron_scantab().'</span></div>';
                                   $onclick = ' onclick="toggleScantab(this.form);"';
                                   $formatjs = <<"END";
   function toggleScantab(form) {
       var divid = 'bubbletype';
       if (document.getElementById(divid)) {
           var radioname = 'fileformat';
           var num = form.elements[radioname].length;
           if (num) {
               for (var i=0; i<num; i++) {
                   if (form.elements[radioname][i].checked) {
                       var chosen = form.elements[radioname][i].value;
                       if (chosen == 'dat') {
                           document.getElementById(divid).style.display = 'none';
                       } else if (chosen == 'csv') {
                           document.getElementById(divid).style.display = 'block';
                       }
                   }
               }
           }
       }
       return;
   }
   
   END
                               } elsif ($count == 1) {
                                   my $formatname = (split(/:/,$singleline,2))[0];
                                   $formatextra = '<input type="hidden" name="scantron_format" value="'.$formatname.'" />';
                               }
                               $formattitle = &mt('File format');
                               $formatoptions = '<label><input name="fileformat" type="radio" value="dat" checked="checked"'.$onclick.' />'.
                                                &mt('Plain Text (no delimiters)').
                                                '</label>'.('&nbsp;'x2).
                                                '<label><input name="fileformat" type="radio" value="csv"'.$onclick.' />'.
                                                &mt('Comma separated values').'</label>'.$formatextra;
                           }
                       }
                   }
               } elsif (keys(%{$domconfig{'scantron'}{'config'}}) == 1) {
                   if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                       if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
                           $formattitle = &mt('Bubblesheet type');
                           $formatoptions = &scantron_scantab();
                       }
                   }
               }
           }
       }
       return ($formatoptions,$formattitle,$formatjs);
   }
   
 sub scantron_upload_scantron_data_save {  sub scantron_upload_scantron_data_save {
     my($r)=@_;      my ($r,$symb) = @_;
     my ($symb)=&get_symb($r,1);  
     my $doanotherupload=      my $doanotherupload=
  '<br /><form action="/adm/grades" method="post">'."\n".   '<br /><form action="/adm/grades" method="post">'."\n".
  '<input type="hidden" name="command" value="scantronupload" />'."\n".   '<input type="hidden" name="command" value="scantronupload" />'."\n".
Line 7778  sub scantron_upload_scantron_data_save { Line 9266  sub scantron_upload_scantron_data_save {
  !&Apache::lonnet::allowed('usc',   !&Apache::lonnet::allowed('usc',
     $env{'form.domainid'}.'_'.$env{'form.courseid'})) {      $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
  $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");   $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");
  if ($symb) {          unless ($symb) {
     $r->print(&show_grading_menu_form($symb));  
  } else {  
     $r->print($doanotherupload);      $r->print($doanotherupload);
  }   }
  return '';   return '';
     }      }
     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 %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$env{'form.domainid'});
             &Apache::lonnet::userfileupload('upfile','','scantron','','','',          my $parser;
           if (ref($domconfig{'scantron'}) eq 'HASH') {
               if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
                   my $is_csv;
                   my @possibles = keys(%{$domconfig{'scantron'}{'config'}});
                   if (@possibles > 1) {
                       if ($env{'form.fileformat'} eq 'csv') {
                           if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
                               if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                                   if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
                                       $is_csv = 1;
                                   }
                               }
                           }
                       }
                   } elsif (@possibles == 1) {
                       if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
                           if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                               if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
                                   $is_csv = 1;
                               }
                           }
                       }
                   }
                   if ($is_csv) {
                      $parser = $domconfig{'scantron'}{'config'}{'csv'};
                   }
               }
           }
           my $result =
               &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',
                                             $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>'));
  }   }
     }      }
     if ($symb) {      if ($symb) {
  $r->print(&scantron_selectphase($r,$uploadedfile));   $r->print(&scantron_selectphase($r,$uploadedfile,$symb));
     } else {      } else {
  $r->print($doanotherupload);   $r->print($doanotherupload);
     }      }
Line 7825  sub validate_uploaded_scantron_file { Line 9348  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 7833  sub validate_uploaded_scantron_file { Line 9356  sub validate_uploaded_scantron_file {
             $idmap{$lckey} = $idmap{$key};              $idmap{$lckey} = $idmap{$key};
         }          }
         my %unique_formats;          my %unique_formats;
         my @formatlines = &get_scantronformat_file();          my @formatlines = &Apache::lonnet::get_scantronformat_file();
         foreach my $line (@formatlines) {          foreach my $line (@formatlines) {
             chomp($line);              chomp($line);
             my @config = split(/:/,$line);              my @config = split(/:/,$line);
Line 7868  sub validate_uploaded_scantron_file { Line 9391  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 7886  sub validate_uploaded_scantron_file { Line 9410  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 7910  sub valid_file { Line 9457  sub valid_file {
 }  }
   
 sub scantron_download_scantron_data {  sub scantron_download_scantron_data {
     my ($r)=@_;      my ($r,$symb) = @_;
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData($symb);
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
     my $file=$env{'form.scantron_selectfile'};      my $file=$env{'form.scantron_selectfile'};
     if (! &valid_file($file)) {      if (! &valid_file($file)) {
  $r->print('   $r->print('
  <p>   <p>
     '.&mt('The requested file name was invalid.').'      '.&mt('The requested filename was invalid.').'
         </p>          </p>
 ');  ');
  $r->print(&show_grading_menu_form(&get_symb($r,1)));  
  return;   return;
     }      }
     my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;      my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
Line 7932  sub scantron_download_scantron_data { Line 9478  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 7944  sub scantron_download_scantron_data { Line 9490  sub scantron_download_scantron_data {
       '<a href="'.$skipped.'">','</a>').'        '<a href="'.$skipped.'">','</a>').'
     </p>      </p>
 ');  ');
     $r->print(&show_grading_menu_form(&get_symb($r,1)));  
     return '';      return '';
 }  }
   
 sub checkscantron_results {  sub checkscantron_results {
     my ($r) = @_;      my ($r,$symb) = @_;
     my ($symb)=&get_symb($r);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $grading_menu_button=&show_grading_menu_form($symb);  
     my $cid = $env{'request.course.id'};      my $cid = $env{'request.course.id'};
     my %lettdig = &letter_to_digits();      my %lettdig = &Apache::lonnet::letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
     my $cnum = $env{'course.'.$cid.'.num'};      my $cnum = $env{'course.'.$cid.'.num'};
     my $cdom = $env{'course.'.$cid.'.domain'};      my $cdom = $env{'course.'.$cid.'.domain'};
     my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});      my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});
     my %record;      my %record;
     my %scantron_config =      my %scantron_config =
         &Apache::grades::get_scantron_config($env{'form.scantron_format'});          &Apache::lonnet::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);
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           $r->print(&navmap_errormsg());
           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 7979  sub checkscantron_results { Line 9537  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,'Scantron/Submissions Comparison Status',      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
                                     'Progress of Scantron Data/Submission Records Comparison',$count,  
                                     'inline',undef,'checkscantron');  
     my ($username,$domain,$started);      my ($username,$domain,$started);
       &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     &scantron_get_maxbubble();  # Need the bubble lines array to parse.      if ($nav_error) {
           $r->print(&navmap_errormsg());
           return '';
       }
   
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
                                           'Processing first student');                                            'Processing first student');
Line 8005  sub checkscantron_results { Line 9564  sub checkscantron_results {
         my $scan_record=          my $scan_record=
             &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,              &Apache::grades::scantron_parse_scanline($line,$i,\%scantron_config,
                                                      $scan_data);                                                       $scan_data);
         unless ($uname=&Apache::grades::scantron_find_student($scan_record,$scan_data,          unless ($uname=&scantron_find_student($scan_record,$scan_data,
                                                               \%idmap,$i)) {                                                \%idmap,$i)) {
             &Apache::grades::scantron_add_delay(\@delayqueue,$line,              &Apache::grades::scantron_add_delay(\@delayqueue,$line,
                                 'Unable to find a student that matches',1);                                  'Unable to find a student that matches',1);
             next;              next;
Line 8019  sub checkscantron_results { Line 9578  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 8058  sub checkscantron_results { Line 9652  sub checkscantron_results {
                 if ($scandata{$pid} eq $record{$pid}) {                  if ($scandata{$pid} eq $record{$pid}) {
                     my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row';                      my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row';
                     $okstudents .= '<tr class="'.$css_class.'">'.                      $okstudents .= '<tr class="'.$css_class.'">'.
 '<td>'.&mt('Scantron').'</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('Scantron').'</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 8076  sub checkscantron_results { Line 9670  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('<p>'.
     $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');                &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 8097  sub checkscantron_results { Line 9702  sub checkscantron_results {
                  &Apache::loncommon::end_data_table()).'<br />'.                   &Apache::loncommon::end_data_table()).'<br />'.
                  &mt('Differences can occur if submissions were modified using manual grading after a bubblesheet grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.');                     &mt('Differences can occur if submissions were modified using manual grading after a bubblesheet grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.');  
     }      }
     $r->print('</form><br />'.$grading_menu_button);      $r->print('</form><br />');
     return;      return;
 }  }
   
 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 8112  sub verify_scantron_grading { Line 9718  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 8216  sub verify_scantron_grading { Line 9828  sub verify_scantron_grading {
     return ($counter,$record);      return ($counter,$record);
 }  }
   
 sub letter_to_digits {   
     my %lettdig = (  
                     A => 1,  
                     B => 2,  
                     C => 3,  
                     D => 4,  
                     E => 5,  
                     F => 6,  
                     G => 7,  
                     H => 8,  
                     I => 9,  
                     J => 0,  
                   );  
     return %lettdig;  
 }  
   
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
   
 #-------------------------- Menu interface -------------------------  #-------------------------- Menu interface -------------------------
 #  #
 #--- Show a Grading Menu button - Calls the next routine ---  #--- Href with symb and command ---
 sub show_grading_menu_form {  
     my ($symb)=@_;  
     my $result.='<br /><form action="/adm/grades" method="post">'."\n".  
  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".  
  '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".  
  '<input type="hidden" name="command" value="gradingmenu" />'."\n".  
  '<input type="submit" name="submit" value="'.&mt('Grading Menu').'" />'."\n".  
  '</form>'."\n";  
     return $result;  
 }  
   
 # -- Retrieve choices for grading form  sub href_symb_cmd {
 sub savedState {      my ($symb,$cmd)=@_;
     my %savedState = ();      return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.$cmd;
     if ($env{'form.saveState'}) {  
  foreach (split(/:/,$env{'form.saveState'})) {  
     my ($key,$value) = split(/=/,$_,2);  
     $savedState{$key} = $value;  
  }  
     }  
     return \%savedState;  
 }  }
   
 sub grading_menu {  sub grading_menu {
     my ($request) = @_;      my ($request,$symb) = @_;
     my ($symb)=&get_symb($request);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $probTitle = &Apache::lonnet::gettitle($symb);  
     my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);  
   
     $request->print($table);  
     my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),      my %fields = ('symb'=>&Apache::lonenc::check_encrypt($symb),
                   'handgrade'=>$hdgrade,                    'command'=>'individual');
                   'probTitle'=>$probTitle,  
                   'command'=>'submit_options',      my $url1a = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
                   'saveState'=>"",  
                   'gradingMenu'=>1,      $fields{'command'}='ungraded';
                   'showgrading'=>"yes");      my $url1b=&Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
     my $url1 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      $fields{'command'}='table';
       my $url1c=&Apache::lonhtmlcommon::build_url('grades/',\%fields);
   
       $fields{'command'}='all_for_one';
       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 8289  sub grading_menu { Line 9870  sub grading_menu {
           
     $fields{'command'} = 'scantron_selectphase';      $fields{'command'} = 'scantron_selectphase';
     my $url4 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url4 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
   
       $fields{'command'} = 'initialverifyreceipt';
       my $url5 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
           
     my @menu = ({ categorytitle=>'Course Grading',      my @menu = ({ categorytitle=>'Hand Grading',
             items =>[              items =>[
                         { linktext => 'Manual Grading/View Submissions',                          {       linktext => 'Select individual students to grade',
                     url => $url1,                                  url => $url1a,
                     permission => 'F',                                  permission => 'F',
                     icon => 'edit-find-replace.png',                                  icon => 'grade_students.png',
                     linktitle => 'Start the process of hand grading submissions.'                                  linktitle => 'Grade current resource for a selection of students.'
                           },
                           {       linktext => 'Grade ungraded submissions',
                                   url => $url1b,
                                   permission => 'F',
                                   icon => 'ungrade_sub.png',
                                   linktitle => 'Grade all submissions that have not been graded yet.'
                           },
   
                           {       linktext => 'Grading table',
                                   url => $url1c,
                                   permission => 'F',
                                   icon => 'grading_table.png',
                                   linktitle => 'Grade current resource for all students.'
                         },                          },
                           {       linktext => 'Grade page/folder for one student',
                                   url => $url1d,
                                   permission => 'F',
                                   icon => 'grade_PageFolder.png',
                                   linktitle => 'Grade all resources in current page/sequence/folder for one student.'
                           },
                           {       linktext => 'Download submitted files',
                                   url => $url1e,
                                   permission => 'F',
                                   icon => 'download_sub.png',
                                   linktitle => 'Download all files submitted by students.'
                           }]},
                            { categorytitle=>'Automated Grading',
                  items =>[
   
                    { linktext => 'Upload Scores',                     { linktext => 'Upload Scores',
                     url => $url2,                      url => $url2,
                     permission => 'F',                      permission => 'F',
Line 8310  sub grading_menu { Line 9922  sub grading_menu {
                     icon => 'addClickerInfoFile.png',                      icon => 'addClickerInfoFile.png',
                     linktitle => 'Specify a file containing the clicker information for this resource.'                      linktitle => 'Specify a file containing the clicker information for this resource.'
                    },                     },
                    { linktext => 'Grade/Manage/Review Scantron Forms',                     { 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',
                                   url => $url5,
                                   permission => 'F',
                                   icon => 'receipt_number.png',
                                   linktitle => 'Verify a system-generated receipt number for correct problem solution.'
                               }
   
                     ]                      ]
             });              });
   
     #$fields{'command'} = 'verify';  
     #$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);  
     #  
     # Create the menu      # Create the menu
     my $Str;      my $Str;
     # $Str .= '<h2>'.&mt('Please select a grading task').'</h2>';  
     $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="handgrade"   value="'.$hdgrade.'" />'."\n".  
  '<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".  
  '<input type="hidden" name="saveState"   value="" />'."\n".  
  '<input type="hidden" name="gradingMenu" value="1" />'."\n".  
  '<input type="hidden" name="showgrading" value="yes" />'."\n";  
   
     $Str .= Apache::lonhtmlcommon::generate_menu(@menu);  
     #$menudata->{'jscript'}  
     $Str .='<hr /><input type="button" value="'.&mt('Verify Receipt').'" '.  
         ' onClick="javascript:checkChoice(document.forms.gradingMenu,\'5\',\'verify\')" '.  
         ' /> '.  
         &Apache::lonnet::recprefix($env{'request.course.id'}).  
         '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')" />';  
   
     $Str .="</form>\n";  
     my $receiptalert = &mt("Please enter a receipt number given by a student in the receipt box.");  
     $request->print(<<GRADINGMENUJS);  
 <script type="text/javascript" language="javascript">  
     function checkChoice(formname,val,cmdx) {  
  if (val <= 2) {  
     var cmd = radioSelection(formname.radioChoice);  
     var cmdsave = cmd;  
  } else {  
     cmd = cmdx;  
     cmdsave = 'submission';  
  }  
  formname.command.value = cmd;  
  if (val < 5) formname.submit();  
  if (val == 5) {  
     if (!checkReceiptNo(formname,'notOK')) {   
         return false;  
     } else {  
         formname.submit();  
     }  
  }  
     }  
   
     function checkReceiptNo(formname,nospace) {      $Str .= &Apache::lonhtmlcommon::generate_menu(@menu);
  var receiptNo = formname.receipt.value;  
  var checkOpt = false;  
  if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}  
  if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}  
  if (checkOpt) {  
     alert("$receiptalert");  
     formname.receipt.value = "";  
     formname.receipt.focus();  
     return false;  
  }  
  return true;  
     }  
 </script>  
 GRADINGMENUJS  
     &commonJSfunctions($request);  
     return $Str;          return $Str;    
 }  }
   
   sub ungraded {
       my ($request)=@_;
       &submit_options($request);
   }
   
 #--- Displays the submissions first page -------  sub submit_options_sequence {
 sub submit_options {      my ($request,$symb) = @_;
     my ($request) = @_;  
     my ($symb)=&get_symb($request);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $probTitle = &Apache::lonnet::gettitle($symb);      &commonJSfunctions($request);
       my $result;
   
     my $receiptalert = &mt("Please enter a receipt number given by a student in the receipt box.");       $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
     $request->print(<<GRADINGMENUJS);          '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
 <script type="text/javascript" language="javascript">      $result.=&selectfield(0).
     function checkChoice(formname,val,cmdx) {              '<input type="hidden" name="command" value="pickStudentPage" />
  if (val <= 2) {              <div>
     var cmd = radioSelection(formname.radioChoice);                <input type="submit" value="'.&mt('Next').' &rarr;" />
     var cmdsave = cmd;              </div>
  } else {          </div>
     cmd = cmdx;    </form>';
     cmdsave = 'submission';      return $result;
  }  }
  formname.command.value = cmd;  
  formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+  sub submit_options_table {
     ":saveSub="+pullDownSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status);      my ($request,$symb) = @_;
  if (val < 5) formname.submit();      if (!$symb) {return '';}
  if (val == 5) {  
     if (!checkReceiptNo(formname,'notOK')) { return false;}  
     formname.submit();  
  }  
  if (val < 7) formname.submit();  
     }  
   
     function checkReceiptNo(formname,nospace) {  
  var receiptNo = formname.receipt.value;  
  var checkOpt = false;  
  if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}  
  if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}  
  if (checkOpt) {  
     alert("$receiptalert");  
     formname.receipt.value = "";  
     formname.receipt.focus();  
     return false;  
  }  
  return true;  
     }  
 </script>  
 GRADINGMENUJS  
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);  
     my $result;      my $result;
     my (undef,$sections) = &getclasslist('all','0');  
     my $savedState = &savedState();      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
     my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'});          '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'});  
     my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'});      $result.=&selectfield(1).
     my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});              '<input type="hidden" name="command" value="viewgrades" />
               <div>
     # Preselect sections                <input type="submit" value="'.&mt('Next').' &rarr;" />
     my $selsec="";              </div>
     if (ref($sections)) {          </div>
         foreach my $section (sort(@$sections)) {    </form>';
             $selsec.='<option value="'.$section.'" '.      return $result;
                 ($saveSec eq $section ? 'selected="selected"':'').'>'.$section.'</option>'."\n";  }
   
   sub submit_options_download {
       my ($request,$symb) = @_;
       if (!$symb) {return '';}
   
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay,$numdropbox) =
           &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&mt('An error occurred retrieving response types'));
           return;
       }
       unless ($numessay) {
           $request->print(&mt('No essayresponse items found'));
           return;
       }
       my $table;
       if (ref($partlist) eq 'ARRAY') {
           if (scalar(@$partlist) > 1 ) {
               $table = &showResourceInfo($symb,$partlist,$responseType,'gradingMenu',1,1);
         }          }
     }      }
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      &commonJSfunctions($request);
  '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".  
  '<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".  
  '<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".  
  '<input type="hidden" name="command"     value="" />'."\n".  
  '<input type="hidden" name="saveState"   value="" />'."\n".  
  '<input type="hidden" name="gradingMenu" value="1" />'."\n".  
  '<input type="hidden" name="showgrading" value="yes" />'."\n";  
   
       my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
           $table."\n".
           '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     $result.='      $result.='
 <h2>  <h2>
   '.&mt('Grade Current Resource').'    '.&mt('Select Students for whom to Download Submitted Files').'
 </h2>  </h2>'.&selectfield(1).'
 <div>                  <input type="hidden" name="command" value="downloadfileslink" />
   '.$table.'                <input type="submit" value="'.&mt('Next').' &rarr;" />
 </div>              </div>
             </div>
   
   
     </form>';
       return $result;
   }
   
   #--- Displays the submissions first page -------
   sub submit_options {
       my ($request,$symb) = @_;
       if (!$symb) {return '';}
   
       &commonJSfunctions($request);
       my $result;
   
       $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
    '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
       $result.=&selectfield(1).'
                   <input type="hidden" name="command" value="submission" />
                 <input type="submit" value="'.&mt('Next').' &rarr;" />
               </div>
             </div>
     </form>';
       return $result;
   }
   
   sub selectfield {
      my ($full)=@_;
      my %options =
          (&substatus_options,
           'select_form_order' => ['yes','queued','graded','incorrect','all']);
      my $result='<div class="LC_columnSection">
   
 <div class="LC_columnSection">  
     
     <fieldset>      <fieldset>
       <legend>        <legend>
        '.&mt('Sections').'         '.&mt('Sections').'
       </legend>        </legend>
       <select name="section" multiple="multiple" size="5">'."\n";        '.&Apache::lonstatistics::SectionSelect('section','multiple',5).'
     $result.= $selsec;  
     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="selected"' : ''). '>all</option></select> &nbsp; ';  
     $result.='  
     </fieldset>      </fieldset>
     
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Groups').'          '.&mt('Groups').'
       </legend>        </legend>
       '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'        '.&Apache::lonstatistics::GroupSelect('group','multiple',5).'
     </fieldset>      </fieldset>
      
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Access Status').'          '.&mt('Access Status').'
       </legend>        </legend>
       '.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,5,undef,'mult').'        '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'
     </fieldset>      </fieldset>';
         if ($full) {
           $result.='
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Submission Status').'          '.&mt('Submission Status').'
       </legend>        </legend>'.
       <select name="submitonly" size="5">         &Apache::loncommon::select_form('all','submitonly',\%options).
          <option value="yes" '.      ($saveSub eq 'yes'       ? 'selected="selected"' : '').'>'.&mt('with submissions').'</option>     '</fieldset>';
          <option value="queued" '.   ($saveSub eq 'queued'    ? 'selected="selected"' : '').'>'.&mt('in grading queue').'</option>      }
          <option value="graded" '.   ($saveSub eq 'graded'    ? 'selected="selected"' : '').'>'.&mt('with ungraded submissions').'</option>      $result.='</div><br />';
          <option value="incorrect" '.($saveSub eq 'incorrect' ? 'selected="selected"' : '').'>'.&mt('with incorrect submissions').'</option>  
                  <option value="all" '.      ($saveSub eq 'all'       ? 'selected="selected"' : '').'>'.&mt('with any status').'</option>  
       </select>  
     </fieldset>  
     
 </div>  
   
 <br />  
           <div>  
             <div>  
               <label>  
                 <input type="radio" name="radioChoice" value="submission" '.  
                   ($saveCmd eq 'submission' ? 'checked="checked"' : '').' /> '.  
              &mt('Select individual students to grade and view submissions.').'  
       </label>   
             </div>  
             <div>  
       <label>  
                 <input type="radio" name="radioChoice" value="viewgrades" '.  
                   ($saveCmd eq 'viewgrades' ? 'checked="checked"' : '').' /> '.  
                     &mt('Grade all selected students in a grading table.').'  
               </label>  
             </div>  
             <div>  
       <input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next').' &rarr;" />  
             </div>  
           </div>  
   
   
         <h2>  
          '.&mt('Grade Complete Folder for One Student').'  
         </h2>  
         <div>  
             <div>  
               <label>  
                 <input type="radio" name="radioChoice" value="pickStudentPage" '.  
   ($saveCmd eq 'pickStudentPage' ? 'checked="checked"' : '').' /> '.  
   &mt('The <b>complete</b> page/sequence/folder: For one student').'  
               </label>  
             </div>  
             <div>  
       <input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next').' &rarr;" />  
             </div>  
         </div>  
   </form>';  
     $result .= &show_grading_menu_form($symb);  
     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 8570  sub init_perm { Line 10130  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 8632  sub clicker_grading_parameters { Line 10207  sub clicker_grading_parameters {
 }  }
   
 sub process_clicker {  sub process_clicker {
     my ($r)=@_;      my ($r,$symb)=@_;
     my ($symb)=&get_symb($r);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $result=&checkforfile_js();      my $result=&checkforfile_js();
     $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);      $result.=&Apache::loncommon::start_data_table().
     my ($table) = &showResourceInfo($symb,$env{'form.probTitle'});               &Apache::loncommon::start_data_table_header_row().
     $result.=$table;               '<th>'.&mt('Specify a file containing clicker information and set grading options.').'</th>'.
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";               &Apache::loncommon::end_data_table_header_row().
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";               &Apache::loncommon::start_data_table_row()."<td>\n";
     $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource.').  
         '</b></td></tr>'."\n";  
     $result.='<tr bgcolor=#ffffe6><td>'."\n";  
 # Attempt to restore parameters from last session, set defaults if not present  # Attempt to restore parameters from last session, set defaults if not present
     my %Saveable_Parameters=&clicker_grading_parameters();      my %Saveable_Parameters=&clicker_grading_parameters();
     &Apache::loncommon::restore_course_settings('grades_clicker',      &Apache::loncommon::restore_course_settings('grades_clicker',
Line 8660  sub process_clicker { Line 10231  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 8670  sub process_clicker { Line 10241  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.=<<ENDUPFORM;      $result.= &Apache::lonhtmlcommon::scripttag(<<ENDUPFORM);
 <script type="text/javascript">  
 function sanitycheck() {  function sanitycheck() {
 // Accept only integer percentages  // Accept only integer percentages
    document.forms.gradesupload.pcorrect.value=Math.round(document.forms.gradesupload.pcorrect.value);     document.forms.gradesupload.pcorrect.value=Math.round(document.forms.gradesupload.pcorrect.value);
Line 8712  function sanitycheck() { Line 10283  function sanitycheck() {
 // Remember the old state  // Remember the old state
    document.forms.gradesupload.waschecked.value=newgradingchoice;     document.forms.gradesupload.waschecked.value=newgradingchoice;
 }  }
 </script>  ENDUPFORM
       $result.= <<ENDUPFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <input type="hidden" name="symb" value="$symb" />  <input type="hidden" name="symb" value="$symb" />
 <input type="hidden" name="command" value="processclickerfile" />  <input type="hidden" name="command" value="processclickerfile" />
 <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />  
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  
 <input type="file" name="upfile" size="50" />  <input type="file" name="upfile" size="50" />
 <br /><label>$type: $selectform</label>  <br /><label>$type: $selectform</label>
 <br /><label><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onClick="sanitycheck()" />$attendance </label>  ENDUPFORM
 <br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onClick="sanitycheck()" />$personnel</label>      $result.='</td>'.&Apache::loncommon::end_data_table_row().
 <br /><label><input type="radio" name="gradingmechanism" value="specific"$checked{'specific'} onClick="sanitycheck()" />$specific </label>                       &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="specific"$checked{'specific'} onclick="sanitycheck()" />$specific </label>
 <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />  <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />
 <br /><label><input type="radio" name="gradingmechanism" value="given"$checked{'given'} onClick="sanitycheck()" />$given </label>  <br /><label><input type="radio" name="gradingmechanism" value="given"$checked{'given'} onclick="sanitycheck()" />$given </label>
 <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
 <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onChange="sanitycheck()" /></label>      $result.='</td>'.&Apache::loncommon::end_data_table_row().
 <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="$upload" />                       &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 /><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().
     $result.=&show_grading_menu_form($symb);               &Apache::loncommon::end_data_table();
     return $result;      return $result;
 }  }
   
 sub process_clicker_file {  sub process_clicker_file {
     my ($r)=@_;      my ($r,$symb) = @_;
     my ($symb)=&get_symb($r);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my %Saveable_Parameters=&clicker_grading_parameters();      my %Saveable_Parameters=&clicker_grading_parameters();
     &Apache::loncommon::store_course_settings('grades_clicker',      &Apache::loncommon::store_course_settings('grades_clicker',
                                               \%Saveable_Parameters);                                                \%Saveable_Parameters);
       my $result='';
     my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});  
     if (($env{'form.gradingmechanism'} eq 'specific') && ($env{'form.specificid'}!~/\w/)) {      if (($env{'form.gradingmechanism'} eq 'specific') && ($env{'form.specificid'}!~/\w/)) {
  $result.='<span class="LC_error">'.&mt('You need to specify a clicker ID for the correct answer').'</span>';   $result.='<span class="LC_error">'.&mt('You need to specify a clicker ID for the correct answer').'</span>';
  return $result.&show_grading_menu_form($symb);   return $result;
     }      }
     if (($env{'form.gradingmechanism'} eq 'given') && ($env{'form.givenanswer'}!~/\S/)) {      if (($env{'form.gradingmechanism'} eq 'given') && ($env{'form.givenanswer'}!~/\S/)) {
         $result.='<span class="LC_error">'.&mt('You need to specify the correct answer').'</span>';          $result.='<span class="LC_error">'.&mt('You need to specify the correct answer').'</span>';
         return $result.&show_grading_menu_form($symb);          return $result;
     }      }
     my $foundgiven=0;      my $foundgiven=0;
     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 8800  sub process_clicker_file { Line 10374  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.&show_grading_menu_form($symb);                   &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.&show_grading_menu_form($symb);          return $result;
       }
       my $mimetype;
       if ($env{'form.upfiletype'} eq 'iclicker') {
           my $mm = new File::MMagic;
           $mimetype = $mm->checktype_contents($env{'form.upfile'});
           unless (($mimetype eq 'text/plain') || ($mimetype eq 'text/html')) {
               $result.= '<p>'.
                   &Apache::lonhtmlcommon::confirm_success(
                       &mt('File format is neither csv (iclicker 6) nor xml (iclicker 7)'),1).'</p>';
               return $result;
           }
       } elsif (($env{'form.upfiletype'} ne 'interwrite') && ($env{'form.upfiletype'} ne 'turning')) {
           $result .= '<p>'.
               &Apache::lonhtmlcommon::confirm_success(
                   &mt('Invalid clicker type: choose one of: i>clicker, Interwrite PRS, or Turning Technologies.'),1).'</p>';
           return $result;
     }      }
   
 # Were able to get all the info needed, now analyze the file  # Were able to get all the info needed, now analyze the 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" />
 <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />  
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  
 <input type="hidden" name="gradingmechanism" value="$env{'form.gradingmechanism'}" />  <input type="hidden" name="gradingmechanism" value="$env{'form.gradingmechanism'}" />
 <input type="hidden" name="pcorrect" value="$env{'form.pcorrect'}" />  <input type="hidden" name="pcorrect" value="$env{'form.pcorrect'}" />
 <input type="hidden" name="pincorrect" value="$env{'form.pincorrect'}" />  <input type="hidden" name="pincorrect" value="$env{'form.pincorrect'}" />
Line 8839  ENDHEADER Line 10430  ENDHEADER
     my $errormsg='';      my $errormsg='';
     my $number=0;      my $number=0;
     if ($env{'form.upfiletype'} eq 'iclicker') {      if ($env{'form.upfiletype'} eq 'iclicker') {
  ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);          if ($mimetype eq 'text/plain') {
     }              ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);
     if ($env{'form.upfiletype'} eq 'interwrite') {          } elsif ($mimetype eq 'text/html') {
               ($errormsg,$number)=&iclickerxml_eval(\@questiontitles,\%responses);
           }
       } elsif ($env{'form.upfiletype'} eq 'interwrite') {
         ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
       } elsif ($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.'" />'.
Line 8851  ENDHEADER Line 10447  ENDHEADER
              '<br />';               '<br />';
     if (($env{'form.gradingmechanism'} eq 'given') && ($number!=$foundgiven)) {      if (($env{'form.gradingmechanism'} eq 'given') && ($number!=$foundgiven)) {
        $result.='<span class="LC_error">'.&mt('Number of given answers does not agree with number of questions in file.').'</span>';         $result.='<span class="LC_error">'.&mt('Number of given answers does not agree with number of questions in file.').'</span>';
        return $result.&show_grading_menu_form($symb);         return $result;
     }       } 
 # Remember Question Titles  # Remember Question Titles
 # FIXME: Possibly need delimiter other than ":"  # FIXME: Possibly need delimiter other than ":"
Line 8871  ENDHEADER Line 10467  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 8885  ENDHEADER Line 10483  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,'',$id);
           $unknown_count++;            $unknown_count++;
        }         }
     }      }
Line 8898  ENDHEADER Line 10498  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 8911  ENDHEADER Line 10511  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().
     return $result.&show_grading_menu_form($symb);               &Apache::loncommon::end_data_table();
       return $result;
 }  }
   
 sub iclicker_eval {  sub iclicker_eval {
Line 8935  sub iclicker_eval { Line 10536  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 8943  sub iclicker_eval { Line 10545  sub iclicker_eval {
     return ($errormsg,$number);      return ($errormsg,$number);
 }  }
   
   sub iclickerxml_eval {
       my ($questiontitles,$responses)=@_;
       my $number=0;
       my $errormsg='';
       my @state;
       my %respbyid;
       my $p = HTML::Parser->new
       (
           xml_mode => 1,
           start_h =>
               [sub {
                    my ($tagname,$attr) = @_;
                    push(@state,$tagname);
                    if ("@state" eq "ssn p") {
                        my $title = $attr->{qn};
                        $title =~ s/(^\s+|\s+$)//g;
                        $questiontitles->[$number]=$title;
                    } elsif ("@state" eq "ssn p v") {
                        my $id = $attr->{id};
                        my $entry = $attr->{ans};
                        $id=~s/^[\#0]+//;
                        $entry =~s/[^a-zA-Z0-9\.\*\-\+]+//g;
                        $respbyid{$id}[$number] = $entry;
                    }
               }, "tagname, attr"],
            end_h =>
                  [sub {
                      my ($tagname) = @_;
                      if ("@state" eq "ssn p") {
                          $number++;
                      }
                      pop(@state);
                   }, "tagname"],
       );
   
       $p->parse($env{'form.upfile'});
       $p->eof;
       foreach my $id (keys(%respbyid)) {
           $responses->{$id}=join(',',@{$respbyid{$id}});
       }
       return ($errormsg,$number);
   }
   
 sub interwrite_eval {  sub interwrite_eval {
     my ($questiontitles,$responses)=@_;      my ($questiontitles,$responses)=@_;
     my $number=0;      my $number=0;
Line 8974  sub interwrite_eval { Line 10619  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)=@_;      my ($r,$symb) = @_;
     my ($symb)=&get_symb($r);  
     if (!$symb) {return '';}      if (!$symb) {return '';}
 # See which part we are saving to  # See which part we are saving to
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my $res_error;
       my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
       if ($res_error) {
           return &navmap_errormsg();
       }
 # 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) = &showResourceInfo($symb,$env{'form.probTitle'});      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 9006  ENDHEADER Line 10676  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 9038  ENDHEADER Line 10709  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 9078  ENDHEADER Line 10767  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().
     return $result.&show_grading_menu_form($symb);               &Apache::loncommon::end_data_table();
       return $result;
   }
   
   sub navmap_errormsg {
       return '<div class="LC_error">'.
              &mt('An error occurred retrieving information about resources in the course.').'<br />'.
              &mt('It is recommended that you [_1]re-initialize the course[_2] and then return to this grading page.','<a href="/adm/roles?selectrole=1&newrole='.$env{'request.role'}.'">','</a>').
              '</div>';
   }
   
   sub startpage {
       my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js,$onload,$divforres) = @_;
       my %args;
       if ($onload) {
            my %loaditems = (
                           'onload' => $onload,
                         );
            $args{'add_entries'} = \%loaditems;
       }
       if ($nomenu) {
           $args{'only_body'} = 1;
           $r->print(&Apache::loncommon::start_page("Student's Version",$js,\%args));
       } else {
           unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
           $args{'bread_crumbs'} = $crumbs;
           $r->print(&Apache::loncommon::start_page('Grading',$js,\%args));
       }
       unless ($nodisplayflag) {
          $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));
       }
   }
   
   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,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'});
     my $symb=&get_symb($request,1);  
   # see what command we need to execute
    
     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));
     }      }
   
   # see what the symb is
   
       my $symb=$env{'form.symb'};
       unless ($symb) {
          (my $url=$env{'form.url'}) =~ s-^https*://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
          $symb=&Apache::lonnet::symbread($url);
       }
       &Apache::lonenc::check_decrypt(\$symb);
   
     $ssi_error = 0;      $ssi_error = 0;
     my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];      if (($symb eq '' || $command eq '') && ($env{'request.course.id'})) {
     $request->print(&Apache::loncommon::start_page('Grading',undef,  #
                                           {'bread_crumbs' => $brcrum}));  # Not called from a resource, but inside a course
     if ($symb eq '' && $command eq '') {  #
  if ($env{'user.adv'}) {          &startpage($request,undef,[],1,1);
     if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&          &select_problem($request);
  ($env{'form.codethree'})) {  
  my $token=$env{'form.codeone'}.'*'.$env{'form.codetwo'}.'*'.  
     $env{'form.codethree'};  
  my ($tsymb,$tuname,$tudom,$tcrsid)=  
     &Apache::lonnet::checkin($token);  
  if ($tsymb) {  
     my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb);  
     if (&Apache::lonnet::allowed('mgr',$tcrsid)) {  
  $request->print(&ssi_with_retries('/res/'.$url, $ssi_retries,  
   ('grade_username' => $tuname,  
    'grade_domain' => $tudom,  
    'grade_courseid' => $tcrsid,  
    'grade_symb' => $tsymb)));  
     } else {  
  $request->print('<h3>Not authorized: '.$token.'</h3>');  
     }  
  } else {  
     $request->print('<h3>Not a valid DocID: '.$token.'</h3>');  
  }  
     } else {  
  $request->print(&Apache::lonxml::tokeninputfield());  
     }  
  }  
     } else {      } else {
  &init_perm();          if ($command eq 'submission' && $perm{'vgr'}) {
  if ($command eq 'submission' && $perm{'vgr'}) {              my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);
     ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));              if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
  } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {                  ($stuvcurrent,$stuvdisp,$versionform,$js) =
     &pickStudentPage($request);                      &choose_task_version_form($symb,$env{'form.student'},
  } elsif ($command eq 'displayPage' && $perm{'vgr'}) {                                                $env{'form.userdom'});
     &displayPage($request);              }
  } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {              my $divforres;
     &updateGradeByPage($request);              if ($env{'form.student'} eq '') {
  } elsif ($command eq 'processGroup' && $perm{'vgr'}) {                  $js .= &part_selector_js();
     &processGroup($request);                  $onload = "toggleParts('gradesub');";
  } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {              } else {
     $request->print(&grading_menu($request));                  $divforres = 1;
  } elsif ($command eq 'submit_options' && $perm{'vgr'}) {              }
     $request->print(&submit_options($request));              &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js,$onload,$divforres);
  } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {              if ($versionform) {
     $request->print(&viewgrades($request));                  if ($divforres) {
  } elsif ($command eq 'handgrade' && $perm{'mgr'}) {                      $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
     $request->print(&processHandGrade($request));                  }
  } elsif ($command eq 'editgrades' && $perm{'mgr'}) {                  $request->print($versionform);
     $request->print(&editgrades($request));              }
  } elsif ($command eq 'verify' && $perm{'vgr'}) {              ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));
     $request->print(&verifyreceipt($request));          } 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'}) {
               &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                          {href=>'',text=>'Select student'}],1,1);
               &pickStudentPage($request,$symb);
           } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
               &startpage($request,$symb,
                                         [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                          {href=>'',text=>'Select student'},
                                          {href=>'',text=>'Grade student'}],1,1);
               &displayPage($request,$symb);
           } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
               &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                          {href=>'',text=>'Select student'},
                                          {href=>'',text=>'Grade student'},
                                          {href=>'',text=>'Store grades'}],1,1);
               &updateGradeByPage($request,$symb);
           } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>'',text=>'...'},
                                          {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,undef,undef,1);
               &processGroup($request,$symb);
           } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
               &startpage($request,$symb);
               $request->print(&grading_menu($request,$symb));
           } elsif ($command eq 'individual' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);
               $request->print(&submit_options($request,$symb));
           } elsif ($command eq 'ungraded' && $perm{'vgr'}) {
               my $js = &part_selector_js();
               my $onload = "toggleParts('gradesub');";
               &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}],
                          undef,undef,undef,undef,undef,$js,$onload);
               $request->print(&listStudents($request,$symb,'graded'));
           } elsif ($command eq 'table' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>"", text=>"Grading table"}]);
               $request->print(&submit_options_table($request,$symb));
           } elsif ($command eq 'all_for_one' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>'',text=>'Grade page/folder for one student'}],1,1);
               $request->print(&submit_options_sequence($request,$symb));
           } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},{href=>'', text=>"Modify grades"}]);
               $request->print(&viewgrades($request,$symb));
           } elsif ($command eq 'handgrade' && $perm{'mgr'}) {
               &startpage($request,$symb,[{href=>'',text=>'...'},
                                          {href=>'',text=>'Store grades'}]);
               $request->print(&processHandGrade($request,$symb));
           } elsif ($command eq 'editgrades' && $perm{'mgr'}) {
               &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"table"), text=>"Grading table"},
                                          {href=>&href_symb_cmd($symb,'viewgrades').'&group=all&section=all&Status=Active',
                                                                                text=>"Modify grades"},
                                          {href=>'', text=>"Store grades"}]);
               $request->print(&editgrades($request,$symb));
           } elsif ($command eq 'initialverifyreceipt' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>'',text=>'Verify Receipt Number'}]);
               $request->print(&initialverifyreceipt($request,$symb));
           } elsif ($command eq 'verify' && $perm{'vgr'}) {
               &startpage($request,$symb,[{href=>&href_symb_cmd($symb,"initialverifyreceipt"),text=>'Verify Receipt Number'},
                                          {href=>'',text=>'Verification Result'}]);
               $request->print(&verifyreceipt($request,$symb));
         } elsif ($command eq 'processclicker' && $perm{'mgr'}) {          } elsif ($command eq 'processclicker' && $perm{'mgr'}) {
             $request->print(&process_clicker($request));              &startpage($request,$symb,[{href=>'', text=>'Process clicker'}]);
               $request->print(&process_clicker($request,$symb));
         } elsif ($command eq 'processclickerfile' && $perm{'mgr'}) {          } elsif ($command eq 'processclickerfile' && $perm{'mgr'}) {
             $request->print(&process_clicker_file($request));              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'processclicker'), text=>'Process clicker'},
                                          {href=>'', text=>'Process clicker file'}]);
               $request->print(&process_clicker_file($request,$symb));
         } elsif ($command eq 'assignclickergrades' && $perm{'mgr'}) {          } elsif ($command eq 'assignclickergrades' && $perm{'mgr'}) {
             $request->print(&assign_clicker_grades($request));              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'processclicker'), text=>'Process clicker'},
  } elsif ($command eq 'csvform' && $perm{'mgr'}) {                                         {href=>'', text=>'Process clicker file'},
     $request->print(&upcsvScores_form($request));                                         {href=>'', text=>'Store grades'}]);
  } elsif ($command eq 'csvupload' && $perm{'mgr'}) {              $request->print(&assign_clicker_grades($request,$symb));
     $request->print(&csvupload($request));          } elsif ($command eq 'csvform' && $perm{'mgr'}) {
  } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $request->print(&csvuploadmap($request));              $request->print(&upcsvScores_form($request,$symb));
  } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {          } elsif ($command eq 'csvupload' && $perm{'mgr'}) {
     if ($env{'form.associate'} ne 'Reverse Association') {              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
  $request->print(&csvuploadoptions($request));              $request->print(&csvupload($request,$symb));
     } else {          } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
  if ( $env{'form.upfile_associate'} ne 'reverse' ) {              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $env{'form.upfile_associate'} = 'reverse';              $request->print(&csvuploadmap($request,$symb));
  } else {          } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {
     $env{'form.upfile_associate'} = 'forward';              if ($env{'form.associate'} ne 'Reverse Association') {
  }                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
  $request->print(&csvuploadmap($request));                  $request->print(&csvuploadoptions($request,$symb));
     }              } else {
  } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {                  if ( $env{'form.upfile_associate'} ne 'reverse' ) {
     $request->print(&csvuploadassign($request));                      $env{'form.upfile_associate'} = 'reverse';
  } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {                  } else {
     $request->print(&scantron_selectphase($request));                      $env{'form.upfile_associate'} = 'forward';
   } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {                  }
      $request->print(&scantron_do_warning($request));                  &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
  } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {                  $request->print(&csvuploadmap($request,$symb));
     $request->print(&scantron_validate_file($request));              }
  } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {          } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
     $request->print(&scantron_process_students($request));              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
   } elsif ($command eq 'scantronupload' &&               $request->print(&csvuploadassign($request,$symb));
   (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||          } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
      $request->print(&scantron_upload_scantron_data($request));                          undef,undef,undef,undef,'toggleScantab(document.rules);');
   } elsif ($command eq 'scantronupload_save' &&              $request->print(&scantron_selectphase($request,undef,$symb));
   (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||          } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
      $request->print(&scantron_upload_scantron_data_save($request));              $request->print(&scantron_do_warning($request,$symb));
   } elsif ($command eq 'scantron_download' &&          } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
  &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
      $request->print(&scantron_download_scantron_data($request));              $request->print(&scantron_validate_file($request,$symb));
           } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
               $request->print(&scantron_process_students($request,$symb));
           } elsif ($command eq 'scantronupload' &&
                    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
                     &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
                          undef,undef,undef,undef,'toggleScantab(document.rules);');
               $request->print(&scantron_upload_scantron_data($request,$symb));
           } elsif ($command eq 'scantronupload_save' &&
                    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
                     &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
               $request->print(&scantron_upload_scantron_data_save($request,$symb));
           } elsif ($command eq 'scantron_download' &&
                    &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
               $request->print(&scantron_download_scantron_data($request,$symb));
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {          } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             $request->print(&checkscantron_results($request));                   &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
  } elsif ($command) {              $request->print(&checkscantron_results($request,$symb));
     $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');          } elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {
  }              my $js = &part_selector_js();
               my $onload = "toggleParts('gradingMenu');";
               &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}],
                          undef,undef,undef,undef,undef,$js,$onload);
               $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 submitted files'}],
                  undef,undef,undef,undef,undef,undef,undef,1);
               $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
               &submit_download_link($request,$symb);
           } elsif ($command) {
               &startpage($request,$symb,[{href=>'', text=>'Access denied'}]);
               $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());      $request->print(&Apache::loncommon::end_page());
     &reset_caches();      &reset_caches();
     return '';      return OK;
 }  }
   
 1;  1;
Line 9244  At least the logic that drives this has Line 11076  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 9289  ssi_with_retries() Line 11121  ssi_with_retries()
     $r           - Apache request object      $r           - Apache request object
     $i           - number of the current scanline      $i           - number of the current scanline
     $scan_record - hash ref as returned from &scantron_parse_scanline()      $scan_record - hash ref as returned from &scantron_parse_scanline()
     $scan_config - hash ref as returned from &get_scantron_config()      $scan_config - hash ref as returned from &Apache::lonnet::get_scantron_config()
     $line        - full contents of the current scanline      $line        - full contents of the current scanline
     $error       - error condition, valid values are      $error       - error condition, valid values are
                    'incorrectCODE', 'duplicateCODE',                     'incorrectCODE', 'duplicateCODE',
Line 9306  ssi_with_retries() Line 11138  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:
          $nav_error  - Reference to scalar which is a flag to indicate a
                         failure to retrieve a navmap object.
          if $nav_error is set to 1 by scantron_get_maxbubble(), the 
          calling routine should trap the error condition and display the warning
          found in &navmap_errormsg().
   
          $scantron_config - Reference to bubblesheet format configuration hash.
   
    Returns the maximum number of bubble lines that are expected to     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 9317  ssi_with_retries() Line 11168  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 9331  ssi_with_retries() Line 11182  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 9351  ssi_with_retries() Line 11202  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 9365  ssi_with_retries() Line 11216  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 9373  ssi_with_retries() Line 11224  ssi_with_retries()
    Validates all scanlines in the selected file to not have any     Validates all scanlines in the selected file to not have any
    invalid or underspecified student/employee IDs     invalid or underspecified student/employee IDs
   
   =item navmap_errormsg() :
   
      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.  
   
 =back  =back
   
 =cut  =cut

Removed from v.1.576  
changed lines
  Added in v.1.596.2.12.2.53


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