Diff for /loncom/homework/grades.pm between versions 1.555 and 1.572

version 1.555, 2009/03/06 21:14:50 version 1.572, 2009/05/07 19:36:45
Line 234  sub reset_caches { Line 234  sub reset_caches {
   
 {  {
     my %analyze_cache;      my %analyze_cache;
       my %analyze_cache_formkeys;
   
     sub reset_analyze_cache {      sub reset_analyze_cache {
  undef(%analyze_cache);   undef(%analyze_cache);
           undef(%analyze_cache_formkeys);
     }      }
   
     sub get_analyze {      sub get_analyze {
  my ($symb,$uname,$udom,$no_increment)=@_;   my ($symb,$uname,$udom,$no_increment,$add_to_hash)=@_;
  my $key = "$symb\0$uname\0$udom";   my $key = "$symb\0$uname\0$udom";
  return $analyze_cache{$key} if (exists($analyze_cache{$key}));   if (exists($analyze_cache{$key})) {
               my $getupdate = 0;
               if (ref($add_to_hash) eq 'HASH') {
                   foreach my $item (keys(%{$add_to_hash})) {
                       if (ref($analyze_cache_formkeys{$key}) eq 'HASH') {
                           if (!exists($analyze_cache_formkeys{$key}{$item})) {
                               $getupdate = 1;
                               last;
                           }
                       } else {
                           $getupdate = 1;
                       }
                   }
               }
               if (!$getupdate) {
                   return $analyze_cache{$key};
               }
           }
   
  my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);   my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
  $url=&Apache::lonnet::clutter($url);   $url=&Apache::lonnet::clutter($url);
  my $subresult=&ssi_with_retries($url, $ssi_retries,          my %form = ('grade_target'      => 'analyze',
    ('grade_target' => 'analyze',                      'grade_domain'      => $udom,
     'grade_domain' => $udom,                      'grade_symb'        => $symb,
     'grade_symb' => $symb,                      'grade_courseid'    =>  $env{'request.course.id'},
     'grade_courseid' =>                       'grade_username'    => $uname,
     $env{'request.course.id'},                      'grade_noincrement' => $no_increment);
     'grade_username' => $uname,          if (ref($add_to_hash)) {
                                             'grade_noincrement' => $no_increment));              %form = (%form,%{$add_to_hash});
           } 
    my $subresult=&ssi_with_retries($url, $ssi_retries,%form);
  (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);   (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
  my %analyze=&Apache::lonnet::str2hash($subresult);   my %analyze=&Apache::lonnet::str2hash($subresult);
           if (ref($add_to_hash) eq 'HASH') {
               $analyze_cache_formkeys{$key} = $add_to_hash;
           } else {
               $analyze_cache_formkeys{$key} = {};
           }
  return $analyze_cache{$key} = \%analyze;   return $analyze_cache{$key} = \%analyze;
     }      }
   
Line 279  sub reset_caches { Line 305  sub reset_caches {
     }      }
   
     sub scantron_partids_tograde {      sub scantron_partids_tograde {
         my ($resource,$cid,$uname,$udom) = @_;          my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_;
         my (%analysis,@parts);          my (%analysis,@parts);
         if (ref($resource)) {          if (ref($resource)) {
             my $symb = $resource->symb();              my $symb = $resource->symb();
             my $analyze = &get_analyze($symb,$uname,$udom);              my $add_to_form;
               if ($check_for_randomlist) {
                   $add_to_form = { 'check_parts_withrandomlist' => 1,};
               }
               my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form);
             if (ref($analyze) eq 'HASH') {              if (ref($analyze) eq 'HASH') {
                 %analysis = %{$analyze};                  %analysis = %{$analyze};
             }              }
Line 814  sub listStudents { Line 844  sub listStudents {
   
     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));      my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
   
     my %lt = ( 'multiple' =>      my %lt = &Apache::lonlocal::texthash (
        &mt("Please select a student or group of students before clicking on the Next button."),   'multiple' => 'Please select a student or group of students before clicking on the Next button.',
        'single'   =>   'single'   => 'Please select the student before clicking on the Next button.',
        &mt("Please select the student before clicking on the Next button."),       );
        );  
     %lt = &Apache::lonlocal::texthash(%lt);  
     $request->print(<<LISTJAVASCRIPT);      $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkSelect(checkBox) {      function checkSelect(checkBox) {
Line 861  LISTJAVASCRIPT Line 889  LISTJAVASCRIPT
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.      my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
  "\n".$table;   "\n".$table;
   
     $gradeTable .=       $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
  '&nbsp;<b>'.&mt('View Problem Text').': </b>'.      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
     '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".                    .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
     '<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n".                    .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
     '<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n";                    .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
     $gradeTable .=                     .&Apache::lonhtmlcommon::row_closure();
  '&nbsp;<b>'.&mt('View Answer').': </b>'.      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
     '<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n".                    .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
     '<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n".                    .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
     '<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n";                    .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
                     .&Apache::lonhtmlcommon::row_closure();
   
     my $submission_options;      my $submission_options;
     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {      if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
Line 885  LISTJAVASCRIPT Line 914  LISTJAVASCRIPT
  '<label><input type="radio" name="lastSub" value="last" /> '.&mt('last submission &amp; parts info').' </label>'."\n".   '<label><input type="radio" name="lastSub" value="last" /> '.&mt('last submission &amp; parts info').' </label>'."\n".
  '<label><input type="radio" name="lastSub" value="datesub" /> '.&mt('by dates and submissions').' </label>'."\n".   '<label><input type="radio" name="lastSub" value="datesub" /> '.&mt('by dates and submissions').' </label>'."\n".
  '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').'</label>';   '<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').'</label>';
     $gradeTable .=       $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))
  '&nbsp;<b>'.&mt('Submissions').': </b>'.$submission_options.'<br />'."\n";                    .$submission_options
                     .&Apache::lonhtmlcommon::row_closure();
   
       $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
                     .'<select name="increment">'
                     .'<option value="1">'.&mt('Whole Points').'</option>'
                     .'<option value=".5">'.&mt('Half Points').'</option>'
                     .'<option value=".25">'.&mt('Quarter Points').'</option>'
                     .'<option value=".1">'.&mt('Tenths of a Point').'</option>'
                     .'</select>'
                     .&Apache::lonhtmlcommon::row_closure();
   
     $gradeTable .=       $gradeTable .= 
         '&nbsp;<b>'.&mt('Grading Increments').': </b>'.  
     '<select name="increment">'.  
     '<option value="1">'.&mt('Whole Points').'</option>'.  
     '<option value=".5">'.&mt('Half Points').'</option>'.  
     '<option value=".25">'.&mt('Quarter Points').'</option>'.  
     '<option value=".1">'.&mt('Tenths of a Point').'</option>'.  
     '</select>';  
       
     $gradeTable .=   
         &build_section_inputs().          &build_section_inputs().
  '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".   '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
  '<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".   '<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".
Line 908  LISTJAVASCRIPT Line 938  LISTJAVASCRIPT
  '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
   
     if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {      if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
  $gradeTable.='<input type="hidden" name="Status"   value="'.$stu_status.'" />'."\n";   $gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
     } else {      } else {
  $gradeTable.=&mt('<b>Student Status:</b> [_1]',          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))
  &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'<br />';                        .&Apache::lonhtmlcommon::StatusOptions(
                              $saveStatus,undef,1,'javascript:reLoadList(this.form);')
                         .&Apache::lonhtmlcommon::row_closure();
     }      }
   
     $gradeTable.=&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.").'<br />'."\n".      $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
  '<input type="hidden" name="command" value="processGroup" />'."\n";                    .'<input type="checkbox" name="checkPlag" checked="checked" />'
                     .&Apache::lonhtmlcommon::row_closure(1)
                     .&Apache::lonhtmlcommon::end_pick_box();
   
       $gradeTable .= '<p>'
                     .&mt('To '.lc($viewgrade)." a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n"
                     .'<input type="hidden" name="command" value="processGroup" />'
                     .'</p>';
   
 # checkall buttons  # checkall buttons
     $gradeTable.=&check_script('gradesub', 'stuinfo');      $gradeTable.=&check_script('gradesub', 'stuinfo');
Line 923  LISTJAVASCRIPT Line 962  LISTJAVASCRIPT
  'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".   'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
  'value="'.&mt('Next').' &rarr;" /> <br />'."\n";   'value="'.&mt('Next').' &rarr;" /> <br />'."\n";
     $gradeTable.=&check_buttons();      $gradeTable.=&check_buttons();
     $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="checked" />'.&mt('Check For Plagiarism').'</label>';  
     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup);      my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup);
     $gradeTable.= &Apache::loncommon::start_data_table().      $gradeTable.= &Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row();   &Apache::loncommon::start_data_table_header_row();
Line 1006  LISTJAVASCRIPT Line 1044  LISTJAVASCRIPT
  $gradeTable.= &Apache::loncommon::start_data_table_row();   $gradeTable.= &Apache::loncommon::start_data_table_row();
     }      }
     $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.      $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.
                '<td align="center"><label><input type=checkbox name="stuinfo" value="'.                 '<td align="center"><label><input type="checkbox" name="stuinfo" value="'.
                $student.':'.$$fullname{$student}.':::SECTION'.$section.                 $student.':'.$$fullname{$student}.':::SECTION'.$section.
        ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.         ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
        &nameUserString(undef,$$fullname{$student},$uname,$udom).         &nameUserString(undef,$$fullname{$student},$uname,$udom).
Line 1513  INNERJS Line 1551  INNERJS
     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");      pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
     pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Compose Message for \"+fullname+\"<\\/span><\\/h3><br /><br />");      pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Compose Message for \"+fullname+\"<\\/span><\\/h3><br /><br />");
   
     pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");      pDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');
     pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");      pDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');
     pDoc.write("<td><b>Type<\\/b><\\/td><td><b>Include<\\/b><\\/td><td><b>Message<\\/td><\\/tr>");      pDoc.write("<td><b>Type<\\/b><\\/td><td><b>Include<\\/b><\\/td><td><b>Message<\\/td><\\/tr>");
 }  }
     function displaySubject(msg,shwsel) {      function displaySubject(msg,shwsel) {
Line 1598  INNERJS Line 1636  INNERJS
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");      hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
     hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Keyword Highlight Options<\\/span><\\/h3><br /><br />");      hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Keyword Highlight Options<\\/span><\\/h3><br /><br />");
   
     hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");      hDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');
     hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");      hDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');
     hDoc.write("<td><b>Text Color<\\/b><\\/td><td><b>Font Size<\\/b><\\/td><td><b>Font Style<\\/td><\\/tr>");      hDoc.write("<td><b>Text Color<\\/b><\\/td><td><b>Font Size<\\/b><\\/td><td><b>Font Style<\\/td><\\/tr>");
   }    }
   
