Diff for /loncom/homework/grades.pm between versions 1.544 and 1.559

version 1.544, 2008/12/31 18:09:57 version 1.559, 2009/03/19 19:09:47
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 268  sub reset_caches { Line 294  sub reset_caches {
     sub get_radiobutton_correct_foil {      sub get_radiobutton_correct_foil {
  my ($partid,$respid,$symb,$uname,$udom)=@_;   my ($partid,$respid,$symb,$uname,$udom)=@_;
  my $analyze = &get_analyze($symb,$uname,$udom);   my $analyze = &get_analyze($symb,$uname,$udom);
  foreach my $foil (@{&get_order($partid,$respid,$symb,$uname,$udom)}) {          my $foils = &get_order($partid,$respid,$symb,$uname,$udom);
     if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {          if (ref($foils) eq 'ARRAY') {
  return $foil;      foreach my $foil (@{$foils}) {
           if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
       return $foil;
           }
     }      }
  }   }
     }      }
   
       sub scantron_partids_tograde {
           my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_;
           my (%analysis,@parts);
           if (ref($resource)) {
               my $symb = $resource->symb();
               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') {
                   %analysis = %{$analyze};
               }
               if (ref($analysis{'parts'}) eq 'ARRAY') {
                   foreach my $part (@{$analysis{'parts'}}) {
                       my ($id,$respid) = split(/\./,$part);
                       if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
                           push(@parts,$part);
                       }
                   }
               }
           }
           return (\%analysis,\@parts);
       }
   
 }  }
   
 #--- Clean response type for display  #--- Clean response type for display
Line 699  sub verifyreceipt { Line 754  sub verifyreceipt {
   
     my $title.=      my $title.=
  '<h3><span class="LC_info">'.   '<h3><span class="LC_info">'.
  &mt('Verifying Submission Receipt [_1]',$receipt).   &mt('Verifying  Receipt No. [_1]',$receipt).
  '</span></h3>'."\n".   '</span></h3>'."\n".
  '<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}).   '<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}).
  '</h4>'."\n";   '</h4>'."\n";
Line 779  sub listStudents { Line 834  sub listStudents {
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
     my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};      my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
     my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};      my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
     my $viewgrade = $env{'form.showgrading'} eq 'yes' ? &mt('View/Grade/Regrade') : &mt('View');      my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
     $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ?       $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
  &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};   &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
   
     my $result='<h3><span class="LC_info">&nbsp;'.$viewgrade.      my $result='<h3><span class="LC_info">&nbsp;'
  &mt(' Submissions for a Student or a Group of Students')   .&mt("$viewgrade Submissions for a Student or a Group of Students")
  .'</span></h3>';   .'</span></h3>';
   
     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));      my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
   
     my %lt = ( 'multiple' =>      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 889  LISTJAVASCRIPT Line 942  LISTJAVASCRIPT
  &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'<br />';   &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'<br />';
     }      }
   
     $gradeTable.=&mt('To [_1] 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.',lc($viewgrade)).'<br />'."\n".      $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".
  '<input type="hidden" name="command" value="processGroup" />'."\n";   '<input type="hidden" name="command" value="processGroup" />'."\n";
   
 # checkall buttons  # checkall buttons
Line 2105  KEYWORDS Line 2158  KEYWORDS
  }   }
  $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.   $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.
     &cleanRecord($subval,$responsetype,$symb,$partid,      &cleanRecord($subval,$responsetype,$symb,$partid,
  $respid,\%record,$order);   $respid,\%record,$order,undef,$uname,$udom);
  if ($similar) {$lastsubonly.="<br /><br />$similar\n";}   if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
  $lastsubonly.='</div>';   $lastsubonly.='</div>';
     }      }
Line 2234  KEYWORDS Line 2287  KEYWORDS
     '<option>7</option><option>10</option></select>'."\n";      '<option>7</option><option>10</option></select>'."\n";
  my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');   my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
  $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;   $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
  $endform.=&mt('[_1]student(s)',$ntstu);   $endform.=&mt('[quant,_1,student]',$ntstu);
  $endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.   $endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.
     'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".      'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
     '<input type="button" value="'.&mt('Next').'" '.      '<input type="button" value="'.&mt('Next').'" '.
