Diff for /loncom/homework/grades.pm between versions 1.596.2.5 and 1.596.2.12.2.10

version 1.596.2.5, 2011/10/10 18:27:34 version 1.596.2.12.2.10, 2012/12/10 13:28:54
Line 52  use POSIX qw(floor); Line 52  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 124  sub getpartlist { Line 125  sub getpartlist {
 # --- Get the symbolic name of a problem and the url  # --- Get the symbolic name of a problem and the url
 sub get_symb {  sub get_symb {
     my ($request,$silent) = @_;      my ($request,$silent) = @_;
     (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;      my $symb=$env{'form.symb'};
     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));      unless ($symb) {
     if ($symb eq '') {           (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
  if (!$silent) {          $symb = &Apache::lonnet::symbread($url);
             $request->print(&mt("Unable to handle ambiguous references: [_1].",$url));          if ($symb eq '') { 
     return ();      if (!$silent) {
  }                  $request->print(&mt("Unable to handle ambiguous references: [_1].",$url));
           return ();
       }
           }
     }      }
     &Apache::lonenc::check_decrypt(\$symb);      &Apache::lonenc::check_decrypt(\$symb);
     return ($symb);      return ($symb);
Line 260  sub showResourceInfo { Line 264  sub showResourceInfo {
 sub reset_caches {  sub reset_caches {
     &reset_analyze_cache();      &reset_analyze_cache();
     &reset_perm();      &reset_perm();
       &reset_old_essays();
 }  }
   
 {  {
Line 272  sub reset_caches { Line 277  sub reset_caches {
     }      }
   
     sub get_analyze {      sub get_analyze {
  my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed)=@_;   my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed,$bubbles_per_row)=@_;
  my $key = "$symb\0$uname\0$udom";   my $key = "$symb\0$uname\0$udom";
         if ($type eq 'randomizetry') {          if ($type eq 'randomizetry') {
             if ($trial ne '') {              if ($trial ne '') {
Line 306  sub reset_caches { Line 311  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') {          if ($type eq 'randomizetry') {
             $form{'grade_questiontype'} = $type;              $form{'grade_questiontype'} = $type;
             if ($rndseed ne '') {              if ($rndseed ne '') {
Line 346  sub reset_caches { Line 354  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) = @_;
         my (%analysis,@parts);          my (%analysis,@parts);
         if (ref($resource)) {          if (ref($resource)) {
             my $symb = $resource->symb();              my $symb = $resource->symb();
Line 354  sub reset_caches { Line 362  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);              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 416  sub cleanRecord { Line 426  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 ($toprow,$bottomrow);   my ($toprow,$bottomrow);
Line 738  sub compute_points { Line 748  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 755  sub most_similar { Line 769  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 769  sub most_similar { Line 783  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 1605  INNERJS Line 1619  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=700,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 1825  sub gradeBox { Line 1837  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>'.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';      '<td>'.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';
     $result.=&Apache::loncommon::end_data_table_row();      $result.=&Apache::loncommon::end_data_table_row();
Line 1912  sub show_problem { Line 1923  sub show_problem {
  $companswer=~s|</form>||g;   $companswer=~s|</form>||g;
  $companswer=~s|name="submit"|name="would_have_been_submit"|g;   $companswer=~s|name="submit"|name="would_have_been_submit"|g;
     }      }
       my $renderheading = &mt('View of the problem');
       my $answerheading = &mt('Correct answer');
       if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
           my $stu_fullname = $env{'form.fullname'};
           if ($stu_fullname eq '') {
               $stu_fullname = &Apache::loncommon::plainname($uname,$udom,'lastname');
           }
           my $forwhom = &nameUserString(undef,$stu_fullname,$uname,$udom);
           if ($forwhom ne '') {
               $renderheading = &mt('View of the problem for[_1]',$forwhom);
               $answerheading = &mt('Correct answer for[_1]',$forwhom);
           }
       }
     $rendered=      $rendered=
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('View of the problem').'</h3>'         .'<h3 class="LC_hcell">'.$renderheading.'</h3>'
        .$rendered         .$rendered
        .'</div>';         .'</div>';
     $companswer=      $companswer=
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('Correct answer').'</h3>'         .'<h3 class="LC_hcell">'.$answerheading.'</h3>'
        .$companswer         .$companswer
        .'</div>';         .'</div>';
     my $result;      my $result;
Line 1990  sub submission { Line 2014  sub submission {
     $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';      $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
     my $symb = &get_symb($request);       my ($symb) = &get_symb($request); 
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
Line 2009  sub submission { Line 2033  sub submission {
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
   
     my %old_essays;  
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
  &sub_page_js($request);   &sub_page_js($request);
Line 2106  sub submission { Line 2129  sub submission {
                           keyw => 'Keyword Options',                            keyw => 'Keyword Options',
                           list => 'List',                            list => 'List',
                           past => 'Paste Selection to List',                            past => 'Paste Selection to List',
                           high => 'Hightlight Attribute',                            high => 'Highlight Attribute',
                      );                       );
 #  #
 # Print out the keyword options line  # Print out the keyword options line
Line 2125  KEYWORDS Line 2148  KEYWORDS
     my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);      my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
     $apath=&escape($apath);      $apath=&escape($apath);
     $apath=~s/\W/\_/gs;      $apath=~s/\W/\_/gs;
     %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);              &init_old_essays($symb,$apath,$adom,$aname);
         }          }
     }      }
   
Line 2262  KEYWORDS Line 2285  KEYWORDS
                     }                      }
     if($env{'form.checkPlag'}){      if($env{'form.checkPlag'}){
  my ($oname,$odom,$ocrsid,$oessay,$osim)=   my ($oname,$odom,$ocrsid,$oessay,$osim)=
     &most_similar($uname,$udom,$subval,\%old_essays);      &most_similar($uname,$udom,$symb,$subval);
  if ($osim) {   if ($osim) {
     $osim=int($osim*100.0);      $osim=int($osim*100.0);
     my %old_course_desc =       my %old_course_desc = 
Line 2468  sub check_collaborators { Line 2491  sub check_collaborators {
     (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 2481  sub check_collaborators { Line 2504  sub check_collaborators {
     }      }
  }   }
  if (scalar(@good_collaborators) != 0) {   if (scalar(@good_collaborators) != 0) {
     $result.='<br />'.&mt('Collaborators: ').'<ol>';      $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);
Line 2594  sub keywords_highlight { Line 2617  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("Unable to handle ambiguous references.");
   
           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">Unable to view previous version for requested student.('.
                           $uname.':'.$udom.' in section '.$usec.' in course id '.
                           $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) = shift;
     my $symb   = &get_symb($request);      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 3002  sub handback_files { Line 3202  sub handback_files {
         &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,
                                                    $domain,$stuname,$getpropath);
       my $version = &get_next_version($answer_name,$answer_ext,$dir_list);
                     # fix file name                      # fix file name
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);                      my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                     my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,                      my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
Line 3163  sub version_portfiles { Line 3365  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 3184  sub version_portfiles { Line 3388  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 3658  sub viewstudentgrade { Line 3865  sub viewstudentgrade {
 sub editgrades {  sub editgrades {
     my ($request) = @_;      my ($request) = @_;
   
     my $symb=&get_symb($request);      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>'.&mt('<b>Current Resource: </b>[_1]',$env{'form.probTitle'}).'</h4>'."\n";
Line 4686  sub displaySubByDates { Line 4893  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 4706  sub displaySubByDates { Line 4914  sub displaySubByDates {
  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 4714  sub displaySubByDates { Line 4924  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}) {
Line 4733  sub displaySubByDates { Line 4946  sub displaySubByDates {
                                           
     my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)      my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));                 : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
                     $displaySub[0].='<span class="LC_nobreak"';                      $displaySub[0].='<span class="LC_nobreak">';
                     $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'                      $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
                                    .' <span class="LC_internal_info">'                                     .' <span class="LC_internal_info">'
                                    .'('.&mt('Response ID: [_1]',$responseId).')'                                     .'('.&mt('Response ID: [_1]',$responseId).')'
Line 4993  sub updateGradeByPage { Line 5206  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.
   
Line 5014  username:domain. Line 5227  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 5157  sub scantron_filenames { Line 5370  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 5318  sub scantron_CODEunique { Line 5533  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 5562  sub scantron_selectphase { Line 5777  sub scantron_selectphase {
     
       LastName    - column that the last name starts in        LastName    - column that the last name starts in
       LastNameLength - number of columns that the last name spans        LastNameLength - number of columns that the last name spans
         BubblesPerRow - number of bubbles available in each row used to
                         bubble an answer. (If not specified, 10 assumed).
   
 =cut  =cut
   
Line 5592  sub get_scantron_config { Line 5809  sub get_scantron_config {
  $config{'FirstNamelength'}=$config[14];   $config{'FirstNamelength'}=$config[14];
  $config{'LastName'}=$config[15];   $config{'LastName'}=$config[15];
  $config{'LastNamelength'}=$config[16];   $config{'LastNamelength'}=$config[16];
           $config{'BubblesPerRow'}=$config[17];
  last;   last;
     }      }
     return %config;      return %config;
Line 6341  sub remember_current_skipped { Line 6559  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 6376  sub scantron_warning_screen { Line 6594  sub scantron_warning_screen {
     '<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">
Line 6384  sub scantron_warning_screen { Line 6608  sub scantron_warning_screen {
 <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 />  <br />
 <p> '.&mt('If this information is correct, please click on \'[_1]\'.',&mt($button_text)).'</p>  <p> '.&mt('If this information is correct, please click on \'[_1]\'.',&mt($button_text)).'</p>
Line 6424  sub scantron_do_warning { Line 6648  sub scantron_do_warning {
  }    } 
     } else {      } else {
  my $warning=&scantron_warning_screen('Grading: Validate Records');   my $warning=&scantron_warning_screen('Grading: Validate Records');
           my $bubbledbyhand=&hand_bubble_option();
  $r->print('   $r->print('
 '.$warning.'  '.$warning.$bubbledbyhand.'
 <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />  <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />
 <input type="hidden" name="command" value="scantron_validate" />  <input type="hidden" name="command" value="scantron_validate" />
 ');  ');
Line 6477  SCANTRONFORM Line 6702  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 6515  sub scantron_validate_file { Line 6740  sub scantron_validate_file {
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $nav_error;      my $nav_error;
     my $max_bubble=&scantron_get_maxbubble(\$nav_error);      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
     }      }
     my $result=&scantron_form_start($max_bubble).$default_form_data;      my $result=&scantron_form_start($max_bubble).$default_form_data;
       if ($env{'form.scantron_lastbubblepoints'} ne '') {
           $result .= '<input type="hidden" name="scantron_lastbubblepoints" value="'.$env{'form.scantron_lastbubblepoints'}.'" />';
       }
     $r->print($result);      $r->print($result);
           
     my @validate_phases=( 'sequence',      my @validate_phases=( 'sequence',
Line 6588  sub scantron_validate_file { Line 6817  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 6613  sub scantron_remove_file { Line 6842  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 6650  sub scantron_remove_scan_data { Line 6879  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 6750  sub lonnet_putfile { Line 6979  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 6844  sub get_todo_count { Line 7073  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 6946  sub scantron_validate_sequence { Line 7175  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 6968  sub scantron_validate_ID { Line 7202  sub scantron_validate_ID {
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # parse needs the bubble_lines.. array.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble_lines.. array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
Line 7033  sub scantron_get_correction { Line 7267  sub scantron_get_correction {
 #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 7055  sub scantron_get_correction { Line 7298  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 7074  sub scantron_get_correction { Line 7317  sub scantron_get_correction {
  $r->print('</li>');   $r->print('</li>');
     } elsif ($error =~ /CODE$/) {      } elsif ($error =~ /CODE$/) {
  if ($error eq 'incorrectCODE') {   if ($error eq 'incorrectCODE') {
     $r->print("<p>".&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 7108  sub scantron_get_correction { Line 7352  sub scantron_get_correction {
     $r->print("      $r->print("
     <label>      <label>
         <input type='radio' name='scantron_CODE_resolution' value='use_unfound'$checked />          <input type='radio' name='scantron_CODE_resolution' value='use_unfound'$checked />
        ".&mt("Use the CODE [_1] that is was on the paper, ignoring the error.",         ".&mt("Use the CODE [_1] that was on the paper, ignoring the error.",
      "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."       "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."
     </label>");      </label>");
     $r->print("\n<br />");      $r->print("\n<br />");
Line 7148  ENDSCRIPT Line 7392  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:
Line 7166  ENDSCRIPT Line 7410  ENDSCRIPT
  }   }
         $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 7371  sub scantron_bubble_selector { Line 7615  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 7526  sub scantron_validate_CODE { Line 7782  sub scantron_validate_CODE {
     my %allcodes=&get_codes();      my %allcodes=&get_codes();
   
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # parse needs the lines per response array.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the lines per response array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
Line 7585  sub scantron_validate_doublebubble { Line 7841  sub scantron_validate_doublebubble {
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # parse needs the bubble line array.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # parse needs the bubble line array.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return(1,$currentphase);          return(1,$currentphase);
Line 7607  sub scantron_validate_doublebubble { Line 7863  sub scantron_validate_doublebubble {
   
   
 sub scantron_get_maxbubble {  sub scantron_get_maxbubble {
     my ($nav_error) = @_;      my ($nav_error,$scantron_config) = @_;
     if (defined($env{'form.scantron_maxbubble'}) &&      if (defined($env{'form.scantron_maxbubble'}) &&
  $env{'form.scantron_maxbubble'}) {   $env{'form.scantron_maxbubble'}) {
  &restore_bubble_lines();   &restore_bubble_lines();
Line 7626  sub scantron_get_maxbubble { Line 7882  sub scantron_get_maxbubble {
     }      }
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my $bubbles_per_row = &bubblesheet_bubbles_per_row($scantron_config);
   
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
   
Line 7641  sub scantron_get_maxbubble { Line 7898  sub scantron_get_maxbubble {
     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 ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,
                                                             $udom,$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 7670  sub scantron_get_maxbubble { Line 7928  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 7683  sub scantron_get_maxbubble { Line 7942  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;
Line 7704  sub scantron_get_maxbubble { Line 7963  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
Line 7714  sub scantron_validate_missingbubbles { Line 7985  sub scantron_validate_missingbubbles {
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $nav_error;      my $nav_error;
     my $max_bubble=&scantron_get_maxbubble(\$nav_error);      my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
         return(1,$currentphase);          return(1,$currentphase);
     }      }
Line 7761  sub scantron_validate_missingbubbles { Line 8032  sub scantron_validate_missingbubbles {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   sub hand_bubble_option {
       my (undef, undef, $sequence) =
           &Apache::lonnet::decode_symb($env{'form.selectpage'});
       return if ($sequence eq '');
       my $navmap = Apache::lonnavmaps::navmap->new();
       unless (ref($navmap)) {
           return;
       }
       my $needs_hand_bubbles;
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       foreach my $res (@resources) {
           if (ref($res)) {
               if ($res->is_problem()) {
                   my $partlist = $res->parts();
                   foreach my $part (@{ $partlist }) {
                       my @types = $res->responseType($part);
                       if (grep(/^(chem|essay|image|formula|math|string|functionplot)$/,@types)) {
                           $needs_hand_bubbles = 1;
                           last;
                       }
                   }
               }
           }
       }
       if ($needs_hand_bubbles) {
           my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
           my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
           return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.
                  &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').
                  '<label><input type="radio" name="scantron_lastbubblepoints" value="'.$bubbles_per_row.'" checked="checked" />'.&mt('[quant,_1,point]',$bubbles_per_row).'</label>&nbsp;'.&mt('or').'&nbsp;'.
                  '<label><input type="radio" name="scantron_lastbubblepoints" value="0"/>0 points</label></p>';
       }
       return;
   }
   
 sub scantron_process_students {  sub scantron_process_students {
     my ($r) = @_;      my ($r) = @_;
Line 7773  sub scantron_process_students { Line 8079  sub scantron_process_students {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my $bubbles_per_row =
           &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
Line 7785  sub scantron_process_students { Line 8093  sub scantron_process_students {
     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 (%grader_partids_by_symb,%grader_randomlists_by_symb);
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,      &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                             \%grader_randomlists_by_symb);                              \%grader_randomlists_by_symb,$bubbles_per_row);
     my $resource_error;      my $resource_error;
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
         my $ressymb;          my $ressymb;
Line 7797  sub scantron_process_students { Line 8105  sub scantron_process_students {
         }          }
         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 7824  SCANTRONFORM Line 8133  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,  
     'inline',undef,'scantronupload');  
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
   'Processing first student');    'Processing first student');
     $r->print('<br />');      $r->print('<br />');
Line 7835  SCANTRONFORM Line 8142  SCANTRONFORM
     my $started;      my $started;
   
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
Line 7892  SCANTRONFORM Line 8199  SCANTRONFORM
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                 my ($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);
                 $partids_by_symb{$ressymb} = $parts;                  $partids_by_symb{$ressymb} = $parts;
             } else {              } else {
                 $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};                  $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
Line 7921  SCANTRONFORM Line 8229  SCANTRONFORM
         }          }
   
         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') {                                     \@resources,\%partids_by_symb,
                                      $bubbles_per_row) eq 'ssi_error') {
             $ssi_error = 0; # So end of handler error message does not trigger.              $ssi_error = 0; # So end of handler error message does not trigger.
             $r->print("</form>");              $r->print("</form>");
             &ssi_print_error($r);              &ssi_print_error($r);
Line 7949  SCANTRONFORM Line 8258  SCANTRONFORM
             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') {                                             \@resources,\%partids_by_symb,
                                              $bubbles_per_row) eq 'ssi_error') {
                     $ssi_error = 0; # So end of handler error message does not trigger.                      $ssi_error = 0; # So end of handler error message does not trigger.
                     $r->print("</form>");                      $r->print("</form>");
                     &ssi_print_error($r);                      &ssi_print_error($r);
Line 7969  SCANTRONFORM Line 8279  SCANTRONFORM
                     $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 7982  SCANTRONFORM Line 8292  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">'.$studentdata.'</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">'.$studentrecord.'</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>');
Line 8014  SCANTRONFORM Line 8324  SCANTRONFORM
 }  }
   
 sub graders_resources_pass {  sub graders_resources_pass {
     my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb) = @_;      my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb,
           $bubbles_per_row) = @_;
     if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) &&       if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) && 
         (ref($grader_randomlists_by_symb) eq 'HASH')) {          (ref($grader_randomlists_by_symb) eq 'HASH')) {
         foreach my $resource (@{$resources}) {          foreach my $resource (@{$resources}) {
             my $ressymb = $resource->symb();              my $ressymb = $resource->symb();
             my ($analysis,$parts) =              my ($analysis,$parts) =
                 &scantron_partids_tograde($resource,$env{'request.course.id'},                  &scantron_partids_tograde($resource,$env{'request.course.id'},
                                           $env{'user.name'},$env{'user.domain'},1);                                            $env{'user.name'},$env{'user.domain'},
                                             1,$bubbles_per_row);
             $grader_partids_by_symb->{$ressymb} = $parts;              $grader_partids_by_symb->{$ressymb} = $parts;
             if (ref($analysis) eq 'HASH') {              if (ref($analysis) eq 'HASH') {
                 if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {                  if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
Line 8035  sub graders_resources_pass { Line 8347  sub graders_resources_pass {
 }  }
   
 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) = @_;
     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 8048  sub grade_student_bubbles { Line 8360  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}}) {
Line 8074  sub scantron_upload_scantron_data { Line 8392  sub scantron_upload_scantron_data {
   '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 ($symb) = &get_symb($r,1);
       my $default_form_data=&defaultFormData($symb);
     my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');      my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
     my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded.");      my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded.");
     $r->print('      $r->print('
Line 8280  sub valid_file { Line 8599  sub valid_file {
   
 sub scantron_download_scantron_data {  sub scantron_download_scantron_data {
     my ($r)=@_;      my ($r)=@_;
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my ($symb) = &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'};
Line 8290  sub scantron_download_scantron_data { Line 8610  sub scantron_download_scantron_data {
     '.&mt('The requested file name was invalid.').'      '.&mt('The requested file name was invalid.').'
         </p>          </p>
 ');  ');
  $r->print(&show_grading_menu_form(&get_symb($r,1)));   $r->print(&show_grading_menu_form($symb));
  return;   return;
     }      }
     my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;      my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
Line 8313  sub scantron_download_scantron_data { Line 8633  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)));      $r->print(&show_grading_menu_form($symb));
     return '';      return '';
 }  }
   
Line 8331  sub checkscantron_results { Line 8651  sub checkscantron_results {
     my %record;      my %record;
     my %scantron_config =      my %scantron_config =
         &Apache::grades::get_scantron_config($env{'form.scantron_format'});          &Apache::grades::get_scantron_config($env{'form.scantron_format'});
       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();      my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);      my %idmap=&Apache::grades::username_to_idmap($classlist);
Line 8342  sub checkscantron_results { Line 8663  sub checkscantron_results {
     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 (%grader_partids_by_symb,%grader_randomlists_by_symb);      my (%grader_partids_by_symb,%grader_randomlists_by_symb);
     &graders_resources_pass(\@resources,\%grader_partids_by_symb,                             \%grader_randomlists_by_symb);      &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);
Line 8353  sub checkscantron_results { Line 8675  sub checkscantron_results {
     my %completedstudents;      my %completedstudents;
   
     my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);      my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet/Submissions Comparison Status',      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
                                     'Progress of Bubblesheet Data/Submission Records Comparison',$count,  
                                     'inline',undef,'checkscantron');  
     my ($username,$domain,$started);      my ($username,$domain,$started);
     my $nav_error;      my $nav_error;
     &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.      &scantron_get_maxbubble(\$nav_error,\%scantron_config); # Need the bubble lines array to parse.
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
Line 8408  sub checkscantron_results { Line 8728  sub checkscantron_results {
             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 $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);
             } else {              } else {
                 $parts = $grader_partids_by_symb{$ressymb};                  $parts = $grader_partids_by_symb{$ressymb};
             }              }
Line 8454  sub checkscantron_results { Line 8776  sub checkscantron_results {
         }          }
     }      }
     $r->print('<p>'.      $r->print('<p>'.
               &mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3]  ([_4] bubblesheet lines/student).',                &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>',                    '<b>',
                   $numstudents,                    $numstudents,
                   '</b>',                    '</b>',
Line 8647  sub savedState { Line 8969  sub savedState {
     return \%savedState;      return \%savedState;
 }  }
   
   #--- Href with symb and command ---
   
   sub href_symb_cmd {
       my ($symb,$cmd)=@_;
       return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.$cmd;
   }
   
 sub grading_menu {  sub grading_menu {
     my ($request) = @_;      my ($request) = @_;
     my ($symb)=&get_symb($request);      my ($symb)=&get_symb($request);
Line 8954  sub init_perm { Line 9283  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 9055  sub process_clicker { Line 9399  sub process_clicker {
     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.=<<ENDUPFORM;
 <script type="text/javascript">  <script type="text/javascript">
Line 9228  ENDHEADER Line 9573  ENDHEADER
     if ($env{'form.upfiletype'} eq 'interwrite') {      if ($env{'form.upfiletype'} eq 'interwrite') {
         ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
     }      }
       if ($env{'form.upfiletype'} eq 'turning') {
           ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);
       }
     $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.      $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.
              '<input type="hidden" name="number" value="'.$number.'" />'.               '<input type="hidden" name="number" value="'.$number.'" />'.
              &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',               &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',
Line 9359  sub interwrite_eval { Line 9707  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)=@_;
     my ($symb)=&get_symb($r);      my ($symb)=&get_symb($r);
Line 9372  sub assign_clicker_grades { Line 9745  sub assign_clicker_grades {
 # FIXME: This should probably look for the first handgradeable part  # FIXME: This should probably look for the first handgradeable part
     my $part=$$partlist[0];      my $part=$$partlist[0];
 # Start screen output  # Start screen output
     my ($result) = &showResourceInfo($symb,$env{'form.probTitle'}).'<br />';      my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
   
     $result .= &Apache::loncommon::start_data_table().      $result .= '<br />'.
                  &Apache::loncommon::start_data_table().
                &Apache::loncommon::start_data_table_header_row().                 &Apache::loncommon::start_data_table_header_row().
                '<th>'.&mt('Assigning grades based on clicker file').'</th>'.                 '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
                &Apache::loncommon::end_data_table_header_row().                 &Apache::loncommon::end_data_table_header_row().
Line 9499  sub navmap_errormsg { Line 9873  sub navmap_errormsg {
            '</div>';             '</div>';
 }  }
   
   sub startpage {
       my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js) = @_;
       if ($nomenu) {
           $r->print(&Apache::loncommon::start_page("Student's Version",$js,{'only_body' => '1'}));
       } else {
           $r->print(&Apache::loncommon::start_page('Grading',$js,
                                                    {'bread_crumbs' => $crumbs}));
       }
       unless ($nodisplayflag) {
          $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp));
       }
   }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
     &reset_caches();      &reset_caches();
Line 9520  sub handler { Line 9907  sub handler {
     $ssi_error = 0;      $ssi_error = 0;
     my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];      my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];
     my $start_page = &Apache::loncommon::start_page('Grading',undef,      my $start_page = &Apache::loncommon::start_page('Grading',undef,
                                           {'bread_crumbs' => $brcrum});                                                      {'bread_crumbs' => $brcrum});
     if ($symb eq '' && $command eq '') {      if ($symb eq '' && $command eq '') {
  if ($env{'user.adv'}) {   if ($env{'user.adv'}) {
             &Apache::loncommon::content_type($request,'text/html');              &Apache::loncommon::content_type($request,'text/html');
Line 9562  sub handler { Line 9949  sub handler {
     } else {      } else {
         &init_perm();          &init_perm();
         if (!$env{'request.course.id'}) {          if (!$env{'request.course.id'}) {
             # Not in a course.              unless ((&Apache::lonnet::allowed('usc',$env{'request.role.domain'})) &&
             $env{'user.error.msg'}="/adm/grades::vgr:0:0:Cannot display grades page outside course context";                      ($command =~ /^scantronupload/)) {
             return HTTP_NOT_ACCEPTABLE;                  # 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) {          } elsif (!%perm) {
             $request->internal_redirect('/adm/quickgrades');              $request->internal_redirect('/adm/quickgrades');
         }          }
         &Apache::loncommon::content_type($request,'text/html');          &Apache::loncommon::content_type($request,'text/html');
         $request->send_http_header;          $request->send_http_header;
         $request->print($start_page);          unless ((($command eq 'submission' || $command eq 'versionsub')) && ($perm{'vgr'})) {
               $request->print($start_page); 
           }
  if ($command eq 'submission' && $perm{'vgr'}) {   if ($command eq 'submission' && $perm{'vgr'}) {
               my ($stuvcurrent,$stuvdisp,$versionform,$js);
               if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
                   ($stuvcurrent,$stuvdisp,$versionform,$js) =
                       &choose_task_version_form($symb,$env{'form.student'},
                                                 $env{'form.userdom'});
               }
               &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js);
               if ($versionform) {
                   $request->print($versionform);
               }
               $request->print('<br clear="all" />');
     ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));      ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
           } elsif ($command eq 'versionsub' && $perm{'vgr'}) {
               my ($stuvcurrent,$stuvdisp,$versionform,$js) =
                   &choose_task_version_form($symb,$env{'form.student'},
                                             $env{'form.userdom'},
                                             $env{'form.inhibitmenu'});
               &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,$stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$js);
               if ($versionform) {
                   $request->print($versionform);
               }
               $request->print('<br clear="all" />');
               $request->print(&show_previous_task_version($request,$symb));
  } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {   } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
     &pickStudentPage($request);      &pickStudentPage($request);
  } elsif ($command eq 'displayPage' && $perm{'vgr'}) {   } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
Line 9752  ssi_with_retries() Line 10166  ssi_with_retries()
        calling routine should trap the error condition and display the warning         calling routine should trap the error condition and display the warning
        found in &navmap_errormsg().         found in &navmap_errormsg().
   
          $scantron_config - Reference to bubblesheet format configuration hash.
   
    Returns the maximum number of bubble lines that are expected to     Returns the maximum number of bubble lines that are expected to
    occur. Does this by walking the selected sequence rendering the     occur. Does this by walking the selected sequence rendering the
    resource and then checking &Apache::lonxml::get_problem_counter()     resource and then checking &Apache::lonxml::get_problem_counter()
Line 9775  ssi_with_retries() Line 10191  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 9795  ssi_with_retries() Line 10211  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 9809  ssi_with_retries() Line 10225  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() : 

Removed from v.1.596.2.5  
changed lines
  Added in v.1.596.2.12.2.10


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