Line 2124  KEYWORDS Line 2162  KEYWORDS
     foreach my $file (@$files) {      foreach my $file (@$files) {
         $file_counter++;          $file_counter++;
  &Apache::lonnet::allowuploaded('/adm/grades',$file);   &Apache::lonnet::allowuploaded('/adm/grades',$file);
  $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>';   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" /> '.$file.'</a>';
     }      }
     $lastsubonly.='<br />';      $lastsubonly.='<br />';
  }   }
Line 3198  sub viewgrades { Line 3236  sub viewgrades {
  '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".   '<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".
  '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";   '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
   
     my $sectionClass;      my ($common_header,$specific_header);
     my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));  
     if ($env{'form.section'} eq 'all') {      if ($env{'form.section'} eq 'all') {
  $sectionClass=&mt('Class');   $common_header = &mt('Assign Common Grade to Class');
           $specific_header = &mt('Assign Grade to Specific Students in Class');
     } elsif ($env{'form.section'} eq 'none') {      } elsif ($env{'form.section'} eq 'none') {
  $sectionClass=&mt('Students in no Section');          $common_header = &mt('Assign Common Grade to Students in no Section');
    $specific_header = &mt('Assign Grade to Specific Students in no Section');
     } else {      } else {
  $sectionClass=&mt('Students in Section(s) [_1]');          my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
           $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);
    $specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);
     }      }
     $result.=      $result.= '<h3>'.$common_header.'</h3>'.&Apache::loncommon::start_data_table();
  '<h3>'.  
  &mt("Assign Common Grade to [_1]",$sectionClass,$section_display).'</h3>';  
     $result.= &Apache::loncommon::start_data_table();  
     #radio buttons/text box for assigning points for a section or class.      #radio buttons/text box for assigning points for a section or class.
     #handles different parts of a problem      #handles different parts of a problem
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my ($partlist,$handgrade,$responseType) = &response_type($symb);
Line 3268  sub viewgrades { Line 3306  sub viewgrades {
   
     #table listing all the students in a section/class      #table listing all the students in a section/class
     #header of table      #header of table
     $result.= '<h3>'.&mt('Assign Grade to Specific Students in ').$sectionClass,      $result.= '<h3>'.$specific_header.'</h3>'.
  $section_display.'</h3>';                &Apache::loncommon::start_data_table().
     $result.= &Apache::loncommon::start_data_table().        &Apache::loncommon::start_data_table_header_row().
  &Apache::loncommon::start_data_table_header_row().        '<th>'.&mt('No.').'</th>'.
  '<th>'.&mt('No.').'</th>'.        '<th>'.&nameUserString('header')."</th>\n";
  '<th>'.&nameUserString('header')."</th>\n";  
     my (@parts) = sort(&getpartlist($symb));      my (@parts) = sort(&getpartlist($symb));
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);      my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     my @partids = ();      my @partids = ();
Line 3767  ENDPICK Line 3804  ENDPICK
 sub csvupload_fields {  sub csvupload_fields {
     my ($symb) = @_;      my ($symb) = @_;
     my (@parts) = &getpartlist($symb);      my (@parts) = &getpartlist($symb);
     my @fields=(['ID','Student ID'],      my @fields=(['ID','Student/Employee ID'],
  ['username','Student Username'],   ['username','Student Username'],
  ['domain','Student Domain']);   ['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
Line 4053  sub csvuploadassign { Line 4090  sub csvuploadassign {
    $countdone++;     $countdone++;
         }          }
     }      }
     $request->print('<br /><span class="LC_info">'.&mt("Saved [_1] students",$countdone)."</span>\n");      $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
     if (@skipped) {      if (@skipped) {
  $request->print('<p><span class="LC_warning">'.&mt('Skipped Students').'</span></p>');   $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />');
  foreach my $student (@skipped) { $request->print("$student<br />\n"); }          $request->print(join(', ',@skipped));
     }      }
     if (@notallowed) {      if (@notallowed) {
  $request->print('<p><span class="LC_error">'.&mt('Students Not Allowed to Modify').'</span></p>');   $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Modification of scores not allowed for the following username(s):'),1).'<br />');
  foreach my $student (@notallowed) { $request->print("$student<br />\n"); }   $request->print(join(', ',@notallowed));
     }      }
     $request->print("<br />\n");      $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb));      $request->print(&show_grading_menu_form($symb));
Line 4680  Next each scanline is checked for any er Line 4717  Next each scanline is checked for any er
 bubbles' (it's an error because it may have been mis-scanned  bubbles' (it's an error because it may have been mis-scanned
 because too light bubbling), 'double bubble' (each bubble line should  because too light bubbling), 'double bubble' (each bubble line should
 have no more that one letter picked), invalid or duplicated CODE,  have no more that one letter picked), invalid or duplicated CODE,
 invalid student ID  invalid student/employee ID
   
 If the CODE option is used that determines the randomization of the  If the CODE option is used that determines the randomization of the
 homework problems, either way the student ID is looked up into a  homework problems, either way the student/employee ID is looked up into a
 username:domain.  username:domain.
   
 During the validation phase the instructor can choose to skip scanlines.   During the validation phase the instructor can choose to skip scanlines. 