Line 2317  sub get_last_submission { Line 2370  sub get_last_submission {
  $$returnhash{$version.':keys'}))) {   $$returnhash{$version.':keys'}))) {
  $lasthash{$key}=$$returnhash{$version.':'.$key};   $lasthash{$key}=$$returnhash{$version.':'.$key};
  $timestamp =    $timestamp = 
     scalar(localtime($$returnhash{$version.':timestamp'}));      &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
     }      }
  }   }
  foreach my $key (keys(%lasthash)) {   foreach my $key (keys(%lasthash)) {
Line 3426  sub editgrades { Line 3479  sub editgrades {
     if ($part !~ m/^\Q$partid\E/) { next;}      if ($part !~ m/^\Q$partid\E/) { next;}
     if ($type eq 'awarded' || $type eq 'solved') { next; }      if ($type eq 'awarded' || $type eq 'solved') { next; }
     my $display=&Apache::lonnet::metadata($url,$stores.'.display');      my $display=&Apache::lonnet::metadata($url,$stores.'.display');
     $display =~ s/\[Part: (\w)+\]//;      $display =~ s/\[Part: \Q$part\E\]//;
             my $narrowtext = &mt('Tries');              my $narrowtext = &mt('Tries');
     $display =~ s/Number of Attempts/$narrowtext/;      $display =~ s/Number of Attempts/$narrowtext/;
     $header .= '<th align="center">'.&mt('Old').' '.$display.'</th>'.      $header .= '<th align="center">'.&mt('Old').' '.$display.'</th>'.
Line 3742  ENDPICK Line 3795  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 4655  Next each scanline is checked for any er Line 4708  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 4728  sub getSequenceDropDown { Line 4781  sub getSequenceDropDown {
 }  }
   
 my %bubble_lines_per_response;     # no. bubble lines for each response.  my %bubble_lines_per_response;     # no. bubble lines for each response.
                                    # index is "symb.part_id"                                     # key is zero-based index - 0, 1, 2 ...
   
 my %first_bubble_line;             # First bubble line no. for each bubble.  my %first_bubble_line;             # First bubble line no. for each bubble.
   
Line 4769  sub restore_bubble_lines { Line 4822  sub restore_bubble_lines {
             $env{"form.scantron.responsetype.$line"};              $env{"form.scantron.responsetype.$line"};
  $line++;   $line++;
     }      }
   
 }  }
   
 #  Given the parsed scanline, get the response for   #  Given the parsed scanline, get the response for 
Line 4778  sub restore_bubble_lines { Line 4830  sub restore_bubble_lines {
 sub get_response_bubbles {  sub get_response_bubbles {
     my ($parsed_line, $response)  = @_;      my ($parsed_line, $response)  = @_;
   
   
     my $bubble_line = $first_bubble_line{$response-1} +1;      my $bubble_line = $first_bubble_line{$response-1} +1;
     my $bubble_lines= $bubble_lines_per_response{$response-1};      my $bubble_lines= $bubble_lines_per_response{$response-1};
           
Line 5134  sub scantron_selectphase { Line 5185  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 5177  sub scantron_selectphase { Line 5232  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 5238  sub get_scantron_config { Line 5293  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 5277  sub username_to_idmap { Line 5332  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 5451  sub digits_to_letters { Line 5506  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 5487  sub scantron_parse_scanline { Line 5542  sub scantron_parse_scanline {
     my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;      my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;
   
     my %record;      my %record;
     my $questions=substr($line,$$scantron_config{'Qstart'}-1);  # Answers      my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};
       my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff      my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff
     if (!($$scantron_config{'CODElocation'} eq 0 ||      if (!($$scantron_config{'CODElocation'} eq 0 ||
   $$scantron_config{'CODElocation'} eq 'none')) {    $$scantron_config{'CODElocation'} eq 'none')) {
Line 6760  ENDSCRIPT Line 6816  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 7234  sub scantron_get_maxbubble { Line 7290  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 = ();
     %first_bubble_line         = ();      %first_bubble_line         = ();
     %subdivided_bubble_lines   = ();      %subdivided_bubble_lines   = ();
     %responsetype_per_response = ();      %responsetype_per_response = ();
     
     my $response_number = 0;      my $response_number = 0;
     my $bubble_line     = 0;      my $bubble_line     = 0;
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
Line 7301  sub scantron_get_maxbubble { Line 7357  sub scantron_get_maxbubble {
     }      }
         }          }
     }      }
     &Apache::lonnet::delenv('scantron\.');      &Apache::lonnet::delenv('scantron.');
   
     &save_bubble_lines();      &save_bubble_lines();
     $env{'form.scantron_maxbubble'} =      $env{'form.scantron_maxbubble'} =
Line 7309  sub scantron_get_maxbubble { Line 7365  sub scantron_get_maxbubble {
     return $env{'form.scantron_maxbubble'};      return $env{'form.scantron_maxbubble'};
 }  }
   
 sub scantron_partids_tograde {  
     my ($resource,$cid,$uname,$udom) = @_;  
     my (%analysis,@parts);   
   
     if (ref($resource)) {  
         my $symb = $resource->symb();  
         my $result=&ssi_with_retries($resource->src(), $ssi_retries,  
                                         ('symb' => $symb,  
                                          'grade_target' => 'analyze',  
                                          'grade_courseid' => $cid,  
                                          'grade_domain' => $udom,  
                                          'grade_username' => $uname));  
         my (undef, $an) = split(/_HASH_REF__/,$result, 2);  
         %analysis = &Apache::lonnet::str2hash($an);  
   
         if (ref($analysis{'parts'}) eq 'ARRAY') {  
             foreach my $part (@{$analysis{'parts'}}) {  
                 my ($id,$respid) = split(/\./,$part);  
                 if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {  
                     push(@parts,$part);  
                 }  
             }  
         }  
     }  
     return (\%analysis,\@parts);  
 }  
   
 sub scantron_validate_missingbubbles {  sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     #get student info      #get student info
Line 7407  sub scantron_process_students { Line 7436  sub scantron_process_students {
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
       my (%grader_partids_by_symb,%grader_randomlists_by_symb);
     my ($uname,$udom,%partids_by_symb);      &graders_resources_pass(\@resources,\%grader_partids_by_symb,
                               \%grader_randomlists_by_symb);
     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'},$uname,$udom);              &scantron_partids_tograde($resource,$env{'request.course.id'},
         $partids_by_symb{$ressymb} = $parts;                                        $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'};
               }
           }
     }      }
 #    $r->print("geto ".scalar(@resources)."<br />");  
       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">
   <input type="hidden" name="command" value="scantron_configphase" />    <input type="hidden" name="command" value="scantron_configphase" />
Line 7439  SCANTRONFORM Line 7477  SCANTRONFORM
     my $started;      my $started;
   
     &scantron_get_maxbubble(); # Need the bubble lines array to parse.      &scantron_get_maxbubble(); # Need the bubble lines array to parse.
       
   
     # If an ssi failed in scantron_get_maxbubble, put an error message out to      # If an ssi failed in scantron_get_maxbubble, put an error message out to
     # the user and return.      # the user and return.
Line 7480  SCANTRONFORM Line 7517  SCANTRONFORM
   }    }
   ($uname,$udom)=split(/:/,$uname);    ($uname,$udom)=split(/:/,$uname);
   
           my %partids_by_symb;
           foreach my $resource (@resources) {
               my $ressymb = $resource->symb();
               if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                   (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();
   &Apache::lonnet::appenv($scan_record);    &Apache::lonnet::appenv($scan_record);
   
Line 7496  SCANTRONFORM Line 7546  SCANTRONFORM
         }          }
   
         if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,          if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                    @resources) eq 'ssi_error') {                                     \@resources,\%partids_by_symb) 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 7514  SCANTRONFORM Line 7564  SCANTRONFORM
             my $studentrecord = '';              my $studentrecord = '';
             my $counter = -1;              my $counter = -1;
             foreach my $resource (@resources) {              foreach my $resource (@resources) {
                   my $ressymb = $resource->symb();
                 ($counter,my $recording) =                  ($counter,my $recording) =
                     &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},                      &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                              $counter,$studentdata,\%partids_by_symb,                                               $counter,$studentdata,$partids_by_symb{$ressymb},
                                              \%scantron_config,\%lettdig,$numletts);                                               \%scantron_config,\%lettdig,$numletts);
                 $studentrecord .= $recording;                  $studentrecord .= $recording;
             }              }
             if ($studentrecord ne $studentdata) {              if ($studentrecord ne $studentdata) {
                   &Apache::lonxml::clear_problem_counter();
                   if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
                                              \@resources,\%partids_by_symb) eq 'ssi_error') {
                       $ssi_error = 0; # So end of handler error message does not trigger.
                       $r->print("</form>");
                       &ssi_print_error($r);
                       $r->print(&show_grading_menu_form($symb));
                       &Apache::lonnet::remove_lock($lock);
                       delete($completedstudents{$uname});
                       return '';
                   }
                 $counter = -1;                  $counter = -1;
                 $studentrecord = '';                  $studentrecord = '';
                 foreach my $resource (@resources) {                  foreach my $resource (@resources) {
                       my $ressymb = $resource->symb();
                     ($counter,my $recording) =                      ($counter,my $recording) =
                         &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},                          &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
                                                  $counter,$studentdata,\%partids_by_symb,                                                   $counter,$studentdata,$partids_by_symb{$ressymb},
                                                  \%scantron_config,\%lettdig,$numletts);                                                   \%scantron_config,\%lettdig,$numletts);
                     $studentrecord .= $recording;                      $studentrecord .= $recording;
                 }                  }
Line 7563  SCANTRONFORM Line 7626  SCANTRONFORM
         if (&Apache::loncommon::connection_aborted($r)) { last; }          if (&Apache::loncommon::connection_aborted($r)) { last; }
     } continue {      } continue {
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
  &Apache::lonnet::delenv('scantron\.');   &Apache::lonnet::delenv('scantron.');
     }      }
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);      &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
     &Apache::lonnet::remove_lock($lock);      &Apache::lonnet::remove_lock($lock);
Line 7575  SCANTRONFORM Line 7638  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) = @_;      my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts) = @_;
     foreach my $resource (@resources) {      if (ref($resources) eq 'ARRAY') {
         my %form = ('submitted'     => 'scantron',          my $count = 0;
                     'grade_target'  => 'grade',          foreach my $resource (@{$resources}) {
                     'grade_username'=> $uname,              my $ressymb = $resource->symb();
                     'grade_domain'  => $udom,              my %form = ('submitted'      => 'scantron',
                     'grade_courseid'=> $env{'request.course.id'},                          'grade_target'   => 'grade',
                     'grade_symb'    => $resource->symb(),                          'grade_username' => $uname,
                     'code'          => $scancode);                          'grade_domain'   => $udom,
         my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);                          'grade_courseid' => $env{'request.course.id'},
         return 'ssi_error' if ($ssi_error);                          'grade_symb'     => $ressymb,
         last if (&Apache::loncommon::connection_aborted($r));                          'CODE'           => $scancode
                          );
               if (ref($parts) eq 'HASH') {
                   if (ref($parts->{$ressymb}) eq 'ARRAY') {
                       foreach my $part (@{$parts->{$ressymb}}) {
                           $form{'scantron_questnum_start.'.$part} =
                               1+$env{'form.scantron.first_bubble_line.'.$count};
                           $count++;
                       }
                   }
               }
               my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
               return 'ssi_error' if ($ssi_error);
               last if (&Apache::loncommon::connection_aborted($r));
           }
     }      }
     return;      return;
 }  }