Line 5032  sub scantron_selectphase { Line 5069  sub scantron_selectphase {
             <td> '.&mt('Sequence to grade:').' </td><td> '.$sequence_selector.' </td>              <td> '.&mt('Sequence to grade:').' </td><td> '.$sequence_selector.' </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> '.&mt('Filename of scoring office file:').' </td><td> '.$file_selector.' </td>              <td> '.&mt('Filename of bubblesheet data file:').' </td><td> '.$file_selector.' </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> '.&mt('Format of data file:').' </td><td> '.$format_selector.' </td>              <td> '.&mt('Format of bubblesheet data file:').' </td><td> '.$format_selector.' </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> '.&mt('Saved CODEs to validate against:').' </td><td> '.$CODE_selector.' </td>              <td> '.&mt('Saved CODEs to validate against:').' </td><td> '.$CODE_selector.' </td>
Line 5053  sub scantron_selectphase { Line 5090  sub scantron_selectphase {
        '.&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 colspan="2">              <td colspan="2">
               <input type="submit" value="'.&mt('Grading: Validate Scantron Records').'" />                <input type="submit" value="'.&mt('Grading: Validate Bubblesheet Records').'" />
             </td>              </td>
        '.&Apache::loncommon::end_data_table_row().'         '.&Apache::loncommon::end_data_table_row().'
     '.&Apache::loncommon::end_data_table().'      '.&Apache::loncommon::end_data_table().'
Line 5072  sub scantron_selectphase { Line 5109  sub scantron_selectphase {
     '.&Apache::loncommon::start_data_table('LC_scantron_action').'      '.&Apache::loncommon::start_data_table('LC_scantron_action').'
        '.&Apache::loncommon::start_data_table_header_row().'         '.&Apache::loncommon::start_data_table_header_row().'
             <th>              <th>
               &nbsp;'.&mt('Specify a Scantron data file to upload.').'                &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
             </th>              </th>
        '.&Apache::loncommon::end_data_table_header_row().'         '.&Apache::loncommon::end_data_table_header_row().'
        '.&Apache::loncommon::start_data_table_row().'         '.&Apache::loncommon::start_data_table_row().'
Line 5099  sub scantron_selectphase { Line 5136  sub scantron_selectphase {
                 <input name="command" value="scantronupload_save" type="hidden" />                  <input name="command" value="scantronupload_save" type="hidden" />
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'                  '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
                 <br />                  <br />
                 <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />                  <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
               </form>                </form>
 ');  ');
   
Line 5141  sub scantron_selectphase { Line 5178  sub scantron_selectphase {
              &Apache::loncommon::start_data_table('LC_scantron_action')."\n".               &Apache::loncommon::start_data_table('LC_scantron_action')."\n".
              &Apache::loncommon::start_data_table_header_row()."\n".               &Apache::loncommon::start_data_table_header_row()."\n".
              '<th colspan="2">               '<th colspan="2">
               &nbsp;'.&mt('Review scantron data and submissions for a previously graded folder/sequence')."\n".                &nbsp;'.&mt('Review bubblesheet data and submissions for a previously graded folder/sequence')."\n".
              '</th>'."\n".               '</th>'."\n".
               &Apache::loncommon::end_data_table_header_row()."\n".                &Apache::loncommon::end_data_table_header_row()."\n".
               &Apache::loncommon::start_data_table_row()."\n".                &Apache::loncommon::start_data_table_row()."\n".
Line 5157  sub scantron_selectphase { Line 5194  sub scantron_selectphase {
               '<td> '.$format_selector.' </td>'."\n".                '<td> '.$format_selector.' </td>'."\n".
               &Apache::loncommon::end_data_table_row()."\n".                &Apache::loncommon::end_data_table_row()."\n".
               &Apache::loncommon::start_data_table_row()."\n".                &Apache::loncommon::start_data_table_row()."\n".
                 '<td> '.&mt('Options').' </td>'."\n".
                 '<td> <label><input type="checkbox" name="scantron_options_hidden" value="ignore_hidden"/> '.&mt('Skip hidden resources').'</label></td>'.
                 &Apache::loncommon::end_data_table_row()."\n".
                 &Apache::loncommon::start_data_table_row()."\n".
               '<td colspan="2">'."\n".                '<td colspan="2">'."\n".
               '<input type="hidden" name="command" value="checksubmissions" />'."\n".                '<input type="hidden" name="command" value="checksubmissions" />'."\n".
               '<input type="submit" value="'.&mt('Review Scantron Data and Submission Records').'" />'."\n".                '<input type="submit" value="'.&mt('Review Scantron Data and Submission Records').'" />'."\n".
Line 5200  sub scantron_selectphase { Line 5241  sub scantron_selectphase {
       CODEstart   - (only matter if a CODE exists) column in the line where        CODEstart   - (only matter if a CODE exists) column in the line where
                      the CODE starts                       the CODE starts
       CODElength  - length of the CODE        CODElength  - length of the CODE
       IDstart     - column where the student ID number starts        IDstart     - column where the student/employee ID number starts
       IDlength    - length of the student ID info        IDlength    - length of the student/employee ID info
       Qstart      - column where the information from the bubbled        Qstart      - column where the information from the bubbled
                     'questions' start                      'questions' start
       Qlength     - number of columns comprising a single bubble line from        Qlength     - number of columns comprising a single bubble line from
Line 5261  sub get_scantron_config { Line 5302  sub get_scantron_config {
   
 =item username_to_idmap  =item username_to_idmap
   
     creates a hash keyed by student id with values of the corresponding      creates a hash keyed by student/employee ID with values of the corresponding
     student username:domain.      student username:domain.
   
   Arguments:    Arguments:
Line 5300  sub username_to_idmap { Line 5341  sub username_to_idmap {
     $whichline         - line number of the passed in scanline      $whichline         - line number of the passed in scanline
     $field             - type of change to process       $field             - type of change to process 
                          (either                            (either 
                           'ID'     -> correct the student ID number                            'ID'     -> correct the student/employee ID number
                           'CODE'   -> correct the CODE                            'CODE'   -> correct the CODE
                           'answer' -> fixup the submitted answers)                            'answer' -> fixup the submitted answers)
           
Line 5474  sub digits_to_letters { Line 5515  sub digits_to_letters {
        CODE_ignore_dup - 1 if the CODE is a duplicated use when unique         CODE_ignore_dup - 1 if the CODE is a duplicated use when unique
                             CODEs were selected, but the usage has been                              CODEs were selected, but the usage has been
                             forced by the operator                              forced by the operator
        ID  - student ID         ID  - student/employee ID
        PaperID - if used, the ID number printed on the sheet when the          PaperID - if used, the ID number printed on the sheet when the 
                  paper was scanned                   paper was scanned
        FirstName - first name from the sheet         FirstName - first name from the sheet
Line 6210  sub scantron_validate_file { Line 6251  sub scantron_validate_file {
                   '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').                    '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').
                   '</label></span><br />'.                    '</label></span><br />'.
                   &mt('Grading will take longer if you use verification.').'<br />'.                    &mt('Grading will take longer if you use verification.').'<br />'.
                   &mt("Alternatively, the 'Review scantron data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.                    &mt("Alternatively, the 'Review bubblesheet data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.
                   '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.                    '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.
                   '<input type="hidden" name="command" value="scantron_process" />'."\n");                    '<input type="hidden" name="command" value="scantron_process" />'."\n");
     } else {      } else {
Line 6736  sub scantron_get_correction { Line 6777  sub scantron_get_correction {
     if ($closest > 0) {      if ($closest > 0) {
  foreach my $testcode (@{$closest}) {   foreach my $testcode (@{$closest}) {
     my $checked='';      my $checked='';
     if (!$i) { $checked=' checked="checked" '; }      if (!$i) { $checked=' checked="checked"'; }
     $r->print("      $r->print("
    <label>     <label>
        <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked />         <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i'$checked />
        ".&mt("Use the similar CODE [_1] instead.",         ".&mt("Use the similar CODE [_1] instead.",
     "<b><tt>".$testcode."</tt></b>")."      "<b><tt>".$testcode."</tt></b>")."
     </label>      </label>
Line 6750  sub scantron_get_correction { Line 6791  sub scantron_get_correction {
     }      }
  }   }
  if ($$scan_record{'scantron.CODE'}=~/\S/ ) {   if ($$scan_record{'scantron.CODE'}=~/\S/ ) {
     my $checked; if (!$i) { $checked=' checked="checked" '; }      my $checked; if (!$i) { $checked=' checked="checked"'; }
     $r->print("      $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 is 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>");
Line 6784  ENDSCRIPT Line 6825  ENDSCRIPT
        ".&mt("[_1]Select[_2] a CODE from the list of all CODEs and use it.",         ".&mt("[_1]Select[_2] a CODE from the list of all CODEs and use it.",
      "<a target='_blank' href='$href'>","</a>")."       "<a target='_blank' href='$href'>","</a>")."
     </label>       </label> 
     ".&mt("Selected CODE is [_1]","<input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />"));      ".&mt("Selected CODE is [_1]",'<input readonly="readonly" type="text" size="8" name="scantron_CODE_selectedvalue" onfocus="javascript:change_radio(\'use_found\')" onchange="javascript:change_radio(\'use_found\')" />'));
     $r->print("\n<br />");      $r->print("\n<br />");
  }   }
  $r->print("   $r->print("
Line 6977  sub prompt_for_corrections { Line 7018  sub prompt_for_corrections {
             ($responsetype_per_response{$question-1} eq 'imageresponse') ||              ($responsetype_per_response{$question-1} eq 'imageresponse') ||
             ($responsetype_per_response{$question-1} eq 'reactionresponse') ||              ($responsetype_per_response{$question-1} eq 'reactionresponse') ||
             ($responsetype_per_response{$question-1} eq 'organicresponse')) {              ($responsetype_per_response{$question-1} eq 'organicresponse')) {
             $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their scantron sheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during scantron grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');              $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during bubblesheet grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');
         } else {          } else {
             $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");              $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");
         }          }
Line 7258  sub scantron_get_maxbubble { Line 7299  sub scantron_get_maxbubble {
   
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
   
     my $uname       = $env{'form.student'};      my $uname       = $env{'user.name'};
     my $udom        = $env{'form.userdom'};      my $udom        = $env{'user.domain'};
     my $cid         = $env{'request.course.id'};      my $cid         = $env{'request.course.id'};
     my $total_lines = 0;      my $total_lines = 0;
     %bubble_lines_per_response = ();      %bubble_lines_per_response = ();
Line 7404  sub scantron_process_students { Line 7445  sub scantron_process_students {
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
 #    $r->print("geto ".scalar(@resources)."<br />");      my (%grader_partids_by_symb,%grader_randomlists_by_symb);
       &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                               \%grader_randomlists_by_symb);
       foreach my $resource (@resources) {
           my $ressymb = $resource->symb();
           my ($analysis,$parts) =
               &scantron_partids_tograde($resource,$env{'request.course.id'},
                                         $env{'user.name'},$env{'user.domain'},1);
           $grader_partids_by_symb{$ressymb} = $parts;
           if (ref($analysis) eq 'HASH') {
               if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
                   $grader_randomlists_by_symb{$ressymb} = 
                       $analysis->{'parts_withrandomlist'};
               }
           }
       }
   
     my ($uname,$udom);      my ($uname,$udom);
     my $result= <<SCANTRONFORM;      my $result= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
Line 7472  SCANTRONFORM Line 7529  SCANTRONFORM
         my %partids_by_symb;          my %partids_by_symb;
         foreach my $resource (@resources) {          foreach my $resource (@resources) {
             my $ressymb = $resource->symb();              my $ressymb = $resource->symb();
             my ($analysis,$parts) =              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);            $partids_by_symb{$ressymb} = $parts;                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                   my ($analysis,$parts) =
                       &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
                   $partids_by_symb{$ressymb} = $parts;
               } else {
                   $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
               }
         }          }
   
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
Line 7584  SCANTRONFORM Line 7647  SCANTRONFORM
     return '';      return '';
 }  }
   
   sub graders_resources_pass {
       my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb) = @_;
       if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) && 
           (ref($grader_randomlists_by_symb) eq 'HASH')) {
           foreach my $resource (@{$resources}) {
               my $ressymb = $resource->symb();
               my ($analysis,$parts) =
                   &scantron_partids_tograde($resource,$env{'request.course.id'},
                                             $env{'user.name'},$env{'user.domain'},1);
               $grader_partids_by_symb->{$ressymb} = $parts;
               if (ref($analysis) eq 'HASH') {
                   if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
                       $grader_randomlists_by_symb->{$ressymb} =
                           $analysis->{'parts_withrandomlist'};
                   }
               }
           }
       }
       return;
   }
   
 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) = @_;
     if (ref($resources) eq 'ARRAY') {      if (ref($resources) eq 'ARRAY') {
Line 7617  sub grade_student_bubbles { Line 7701  sub grade_student_bubbles {
   
 sub scantron_upload_scantron_data {  sub scantron_upload_scantron_data {
     my ($r)=@_;      my ($r)=@_;
     $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));      my $dom = $env{'request.role.domain'};
       my $domdesc = &Apache::lonnet::domain($dom,'description');
       $r->print(&Apache::loncommon::coursebrowser_javascript($dom));
     my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',      my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
   'domainid',    'domainid',
   'coursename');    'coursename',$dom);
     my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'},      my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.
    'domainid');                         ('&nbsp'x2).&mt('(shows course personnel)'); 
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData(&get_symb($r,1));
     $r->print('      $r->print('
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
Line 7631  sub scantron_upload_scantron_data { Line 7717  sub scantron_upload_scantron_data {
     alert("'.&mt('Please use the browse button to select a file from your local directory.').'");      alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
     return false;      return false;
  }   }
           if (formname.courseid.value == "") {
               alert("'.&mt('Please use the \"Select Course\" link to open a separate window where you can search for a course to which a file can be uploaded.').'");
               return false;
           }
  formname.submit();   formname.submit();
     }      }
   
       function ToSyllabus() {
           var cdom = '."'$dom'".';
           var cnum = document.rules.courseid.value;
           if (cdom == "" || cdom == null) {
               return;
           }
           if (cnum == "" || cnum == null) {
              return;
           }
           syllwin=window.open("/public/"+cdom+"/"+cnum+"/syllabus","LONCAPASyllabus",
                               "height=350,width=350,scrollbars=yes,menubar=no");
           return;
       }
   
 </script>  </script>
   
   <h3>'.&mt('Send scanned bubblesheet data to a course').'</h3>
   
 <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">  <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
 '.$default_form_data.'  '.$default_form_data.
 <table>    &Apache::lonhtmlcommon::start_pick_box().
 <tr><td>'.$select_link.'                             </td></tr>    &Apache::lonhtmlcommon::row_title(&mt('Course ID')).
 <tr><td>'.&mt('Course ID:').'     </td>    '<input name="courseid" type="text" size="30" />'.$select_link.
     <td><input name="courseid"   type="text" />      </td></tr>    &Apache::lonhtmlcommon::row_closure().
 <tr><td>'.&mt('Course Name:').'   </td>    &Apache::lonhtmlcommon::row_title(&mt('Course Name')).
     <td><input name="coursename" type="text" />      </td></tr>    '<input name="coursename" type="text" size="30" />'.$syllabuslink.
 <tr><td>'.&mt('Domain:').'        </td>    &Apache::lonhtmlcommon::row_closure().
     <td>'.$domsel.'                                  </td></tr>    &Apache::lonhtmlcommon::row_title(&mt('Domain')).
 <tr><td>'.&mt('File to upload:').'</td>    '<input name="domainid" type="hidden" />'.$domdesc.
     <td><input type="file" name="upfile" size="50" /></td></tr>    &Apache::lonhtmlcommon::row_closure().
 </table>    &Apache::lonhtmlcommon::row_title(&mt('File to upload')).
     '<input type="file" name="upfile" size="50" />'.
     &Apache::lonhtmlcommon::row_closure(1).
     &Apache::lonhtmlcommon::end_pick_box().'<br />
   
 <input name="command" value="scantronupload_save" type="hidden" />  <input name="command" value="scantronupload_save" type="hidden" />
 <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />  <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
 </form>  </form>
Line 7676  sub scantron_upload_scantron_data_save { Line 7787  sub scantron_upload_scantron_data_save {
  return '';   return '';
     }      }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});      my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
     $r->print(&mt("Doing upload to [_1]",$coursedata{'description'})." <br />");      my $uploadedfile;
     my $fname=$env{'form.upfile.filename'};      $r->print('<h3>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</h3>');
     #FIXME  
     #copied from lonnet::userfileupload()  
     #make that function able to target a specified course  
     # Replace Windows backslashes by forward slashes  
     $fname=~s/\\/\//g;  
     # Get rid of everything but the actual filename  
     $fname=~s/^.*\/([^\/]+)$/$1/;  
     # Replace spaces by underscores  
     $fname=~s/\s+/\_/g;  
     # Replace all other weird characters by nothing  
     $fname=~s/[^\w\.\-]//g;  
     # See if there is anything left  
     unless ($fname) { return 'error: no uploaded file'; }  
     my $uploadedfile=$fname;  
     $fname='scantron_orig_'.$fname;  
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
  $r->print(&mt("<span class=\"LC_error\">Error:</span> The file you attempted to upload, [_1]  contained no information. Please check that you entered the correct filename.",'<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));          $r->print(&mt('[_1]Error:[_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.','<span class="LC_error">','</span>','<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
     } else {      } else {
  my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);          my $result = 
  if ($result =~ m|^/uploaded/|) {              &Apache::lonnet::userfileupload('upfile','','scantron','','','',
     $r->print(&mt("<span class=\"LC_success\">Success:</span> Successfully uploaded [_1] bytes of data into location [_2]",                                              $env{'form.courseid'},$env{'form.domainid'});
   (length($env{'form.upfile'})-1),   if ($result =~ m{^/uploaded/}) {
   '<span class="LC_filename">'.$result."</span>"));      $r->print(&mt('[_1]Success:[_2] Successfully uploaded [_3] bytes of data into location: [_4]',
                             '<span class="LC_success">','</span>',(length($env{'form.upfile'})-1),
     '<span class="LC_filename">'.$result.'</span>'));
               ($uploadedfile) = ($result =~ m{/([^/]+)$});
               $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
                                                          $env{'form.courseid'},$uploadedfile));
  } else {   } else {
     $r->print(&mt("<span class=\"LC_error\">Error:</span> An error ([_1]) occurred when attempting to upload the file, [_2]",      $r->print(&mt('[_1]Error:[_2] An error ([_3]) occurred when attempting to upload the file, [_4]',
   $result,                            '<span class="LC_error">','</span>',$result,
   '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));    '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
   
  }   }
     }      }
     if ($symb) {      if ($symb) {
Line 7716  sub scantron_upload_scantron_data_save { Line 7816  sub scantron_upload_scantron_data_save {
     return '';      return '';
 }  }
   
   sub validate_uploaded_scantron_file {
       my ($cdom,$cname,$fname) = @_;
       my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
       my @lines;
       if ($scanlines ne '-1') {
           @lines=split("\n",$scanlines,-1);
       }
       my $output;
       if (@lines) {
           my (%counts,$max_match_format);
           my ($max_match_count,$max_match_pct) = (0,0);
           my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cname);
           my %idmap = &username_to_idmap($classlist);
           foreach my $key (keys(%idmap)) {
               my $lckey = lc($key);
               $idmap{$lckey} = $idmap{$key};
           }
           my %unique_formats;
           my @formatlines = &get_scantronformat_file();
           foreach my $line (@formatlines) {
               chomp($line);
               my @config = split(/:/,$line);
               my $idstart = $config[5];
               my $idlength = $config[6];
               if (($idstart ne '') && ($idlength > 0)) {
                   if (ref($unique_formats{$idstart.':'.$idlength}) eq 'ARRAY') {
                       push(@{$unique_formats{$idstart.':'.$idlength}},$config[0].':'.$config[1]); 
                   } else {
                       $unique_formats{$idstart.':'.$idlength} = [$config[0].':'.$config[1]];
                   }
               }
           }
           foreach my $key (keys(%unique_formats)) {
               my ($idstart,$idlength) = split(':',$key);
               %{$counts{$key}} = (
                                  'found'   => 0,
                                  'total'   => 0,
                                 );
               foreach my $line (@lines) {
                   next if ($line =~ /^#/);
                   next if ($line =~ /^[\s\cz]*$/);
                   my $id = substr($line,$idstart-1,$idlength);
                   $id = lc($id);
                   if (exists($idmap{$id})) {
                       $counts{$key}{'found'} ++;
                   }
                   $counts{$key}{'total'} ++;
               }
               if ($counts{$key}{'total'}) {
                   my $percent_match = (100*$counts{$key}{'found'})/($counts{$key}{'total'});
                   if (($max_match_format eq '') || ($percent_match > $max_match_pct)) {
                       $max_match_pct = $percent_match;
                       $max_match_format = $key;
                       $max_match_count = $counts{$key}{'total'};
                   }
               }
           }
           if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {
               my $format_descs;
               my $numwithformat = @{$unique_formats{$max_match_format}};
               for (my $i=0; $i<$numwithformat; $i++) {
                   my ($name,$desc) = split(':',$unique_formats{$max_match_format}[$i]);
                   if ($i<$numwithformat-2) {
                       $format_descs .= '"<i>'.$desc.'</i>", ';
                   } elsif ($i==$numwithformat-2) {
                       $format_descs .= '"<i>'.$desc.'</i>" '.&mt('and').' ';
                   } elsif ($i==$numwithformat-1) {
                       $format_descs .= '"<i>'.$desc.'</i>"';
                   }
               }
               my $showpct = sprintf("%.0f",$max_match_pct).'%';
               $output .= '<br />'.&mt('Comparison of student IDs in the uploaded file with the course roster found matches for [_1] of the [_2] entries in the file (for the format defined for [_3]).','<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs).
                          '<br />'.&mt('A low percentage of matches results from one of the following:').'<ul>'.
                          '<li>'.&mt('The file was uploaded to the wrong course').'</li>'.
                          '<li>'.&mt('The data are not in the format expected for the domain: [_1]',
                                     '<i>'.$cdom.'</i>').'</li>'.
                          '<li>'.&mt('Students did not bubble their IDs, or mis-bubbled them').'</li>'.
                          '<li>'.&mt('The course roster is not up to date').'</li>'.
                          '</ul>';
           }
       } else {
           $output = '<span class="LC_warning">'.&mt('Uploaded file contained no data').'</span>';
       }
       return $output;
   }
   
 sub valid_file {  sub valid_file {
     my ($requested_file)=@_;      my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {      foreach my $filename (sort(&scantron_filenames())) {
Line 7782  sub checkscantron_results { Line 7968  sub checkscantron_results {
     my %idmap=&Apache::grades::username_to_idmap($classlist);      my %idmap=&Apache::grades::username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,undef,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my (%grader_partids_by_symb,%grader_randomlists_by_symb);
       &graders_resources_pass(\@resources,\%grader_partids_by_symb,                             \%grader_randomlists_by_symb);
   
     my ($uname,$udom);      my ($uname,$udom);
     my (%scandata,%lastname,%bylast);      my (%scandata,%lastname,%bylast);
     $r->print('      $r->print('
Line 7797  sub checkscantron_results { Line 7986  sub checkscantron_results {
                                     'inline',undef,'checkscantron');                                      'inline',undef,'checkscantron');
     my ($username,$domain,$started);      my ($username,$domain,$started);
   
     &Apache::grades::scantron_get_maxbubble();  # Need the bubble lines array to parse.      &scantron_get_maxbubble();  # Need the bubble lines array to parse.
   
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
                                           'Processing first student');                                            'Processing first student');
Line 7838  sub checkscantron_results { Line 8027  sub checkscantron_results {
         ($username,$domain)=split(/:/,$uname);          ($username,$domain)=split(/:/,$uname);
         my $counter = -1;          my $counter = -1;
         foreach my $resource (@resources) {          foreach my $resource (@resources) {
               my $parts;
             my $ressymb = $resource->symb();              my $ressymb = $resource->symb();
             my ($analysis,$parts) =              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain);                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                   (my $analysis,$parts) =
                       &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain);
               } else {
                   $parts = $grader_partids_by_symb{$ressymb};
               }
             ($counter,my $recording) =              ($counter,my $recording) =
                 &verify_scantron_grading($resource,$domain,$username,$cid,$counter,                  &verify_scantron_grading($resource,$domain,$username,$cid,$counter,
                                          $scandata{$pid},$parts,                                           $scandata{$pid},$parts,
Line 7882  sub checkscantron_results { Line 8077  sub checkscantron_results {
             }              }
         }          }
     }      }
     $r->print('<p>'.&mt('Comparison of scantron data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');      $r->print('<p>'.&mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');
     $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');      $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');
     if ($passed) {      if ($passed) {
         $r->print(&mt('Students with exact correspondence between scantron data and submissions are as follows:').'<br /><br />');          $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".          $r->print(&Apache::loncommon::start_data_table()."\n".
                  &Apache::loncommon::start_data_table_header_row()."\n".                   &Apache::loncommon::start_data_table_header_row()."\n".
                  '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.                   '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
Line 7894  sub checkscantron_results { Line 8089  sub checkscantron_results {
                  &Apache::loncommon::end_data_table().'<br />');                   &Apache::loncommon::end_data_table().'<br />');
     }      }
     if ($failed) {      if ($failed) {
         $r->print(&mt('Students with differences between scantron data and submissions are as follows:').'<br /><br />');          $r->print(&mt('Students with differences between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".          $r->print(&Apache::loncommon::start_data_table()."\n".
                  &Apache::loncommon::start_data_table_header_row()."\n".                   &Apache::loncommon::start_data_table_header_row()."\n".
                  '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.                   '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
                  &Apache::loncommon::end_data_table_header_row()."\n".                   &Apache::loncommon::end_data_table_header_row()."\n".
                  $badstudents."\n".                   $badstudents."\n".
                  &Apache::loncommon::end_data_table()).'<br />'.                   &Apache::loncommon::end_data_table()).'<br />'.
                  &mt('Differences can occur if submissions were modified using manual grading after a scantron grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original scantron sheets.');                     &mt('Differences can occur if submissions were modified using manual grading after a bubblesheet grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.');  
     }      }
     $r->print('</form><br />'.$grading_menu_button);      $r->print('</form><br />'.$grading_menu_button);
     return;      return;
Line 8462  sub process_clicker { Line 8657  sub process_clicker {
     my %checked;      my %checked;
     foreach my $gradingmechanism ('attendance','personnel','specific','given') {      foreach my $gradingmechanism ('attendance','personnel','specific','given') {
        if ($env{'form.gradingmechanism'} eq $gradingmechanism) {         if ($env{'form.gradingmechanism'} eq $gradingmechanism) {
           $checked{$gradingmechanism}="checked='checked'";            $checked{$gradingmechanism}=' checked="checked"';
        }         }
     }      }
   
Line 8526  function sanitycheck() { Line 8721  function sanitycheck() {
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 <input type="file" name="upfile" size="50" />  <input type="file" name="upfile" size="50" />
 <br /><label>$type: $selectform</label>  <br /><label>$type: $selectform</label>
 <br /><label><input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" />$attendance </label>  <br /><label><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onClick="sanitycheck()" />$attendance </label>
 <br /><label><input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" />$personnel</label>  <br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onClick="sanitycheck()" />$personnel</label>
 <br /><label><input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" />$specific </label>  <br /><label><input type="radio" name="gradingmechanism" value="specific"$checked{'specific'} onClick="sanitycheck()" />$specific </label>
 <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />  <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />
 <br /><label><input type="radio" name="gradingmechanism" value="given" $checked{'given'} onClick="sanitycheck()" />$given </label>  <br /><label><input type="radio" name="gradingmechanism" value="given"$checked{'given'} onClick="sanitycheck()" />$given </label>
 <br />&nbsp;&nbsp;&nbsp;  <br />&nbsp;&nbsp;&nbsp;
 <input type="text" name="givenanswer" size="50" />  <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />  <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
Line 9009  sub handler { Line 9204  sub handler {
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {          } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             $request->print(&checkscantron_results($request));                   $request->print(&checkscantron_results($request));     
  } elsif ($command) {   } elsif ($command) {
     $request->print("Access Denied ($command)");      $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');
  }   }
     }      }
     if ($ssi_error) {      if ($ssi_error) {
Line 9177  ssi_with_retries() Line 9372  ssi_with_retries()
 =item  scantron_validate_ID() :   =item  scantron_validate_ID() : 
   
    Validates all scanlines in the selected file to not have any     Validates all scanlines in the selected file to not have any
    invalid or underspecified student IDs     invalid or underspecified student/employee IDs
   
 =back  =back
   

Removed from v.1.555  
changed lines
  Added in v.1.572


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