Line 7759  sub checkscantron_results { Line 7857  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 ($uname,$udom,%partids_by_symb);      my (%grader_partids_by_symb,%grader_randomlists_by_symb);
     foreach my $resource (@resources) {      &graders_resources_pass(\@resources,\%grader_partids_by_symb,                             \%grader_randomlists_by_symb);
         my $ressymb = $resource->symb();  
         my ($analysis,$parts) =      my ($uname,$udom);
             &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);  
         $partids_by_symb{$ressymb} = $parts;  
     }  
     my (%scandata,%lastname,%bylast);      my (%scandata,%lastname,%bylast);
     $r->print('      $r->print('
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n");  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n");
Line 7778  sub checkscantron_results { Line 7873  sub checkscantron_results {
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status',      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status',
                                     'Progress of Scantron Data/Submission Records Comparison',$count,                                      'Progress of Scantron Data/Submission Records Comparison',$count,
                                     'inline',undef,'checkscantron');                                      'inline',undef,'checkscantron');
     my ($username,$domain,$uname,$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 7821  sub checkscantron_results { Line 7916  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();
               if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                   (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},\%partids_by_symb,                                           $scandata{$pid},$parts,
                                          \%scantron_config,\%lettdig,$numletts);                                           \%scantron_config,\%lettdig,$numletts);
             $record{$pid} .= $recording;              $record{$pid} .= $recording;
         }          }
Line 7888  sub checkscantron_results { Line 7992  sub checkscantron_results {
 }  }
   
 sub verify_scantron_grading {  sub verify_scantron_grading {
     my ($resource,$domain,$username,$cid,$counter,$scandata,$partids_by_symb,      my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,
         $scantron_config,$lettdig,$numletts) = @_;          $scantron_config,$lettdig,$numletts) = @_;
     my ($record,%expected,%startpos);      my ($record,%expected,%startpos);
     return ($counter,$record) if (!ref($resource));      return ($counter,$record) if (!ref($resource));
     return ($counter,$record) if (!$resource->is_problem());      return ($counter,$record) if (!$resource->is_problem());
     my $symb = $resource->symb();      my $symb = $resource->symb();
     return ($counter,$record) if (ref($partids_by_symb) ne 'HASH');      return ($counter,$record) if (ref($partids) ne 'ARRAY');
     return ($counter,$record) if (ref($partids_by_symb->{$symb}) ne 'ARRAY');      foreach my $part_id (@{$partids}) {
     foreach my $part_id (@{$partids_by_symb->{$symb}}) {  
         $counter ++;          $counter ++;
         $expected{$part_id} = 0;          $expected{$part_id} = 0;
         if ($env{"form.scantron.sub_bubblelines.$counter"}) {          if ($env{"form.scantron.sub_bubblelines.$counter"}) {
Line 7989  sub verify_scantron_grading { Line 8092  sub verify_scantron_grading {
                 }                  }
             }              }
         }          }
         foreach my $part_id (@{$partids_by_symb->{$symb}}) {          foreach my $part_id (@{$partids}) {
             if ($recorded{$part_id} eq '') {              if ($recorded{$part_id} eq '') {
                 for (my $i=0; $i<$expected{$part_id}; $i++) {                  for (my $i=0; $i<$expected{$part_id}; $i++) {
                     for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {                      for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
Line 8864  ENDHEADER Line 8967  ENDHEADER
        }         }
     }      }
 # We are done  # We are done
     $result.='<br />'.&mt('Successfully stored grades for [_1] student(s).',$storecount).      $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).
              '</td></tr></table>'."\n".               '</td></tr></table>'."\n".
              '</td></tr></table><br /><br />'."\n";               '</td></tr></table><br /><br />'."\n";
     return $result.&show_grading_menu_form($symb);      return $result.&show_grading_menu_form($symb);
Line 9158  ssi_with_retries() Line 9261  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.544  
changed lines
  Added in v.1.559


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