Diff for /loncom/homework/grades.pm between versions 1.336 and 1.436

version 1.336, 2006/03/19 21:38:55 version 1.436, 2007/08/29 13:41:32
Line 36  use Apache::lonhtmlcommon; Line 36  use Apache::lonhtmlcommon;
 use Apache::lonnavmaps;  use Apache::lonnavmaps;
 use Apache::lonhomework;  use Apache::lonhomework;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use Apache::lonmsg qw(:user_normal_msg);  use Apache::lonmsg();
 use Apache::Constants qw(:common);  use Apache::Constants qw(:common);
 use Apache::lonlocal;  use Apache::lonlocal;
   use Apache::lonenc;
 use String::Similarity;  use String::Similarity;
   use LONCAPA;
   
 use POSIX qw(floor);  use POSIX qw(floor);
   
 my %oldessays=();  
 my %perm=();  my %perm=();
   my %bubble_lines_per_response;     # no. bubble lines for each response.
                                      # index is "symb.part_id"
   
   
 # ----- These first few routines are general use routines.----  # ----- These first few routines are general use routines.----
 #  #
Line 90  sub get_symb { Line 96  sub get_symb {
     return ();      return ();
  }   }
     }      }
       &Apache::lonenc::check_decrypt(\$symb);
     return ($symb);      return ($symb);
 }  }
   
Line 98  sub get_symb { Line 105  sub get_symb {
 sub nameUserString {  sub nameUserString {
     my ($type,$fullname,$uname,$udom) = @_;      my ($type,$fullname,$uname,$udom) = @_;
     if ($type eq 'header') {      if ($type eq 'header') {
  return '<b>&nbsp;Fullname&nbsp;</b><font color="#999999">(Username)</font>';   return '<b>&nbsp;Fullname&nbsp;</b><span class="LC_internal_info">(Username)</span>';
     } else {      } else {
  return '&nbsp;'.$fullname.'<font color="#999999">&nbsp;('.$uname.   return '&nbsp;'.$fullname.'<span class="LC_internal_info">&nbsp;('.$uname.
     ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</font>';      ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</span>';
     }      }
 }  }
   
Line 109  sub nameUserString { Line 116  sub nameUserString {
 #--- Indicate if a response type is coded handgraded or not. ---  #--- Indicate if a response type is coded handgraded or not. ---
 sub response_type {  sub response_type {
     my ($symb) = shift;      my ($symb) = shift;
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);  
     my $allkeys = &Apache::lonnet::metadata($url,'keys');      my $navmap = Apache::lonnavmaps::navmap->new();
     my %vPart;      my $res = $navmap->getBySymb($symb);
     foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {      my $partlist = $res->parts();
  $vPart{$partid}=1;      my %vPart = 
     }   map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
     my %seen = ();      my (%response_types,%handgrade);
     my (@partlist,%handgrade,%responseType);      foreach my $part (@{ $partlist }) {
     foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {   next if (%vPart && !exists($vPart{$part}));
  if (/^\w+response_.*/ || /^Task_/) {  
     my ($responsetype,$part) = split(/_/,$_,2);   my @types = $res->responseType($part);
     my ($partid,$respid) = split(/_/,$part);   my @ids = $res->responseIds($part);
     if ($responsetype eq 'Task') { $respid='0'; }   for (my $i=0; $i < scalar(@ids); $i++) {
     if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) {      $response_types{$part}{$ids[$i]} = $types[$i];
  next;      $handgrade{$part.'_'.$ids[$i]} = 
     }   &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
     if (%vPart && !exists($vPart{$partid})) {       '.handgrade',$symb);
  next;   }
     }      }
     $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!      return ($partlist,\%handgrade,\%response_types);
     my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb);  }
     $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no');   
     if (!exists($responseType{$partid})) { $responseType{$partid}={}; }  sub flatten_responseType {
     $responseType{$partid}->{$respid}=$responsetype;      my ($responseType) = @_;
     next if ($seen{$partid} > 0);      my @part_response_id =
     $seen{$partid}++;   map { 
     push @partlist,$partid;      my $part = $_;
  }      map {
     }   [$part,$_]
     return (\@partlist,\%handgrade,\%responseType);   } sort(keys(%{ $responseType->{$part} }));
    } sort(keys(%$responseType));
       return @part_response_id;
 }  }
   
 sub get_display_part {  sub get_display_part {
     my ($partID,$symb)=@_;      my ($partID,$symb)=@_;
     my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);      my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
     if (defined($display) and $display ne '') {      if (defined($display) and $display ne '') {
  $display.= " (<font color=\"#999900\">id $partID</font>)";   $display.= " (<span class=\"LC_internal_info\">id $partID</span>)";
     } else {      } else {
  $display=$partID;   $display=$partID;
     }      }
Line 158  sub showResourceInfo { Line 167  sub showResourceInfo {
     my ($symb,$probTitle,$checkboxes) = @_;      my ($symb,$probTitle,$checkboxes) = @_;
     my $col=3;      my $col=3;
     if ($checkboxes) { $col=4; }      if ($checkboxes) { $col=4; }
     my $result ='<table border="0">'.      my $result = '<h3>'.&mt('Current Resource').': '.$probTitle.'</h3>'."\n";
  '<tr><td colspan="'.$col.'"><font size="+1"><b>'.&mt('Current Resource').': </b>'.      $result .='<table border="0">';
  $probTitle.'</font></td></tr>'."\n";  
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my ($partlist,$handgrade,$responseType) = &response_type($symb);
     my %resptype = ();      my %resptype = ();
     my $hdgrade='no';      my $hdgrade='no';
     my %partsseen;      my %partsseen;
     for my $part_resID (sort keys(%$handgrade)) {      foreach my $partID (sort keys(%$responseType)) {
  my $handgrade=$$handgrade{$part_resID};   foreach my $resID (sort keys(%{ $responseType->{$partID} })) {
  my ($partID,$resID) = split(/_/,$part_resID);      my $handgrade=$$handgrade{$partID.'_'.$resID};
  my $responsetype = $responseType->{$partID}->{$resID};      my $responsetype = $responseType->{$partID}->{$resID};
  $hdgrade = $handgrade if ($handgrade eq 'yes');      $hdgrade = $handgrade if ($handgrade eq 'yes');
  $result.='<tr>';      $result.='<tr>';
  if ($checkboxes) {      if ($checkboxes) {
     if (exists($partsseen{$partID})) {   if (exists($partsseen{$partID})) {
  $result.="<td>&nbsp;</td>";      $result.="<td>&nbsp;</td>";
     } else {   } else {
  $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='on' /></td>";      $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='checked' /></td>";
    }
    $partsseen{$partID}=1;
     }      }
     $partsseen{$partID}=1;      my $display_part=&get_display_part($partID,$symb);
  }      $result.='<td><b>Part: </b>'.$display_part.' <span class="LC_internal_info">'.
  my $display_part=&get_display_part($partID,$symb);   $resID.'</span></td>'.
  $result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'.   '<td><b>Type: </b>'.$responsetype.'</td></tr>';
     $resID.'</font></td>'.  
     '<td><b>Type: </b>'.$responsetype.'</td></tr>';  
 #    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';  #    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';
    }
     }      }
     $result.='</table>'."\n";      $result.='</table>'."\n";
     return $result,$responseType,$hdgrade,$partlist,$handgrade;      return $result,$responseType,$hdgrade,$partlist,$handgrade;
 }  }
   
   sub reset_caches {
       &reset_analyze_cache();
       &reset_perm();
   }
   
 sub get_order {  {
     my ($partid,$respid,$symb,$uname,$udom)=@_;      my %analyze_cache;
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);  
     $url=&Apache::lonnet::clutter($url);      sub reset_analyze_cache {
     my $subresult=&Apache::lonnet::ssi($url,   undef(%analyze_cache);
        ('grade_target' => 'analyze'),      }
        ('grade_domain' => $udom),  
        ('grade_symb' => $symb),      sub get_analyze {
        ('grade_courseid' =>    my ($symb,$uname,$udom)=@_;
         $env{'request.course.id'}),   my $key = "$symb\0$uname\0$udom";
        ('grade_username' => $uname));   return $analyze_cache{$key} if (exists($analyze_cache{$key}));
     (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);  
     my %analyze=&Apache::lonnet::str2hash($subresult);   my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     return ($analyze{"$partid.$respid.shown"});   $url=&Apache::lonnet::clutter($url);
    my $subresult=&Apache::lonnet::ssi($url,
      ('grade_target' => 'analyze'),
      ('grade_domain' => $udom),
      ('grade_symb' => $symb),
      ('grade_courseid' => 
       $env{'request.course.id'}),
      ('grade_username' => $uname));
    (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
    my %analyze=&Apache::lonnet::str2hash($subresult);
    return $analyze_cache{$key} = \%analyze;
       }
   
       sub get_order {
    my ($partid,$respid,$symb,$uname,$udom)=@_;
    my $analyze = &get_analyze($symb,$uname,$udom);
    return $analyze->{"$partid.$respid.shown"};
       }
   
       sub get_radiobutton_correct_foil {
    my ($partid,$respid,$symb,$uname,$udom)=@_;
    my $analyze = &get_analyze($symb,$uname,$udom);
    foreach my $foil (@{&get_order($partid,$respid,$symb,$uname,$udom)}) {
       if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
    return $foil;
       }
    }
       }
 }  }
   
 #--- Clean response type for display  #--- Clean response type for display
 #--- Currently filters option/rank/radiobutton/match/essay/Task  #--- Currently filters option/rank/radiobutton/match/essay/Task
 #        response types only.  #        response types only.
 sub cleanRecord {  sub cleanRecord {
     my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,      my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,
  $uname,$udom) = @_;   $uname,$udom) = @_;
     my $grayFont = '<font color="#999999">';      my $grayFont = '<span class="LC_internal_info">';
     if ($response =~ /^(option|rank)$/) {      if ($response =~ /^(option|rank)$/) {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
Line 222  sub cleanRecord { Line 263  sub cleanRecord {
     } else {      } else {
  $toprow.='<td><i>'.$answer{$foil}.'&nbsp;</i></td>';   $toprow.='<td><i>'.$answer{$foil}.'&nbsp;</i></td>';
     }      }
     $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';      $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';
  }   }
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.      '<tr valign="top"><td>'.$grayFont.'Option ID</span></td>'.
     $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';      $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
     } elsif ($response eq 'match') {      } elsif ($response eq 'match') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
Line 237  sub cleanRecord { Line 278  sub cleanRecord {
     my $item=shift(@items);      my $item=shift(@items);
     if ($grading{$foil} == 1) {      if ($grading{$foil} == 1) {
  $toprow.='<td><b>'.$item.'&nbsp;</b></td>';   $toprow.='<td><b>'.$item.'&nbsp;</b></td>';
  $middlerow.='<td><b>'.$grayFont.$answer{$foil}.'&nbsp;</font></b></td>';   $middlerow.='<td><b>'.$grayFont.$answer{$foil}.'&nbsp;</span></b></td>';
     } else {      } else {
  $toprow.='<td><i>'.$item.'&nbsp;</i></td>';   $toprow.='<td><i>'.$item.'&nbsp;</i></td>';
  $middlerow.='<td><i>'.$grayFont.$answer{$foil}.'&nbsp;</font></i></td>';   $middlerow.='<td><i>'.$grayFont.$answer{$foil}.'&nbsp;</span></i></td>';
     }      }
     $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';      $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';
  }   }
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Item ID</font></td>'.      '<tr valign="top"><td>'.$grayFont.'Item ID</span></td>'.
     $middlerow.'</tr>'.      $middlerow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.      '<tr valign="top"><td>'.$grayFont.'Option ID</span></td>'.
     $bottomrow.'</tr>'.'</table></blockquote>';      $bottomrow.'</tr>'.'</table></blockquote>';
     } elsif ($response eq 'radiobutton') {      } elsif ($response eq 'radiobutton') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
  my ($toprow,$bottomrow);   my ($toprow,$bottomrow);
  my $correct=($order->[0])+1;   my $correct = 
  for (my $i=1;$i<=$#$order;$i++) {      &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom);
     my $foil=$order->[$i];   foreach my $foil (@$order) {
     if (exists($answer{$foil})) {      if (exists($answer{$foil})) {
  if ($i == $correct) {   if ($foil eq $correct) {
     $toprow.='<td><b>true</b></td>';      $toprow.='<td><b>true</b></td>';
  } else {   } else {
     $toprow.='<td><i>true</i></td>';      $toprow.='<td><i>true</i></td>';
Line 265  sub cleanRecord { Line 306  sub cleanRecord {
     } else {      } else {
  $toprow.='<td>false</td>';   $toprow.='<td>false</td>';
     }      }
     $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';      $bottomrow.='<td>'.$grayFont.$foil.'</span>&nbsp;</td>';
  }   }
  return '<blockquote><table border="1">'.   return '<blockquote><table border="1">'.
     '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.      '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
     '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.      '<tr valign="top"><td>'.$grayFont.'Option ID</span></td>'.
     $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';      $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
     } elsif ($response eq 'essay') {      } elsif ($response eq 'essay') {
  if (! exists ($env{'form.'.$symb})) {   if (! exists ($env{'form.'.$symb})) {
Line 487  sub jscriptNform { Line 528  sub jscriptNform {
  '    }'."\n".   '    }'."\n".
  '</script>'."\n";   '</script>'."\n";
     $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".      $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n".   '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n".
  '<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".   '<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".
Line 532  sub compute_points { Line 573  sub compute_points {
 #  #
   
 sub most_similar {  sub most_similar {
     my ($uname,$udom,$uessay)=@_;      my ($uname,$udom,$uessay,$old_essays)=@_;
   
 # ignore spaces and punctuation  # ignore spaces and punctuation
   
Line 549  sub most_similar { Line 590  sub most_similar {
     my $scrsid='';      my $scrsid='';
     my $sessay='';      my $sessay='';
 # go through all essays ...  # go through all essays ...
     foreach my $tkey (keys %oldessays) {      foreach my $tkey (keys(%$old_essays)) {
  my ($tname,$tdom,$tcrsid)=split(/\./,$tkey);   my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey));
 # ... except the same student  # ... except the same student
         if (($tname ne $uname) || ($tdom ne $udom)) {          next if (($tname eq $uname) && ($tdom eq $udom));
     my $tessay=$oldessays{$tkey};   my $tessay=$old_essays->{$tkey};
             $tessay=~s/\W+/ /gs;   $tessay=~s/\W+/ /gs;
 # String similarity gives up if not even limit  # String similarity gives up if not even limit
             my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);   my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
 # Found one  # Found one
             if ($tsimilar>$limit) {   if ($tsimilar>$limit) {
  $limit=$tsimilar;      $limit=$tsimilar;
                 $sname=$tname;      $sname=$tname;
                 $sdom=$tdom;      $sdom=$tdom;
                 $scrsid=$tcrsid;      $scrsid=$tcrsid;
                 $sessay=$oldessays{$tkey};      $sessay=$old_essays->{$tkey};
             }   }
         }   
     }      }
     if ($limit>0.6) {      if ($limit>0.6) {
        return ($sname,$sdom,$scrsid,$sessay,$limit);         return ($sname,$sdom,$scrsid,$sessay,$limit);
Line 586  sub verifyreceipt { Line 626  sub verifyreceipt {
     my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.      my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.
  $env{'form.receipt'};   $env{'form.receipt'};
     $receipt     =~ s/[^\-\d]//g;      $receipt     =~ s/[^\-\d]//g;
     my $symb     = &Apache::lonnet::symbread();      my ($symb)   = &get_symb($request);
   
     my $title.='<h3><font color="#339933">Verifying Submission Receipt '.      my $title.='<h3><span class="LC_info">Verifying Submission Receipt '.
  $receipt.'</h3></font>'."\n".   $receipt.'</h3></span>'."\n".
  '<font size=+1><b>Resource: </b>'.$env{'form.probTitle'}.'</font><br /><br />'."\n";   '<h4><b>Resource: </b>'.$env{'form.probTitle'}.'</h4><br /><br />'."\n";
   
     my ($string,$contents,$matches) = ('','',0);      my ($string,$contents,$matches) = ('','',0);
     my (undef,undef,$fullname) = &getclasslist('all','0');      my (undef,undef,$fullname) = &getclasslist('all','0');
           
     my $receiptparts=0;      my $receiptparts=0;
     if ($env{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; }      if ($env{"course.$courseid.receiptalg"} eq 'receipt2' ||
    $env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; }
     my $parts=['0'];      my $parts=['0'];
     if ($receiptparts) { ($parts)=&response_type($symb); }      if ($receiptparts) { ($parts)=&response_type($symb); }
     foreach (sort       foreach (sort 
Line 611  sub verifyreceipt { Line 652  sub verifyreceipt {
     if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) {      if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) {
  $contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".   $contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".
     '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.      '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
     '\')"; TARGET=_self>'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".      '\');" target="_self">'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".
     '<td>&nbsp;'.$uname.'&nbsp;</td>'.      '<td>&nbsp;'.$uname.'&nbsp;</td>'.
     '<td>&nbsp;'.$udom.'&nbsp;</td>';      '<td>&nbsp;'.$udom.'&nbsp;</td>';
  if ($receiptparts) {   if ($receiptparts) {
Line 660  sub listStudents { Line 701  sub listStudents {
     $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><font color="#339933">&nbsp;'.$viewgrade.      my $result='<h3><span class="LC_info">&nbsp;'.$viewgrade.
  ' Submissions for a Student or a Group of Students</font></h3>';   ' Submissions for a Student or a Group of Students</span></h3>';
   
     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));      my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
   
Line 701  LISTJAVASCRIPT Line 742  LISTJAVASCRIPT
     &commonJSfunctions($request);      &commonJSfunctions($request);
     $request->print($result);      $request->print($result);
   
     my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : '';      my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked="checked"' : '';
     my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';      my $checklastsub = $checkhdgrade eq '' ? 'checked="checked"' : '';
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.      my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
  "\n".$table.   "\n".$table.
  '&nbsp;<b>View Problem Text: </b><label><input type="radio" name="vProb" value="no" checked="on" /> no </label>'."\n".   '&nbsp;<b>View Problem Text: </b><label><input type="radio" name="vProb" value="no" checked="checked" /> no </label>'."\n".
  '<label><input type="radio" name="vProb" value="yes" /> one student </label>'."\n".   '<label><input type="radio" name="vProb" value="yes" /> one student </label>'."\n".
  '<label><input type="radio" name="vProb" value="all" /> all students </label><br />'."\n".   '<label><input type="radio" name="vProb" value="all" /> all students </label><br />'."\n".
  '&nbsp;<b>View Answer: </b><label><input type="radio" name="vAns" value="no"  /> no </label>'."\n".   '&nbsp;<b>View Answer: </b><label><input type="radio" name="vAns" value="no"  /> no </label>'."\n".
  '<label><input type="radio" name="vAns" value="yes" /> one student </label>'."\n".   '<label><input type="radio" name="vAns" value="yes" /> one student </label>'."\n".
  '<label><input type="radio" name="vAns" value="all" checked="on" /> all students </label><br />'."\n".   '<label><input type="radio" name="vAns" value="all" checked="checked" /> all students </label><br />'."\n".
  '&nbsp;<b>Submissions: </b>'."\n";   '&nbsp;<b>Submissions: </b>'."\n";
     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {      if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
  $gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";   $gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";
Line 718  LISTJAVASCRIPT Line 759  LISTJAVASCRIPT
   
     my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'};      my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'};
     $env{'form.Status'} = $saveStatus;      $env{'form.Status'} = $saveStatus;
   
     $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".      $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".
  '<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".   '<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".
  '<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n".   '<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n".
  '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n".   '<label><input type="radio" name="lastSub" value="all" /> all details</label><br />'."\n".
  '<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".          '&nbsp;<b>Grading Increments:</b> <select name="increment">'.
           '<option value="1">Whole Points</option>'.
           '<option value=".5">Half Points</option>'.
           '<option value=".25">Quarter Points</option>'.
           '<option value=".1">Tenths of a Point</option>'.
           '</select>'.
           &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".
  '<input type="hidden" name="showgrading" value="'.$env{'form.showgrading'}.'" /><br />'."\n".   '<input type="hidden" name="showgrading" value="'.$env{'form.showgrading'}.'" /><br />'."\n".
  '<input type="hidden" name="saveState"   value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState"   value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="probTitle"   value="'.$env{'form.probTitle'}.'" />'."\n".   '<input type="hidden" name="probTitle"   value="'.$env{'form.probTitle'}.'" />'."\n".
  '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
   
     if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {      if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
Line 749  LISTJAVASCRIPT Line 795  LISTJAVASCRIPT
  'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".   'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
  'value="Next->" /> <br />'."\n";   'value="Next->" /> <br />'."\n";
     $gradeTable.=&check_buttons();      $gradeTable.=&check_buttons();
     $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="on" />Check For Plagiarism</label>';      $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="checked" />Check For Plagiarism</label>';
     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1');      my ($classlist, undef, $fullname) = &getclasslist($getsec,'1');
     $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.      $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
  '<table border="0"><tr bgcolor="#e6ffff">';   '<table border="0"><tr bgcolor="#e6ffff">';
Line 866  LISTJAVASCRIPT Line 912  LISTJAVASCRIPT
     if ($ctr == 0) {      if ($ctr == 0) {
  my $num_students=(scalar(keys(%$fullname)));   my $num_students=(scalar(keys(%$fullname)));
  if ($num_students eq 0) {   if ($num_students eq 0) {
     $gradeTable='<br />&nbsp;<font color="red">There are no students currently enrolled.</font>';      $gradeTable='<br />&nbsp;<span class="LC_warning">There are no students currently enrolled.</span>';
  } else {   } else {
     my $submissions='submissions';      my $submissions='submissions';
     if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }      if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }
     if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }      if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }
     if ($submitonly eq 'queued'   ) { $submissions = 'queued submissions'; }      if ($submitonly eq 'queued'   ) { $submissions = 'queued submissions'; }
     $gradeTable='<br />&nbsp;<font color="red">'.      $gradeTable='<br />&nbsp;<span class="LC_warning">'.
  'No '.$submissions.' found for this resource for any students. ('.$num_students.   'No '.$submissions.' found for this resource for any students. ('.$num_students.
  ' students checked for '.$submissions.')</font><br />';   ' students checked for '.$submissions.')</span><br />';
  }   }
     } elsif ($ctr == 1) {      } elsif ($ctr == 1) {
  $gradeTable =~ s/type=checkbox/type=checkbox checked/;   $gradeTable =~ s/type=checkbox/type=checkbox checked/;
Line 938  sub processGroup { Line 984  sub processGroup {
     my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
     my $total      = scalar(@stuchecked)-1;      my $total      = scalar(@stuchecked)-1;
   
     foreach (@stuchecked) {      foreach my $student (@stuchecked) {
  my ($uname,$udom,$fullname) = split(/:/);   my ($uname,$udom,$fullname) = split(/:/,$student);
  $env{'form.student'}        = $uname;   $env{'form.student'}        = $uname;
  $env{'form.userdom'}        = $udom;   $env{'form.userdom'}        = $udom;
  $env{'form.fullname'}       = $fullname;   $env{'form.fullname'}       = $fullname;
Line 1135  sub sub_page_kw_js { Line 1181  sub sub_page_kw_js {
     my $request = shift;      my $request = shift;
     my $iconpath = $request->dir_config('lonIconsURL');      my $iconpath = $request->dir_config('lonIconsURL');
     &commonJSfunctions($request);      &commonJSfunctions($request);
   
       my $inner_js_msg_central=<<INNERJS;
       <script text="text/javascript">
       function checkInput() {
         opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);
         var nmsg   = opener.document.SCORE.savemsgN.value;
         var usrctr = document.msgcenter.usrctr.value;
         var newval = opener.document.SCORE["newmsg"+usrctr];
         newval.value = opener.checkEntities(document.msgcenter.newmsg.value);
   
         var msgchk = "";
         if (document.msgcenter.subchk.checked) {
            msgchk = "msgsub,";
         }
         var includemsg = 0;
         for (var i=1; i<=nmsg; i++) {
             var opnmsg = opener.document.SCORE["savemsg"+i];
             var frmmsg = document.msgcenter["msg"+i];
             opnmsg.value = opener.checkEntities(frmmsg.value);
             var showflg = opener.document.SCORE["shownOnce"+i];
             showflg.value = "1";
             var chkbox = document.msgcenter["msgn"+i];
             if (chkbox.checked) {
                msgchk += "savemsg"+i+",";
                includemsg = 1;
             }
         }
         if (document.msgcenter.newmsgchk.checked) {
            msgchk += "newmsg"+usrctr;
            includemsg = 1;
         }
         imgformname = opener.document.SCORE["mailicon"+usrctr];
         imgformname.src = "$iconpath/"+((includemsg) ? "mailto.gif" : "mailbkgrd.gif");
         var includemsg = opener.document.SCORE["includemsg"+usrctr];
         includemsg.value = msgchk;
   
         self.close()
   
       }
       </script>
   INNERJS
   
       my $inner_js_highlight_central=<<INNERJS;
    <script type="text/javascript">
       function updateChoice(flag) {
         opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
         opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
         opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);
         opener.document.SCORE.refresh.value = "on";
         if (opener.document.SCORE.keywords.value!=""){
            opener.document.SCORE.submit();
         }
         self.close()
       }
   </script>
   INNERJS
   
       my $start_page_msg_central = 
           &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,
          {'js_ready'  => 1,
    'only_body' => 1,
    'bgcolor'   =>'#FFFFFF',});
       my $end_page_msg_central = 
    &Apache::loncommon::end_page({'js_ready' => 1});
   
   
       my $start_page_highlight_central = 
           &Apache::loncommon::start_page('Highlight Central',
          $inner_js_highlight_central,
          {'js_ready'  => 1,
    'only_body' => 1,
    'bgcolor'   =>'#FFFFFF',});
       my $end_page_highlight_central = 
    &Apache::loncommon::end_page({'js_ready' => 1});
   
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();      my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;      $docopen=~s/^document\.//;
     $request->print(<<SUBJAVASCRIPT);      $request->print(<<SUBJAVASCRIPT);
Line 1250  sub sub_page_kw_js { Line 1371  sub sub_page_kw_js {
     pWin.focus();      pWin.focus();
     pDoc = pWin.document;      pDoc = pWin.document;
     pDoc.$docopen;      pDoc.$docopen;
     pDoc.write("<html><head>");      pDoc.write('$start_page_msg_central');
     pDoc.write("<title>Message Central</title>");  
   
     pDoc.write("<script language=javascript>");  
     pDoc.write("function checkInput() {");  
     pDoc.write("  opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);");  
     pDoc.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");  
     pDoc.write("  var usrctr = document.msgcenter.usrctr.value;");  
     pDoc.write("  var newval = opener.document.SCORE[\\"newmsg\\"+usrctr];");  
     pDoc.write("  newval.value = opener.checkEntities(document.msgcenter.newmsg.value);");  
   
     pDoc.write("  var msgchk = \\"\\";");  
     pDoc.write("  if (document.msgcenter.subchk.checked) {");  
     pDoc.write("     msgchk = \\"msgsub,\\";");  
     pDoc.write("  }");  
     pDoc.write("  var includemsg = 0;");  
     pDoc.write("  for (var i=1; i<=nmsg; i++) {");  
     pDoc.write("      var opnmsg = opener.document.SCORE[\\"savemsg\\"+i];");  
     pDoc.write("      var frmmsg = document.msgcenter[\\"msg\\"+i];");  
     pDoc.write("      opnmsg.value = opener.checkEntities(frmmsg.value);");  
     pDoc.write("      var showflg = opener.document.SCORE[\\"shownOnce\\"+i];");  
     pDoc.write("      showflg.value = \\"1\\";");  
     pDoc.write("      var chkbox = document.msgcenter[\\"msgn\\"+i];");  
     pDoc.write("      if (chkbox.checked) {");  
     pDoc.write("         msgchk += \\"savemsg\\"+i+\\",\\";");  
     pDoc.write("         includemsg = 1;");  
     pDoc.write("      }");  
     pDoc.write("  }");  
     pDoc.write("  if (document.msgcenter.newmsgchk.checked) {");  
     pDoc.write("     msgchk += \\"newmsg\\"+usrctr;");  
     pDoc.write("     includemsg = 1;");  
     pDoc.write("  }");  
     pDoc.write("  imgformname = opener.document.SCORE[\\"mailicon\\"+usrctr];");  
     pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");  
     pDoc.write("  var includemsg = opener.document.SCORE[\\"includemsg\\"+usrctr];");  
     pDoc.write("  includemsg.value = msgchk;");  
   
     pDoc.write("  self.close()");  
   
     pDoc.write("}");  
   
     pDoc.write("<");  
     pDoc.write("/script>");  
   
     pDoc.write("</head><body bgcolor=white>");  
   
     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");      pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");      pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
     pDoc.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><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\\">");
Line 1335  sub sub_page_kw_js { Line 1412  sub sub_page_kw_js {
     pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");      pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");      pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
     pDoc.write("</form>");      pDoc.write("</form>");
     pDoc.write("</body></html>");      pDoc.write('$end_page_msg_central');
     pDoc.close();      pDoc.close();
 }  }
   
Line 1381  sub sub_page_kw_js { Line 1458  sub sub_page_kw_js {
     hwdWin.focus();      hwdWin.focus();
     var hDoc = hwdWin.document;      var hDoc = hwdWin.document;
     hDoc.$docopen;      hDoc.$docopen;
     hDoc.write("<html><head>");      hDoc.write('$start_page_highlight_central');
     hDoc.write("<title>Highlight Central</title>");  
   
     hDoc.write("<script language=javascript>");  
     hDoc.write("function updateChoice(flag) {");  
     hDoc.write("  opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);");  
     hDoc.write("  opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);");  
     hDoc.write("  opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);");  
     hDoc.write("  opener.document.SCORE.refresh.value = \\"on\\";");  
     hDoc.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");  
     hDoc.write("     opener.document.SCORE.submit();");  
     hDoc.write("  }");  
     hDoc.write("  self.close()");  
     hDoc.write("}");  
   
     hDoc.write("<");  
     hDoc.write("/script>");  
   
     hDoc.write("</head><body bgcolor=white>");  
   
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");      hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
     hDoc.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><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\\">");
Line 1428  sub sub_page_kw_js { Line 1486  sub sub_page_kw_js {
     hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");      hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
     hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");      hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
     hDoc.write("</form>");      hDoc.write("</form>");
     hDoc.write("</body></html>");      hDoc.write('$end_page_highlight_central');
     hDoc.close();      hDoc.close();
   }    }
   
Line 1436  sub sub_page_kw_js { Line 1494  sub sub_page_kw_js {
 SUBJAVASCRIPT  SUBJAVASCRIPT
 }  }
   
   sub get_increment {
       my $increment = $env{'form.increment'};
       if ($increment != 1 && $increment != .5 && $increment != .25 &&
           $increment != .1) {
           $increment = 1;
       }
       return $increment;
   }
   
 #--- displays the grading box, used in essay type problem and grading by page/sequence  #--- displays the grading box, used in essay type problem and grading by page/sequence
 sub gradeBox {  sub gradeBox {
     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;      my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').      my $checkIcon = '<img alt="'.&mt('Check Mark').
    '" src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
     my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);      my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
     my $wgtmsg = ($wgt > 0 ? '(problem weight)' :       my $wgtmsg = ($wgt > 0 ? '(problem weight)' : 
   '<font color="red">problem weight assigned by computer</font>');    '<span class="LC_info">problem weight assigned by computer</span>');
     $wgt       = ($wgt > 0 ? $wgt : '1');      $wgt       = ($wgt > 0 ? $wgt : '1');
     my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?      my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
   '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));    '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));
Line 1458  sub gradeBox { Line 1526  sub gradeBox {
     $result.='<table border="0"><tr><td>'.      $result.='<table border="0"><tr><td>'.
  '<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n";   '<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n";
     my $ctr = 0;      my $ctr = 0;
       my $thisweight = 0;
       my $increment = &get_increment();
     $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across      $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
     while ($ctr<=$wgt) {      while ($thisweight<=$wgt) {
  $result.= '<td><nobr><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.   $result.= '<td><span style="white-space: nowrap;"><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
     'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.      'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
     $ctr.')" value="'.$ctr.'" '.      $thisweight.')" value="'.$thisweight.'" '.
     ($score eq $ctr ? 'checked':'').' /> '.$ctr."</label></nobr></td>\n";      ($score eq $thisweight ? 'checked="checked"':'').' /> '.$thisweight."</label></span></td>\n";
  $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');   $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
           $thisweight += $increment;
  $ctr++;   $ctr++;
     }      }
     $result.='</tr></table>';      $result.='</tr></table>';
Line 1479  sub gradeBox { Line 1550  sub gradeBox {
     $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.      $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
  'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";   'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {      if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
  $result.='<option> </option>'.   $result.='<option></option>'.
     '<option selected="on">excused</option>';      '<option selected="selected">excused</option>';
     } else {      } else {
  $result.='<option selected="on"> </option>'.   $result.='<option selected="selected"></option>'.
     '<option>excused</option>';      '<option>excused</option>';
     }      }
     $result.='<option>reset status</option></select>'."\n";      $result.='<option>reset status</option></select>'."\n";
     $result.="&nbsp&nbsp\n";      $result.="&nbsp;&nbsp;\n";
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".      $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
  '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".   '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
  '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.   '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
Line 1504  sub handback_box { Line 1575  sub handback_box {
     my ($symb,$uname,$udom,$counter,$partid,$record) = @_;      my ($symb,$uname,$udom,$counter,$partid,$record) = @_;
     my ($partlist,$handgrade,$responseType) = &response_type($symb);      my ($partlist,$handgrade,$responseType) = &response_type($symb);
     my (@respids);      my (@respids);
     foreach my $part_resp (sort(keys(%$handgrade))) {       my @part_response_id = &flatten_responseType($responseType);
         my ($part,$resp) = split(/_/,$part_resp);      foreach my $part_response_id (@part_response_id) {
       my ($part,$resp) = @{ $part_response_id };
         if ($part eq $partid) {          if ($part eq $partid) {
             push @respids,$resp;              push(@respids,$resp);
         }          }
     }      }
     my $result;      my $result;
Line 1517  sub handback_box { Line 1589  sub handback_box {
  next if (!@$files);   next if (!@$files);
  my $file_counter = 1;   my $file_counter = 1;
  foreach my $file (@$files) {   foreach my $file (@$files) {
     my ($file_disp) = ($file =~ m|.+/(.+)$|);      if ($file =~ /\/portfolio\//) {
     $result.=&mt('Return commented version of [_1] to student.',             my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
  '<span class="filename">'.$file_disp.'</span>');             my ($name,$version,$ext) = &file_name_version_ext($file_disp);
     $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";             $file_disp = "$name.$ext";
     $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';             $file = $file_path.$file_disp;
     $file_counter++;             $result.=&mt('Return commented version of [_1] to student.',
       '<span class="LC_filename">'.$file_disp.'</span>');
              $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";
              $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';
              $result.='(File will be uploaded when you click on Save & Next below.)<br />';
              $file_counter++;
       }
  }   }
     }      }
     return $result;          return $result;    
 }  }
   
 sub show_problem {  sub show_problem {
     my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode) = @_;      my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode,$form) = @_;
     my $rendered;      my $rendered;
       my %form = ((ref($form) eq 'HASH')? %{$form} : ());
     &Apache::lonxml::remember_problem_counter();      &Apache::lonxml::remember_problem_counter();
     if ($mode eq 'both' or $mode eq 'text') {      if ($mode eq 'both' or $mode eq 'text') {
  $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,   $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
      $env{'request.course.id'});         $env{'request.course.id'},
          undef,\%form);
     }      }
     if ($removeform) {      if ($removeform) {
  $rendered=~s|<form(.*?)>||g;   $rendered=~s|<form(.*?)>||g;
  $rendered=~s|</form>||g;   $rendered=~s|</form>||g;
  $rendered=~s|name="submit"|name="would_have_been_submit"|g;   $rendered=~s|(<input[^>]*name\s*=\s*"?)(\w+)("?)|$1would_have_been_$2$3|g;
     }      }
     my $companswer;      my $companswer;
     if ($mode eq 'both' or $mode eq 'answer') {      if ($mode eq 'both' or $mode eq 'answer') {
  &Apache::lonxml::restore_problem_counter();   &Apache::lonxml::restore_problem_counter();
  $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,   $companswer=
     $env{'request.course.id'});      &Apache::loncommon::get_student_answers($symb,$uname,$udom,
       $env{'request.course.id'},
       %form);
     }      }
     if ($removeform) {      if ($removeform) {
  $companswer=~s|<form(.*?)>||g;   $companswer=~s|<form(.*?)>||g;
Line 1576  sub show_problem { Line 1658  sub show_problem {
     return $result;      return $result;
 }  }
   
   sub files_exist {
       my ($r, $symb) = @_;
       my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');
   
       foreach my $student (@students) {
           my ($uname,$udom,$fullname) = split(/:/,$student);
           my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
         $udom,$uname);
           my ($string,$timestamp)= &get_last_submission(\%record);
           foreach my $submission (@$string) {
               my ($partid,$respid) =
    ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
               my $files=&get_submitted_files($udom,$uname,$partid,$respid,
      \%record);
               return 1 if (@$files);
           }
       }
       return 0;
   }
   
   sub download_all_link {
       my ($r,$symb) = @_;
       my $all_students = 
    join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));
   
       my $parts =
    join("\n",&Apache::loncommon::get_env_multiple('form.vPart'));
   
       my $identifier = &Apache::loncommon::get_cgi_id();
       &Apache::lonnet::appenv('cgi.'.$identifier.'.students' => $all_students,
                               'cgi.'.$identifier.'.symb' => $symb,
                               'cgi.'.$identifier.'.parts' => $parts,);
       $r->print('<a href="/cgi-bin/multidownload.pl?'.$identifier.'">'.
         &mt('Download All Submitted Documents').'</a>');
       return
   }
   
   sub build_section_inputs {
       my $section_inputs;
       if ($env{'form.section'} eq '') {
           $section_inputs .= '<input type="hidden" name="section" value="all" />'."\n";
       } else {
           my @sections = &Apache::loncommon::get_env_multiple('form.section');
           foreach my $section (@sections) {
               $section_inputs .= '<input type="hidden" name="section" value="'.$section.'" />'."\n";
           }
       }
       return $section_inputs;
   }
   
 # --------------------------- show submissions of a student, option to grade   # --------------------------- show submissions of a student, option to grade 
 sub submission {  sub submission {
     my ($request,$counter,$total) = @_;      my ($request,$counter,$total) = @_;
Line 1589  sub submission { Line 1721  sub submission {
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print('<font color="red">Unable to view requested student.('.   $request->print('<span class="LC_warning">Unable to view requested student.('.
  $uname.'@'.$udom.' in section '.$usec.' in course id '.   $uname.':'.$udom.' in section '.$usec.' in course id '.
  $env{'request.course.id'}.')</font>');   $env{'request.course.id'}.')</span>');
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;   return;
     }      }
Line 1600  sub submission { Line 1732  sub submission {
     if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }      if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
     if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }      if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
     my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');      my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').      my $checkIcon = '<img alt="'.&mt('Check Mark').
    '" src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
   
       my %old_essays;
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
  &sub_page_js($request);   &sub_page_js($request);
  &sub_page_kw_js($request) if ($env{'form.handgrade'} eq 'yes');   &sub_page_kw_js($request) if ($env{'form.handgrade'} eq 'yes');
  $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'};
    if ($env{'form.handgrade'} eq 'yes' && &files_exist($request, $symb)) {
  $request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".      &download_all_link($request, $symb);
  '<font size=+1>&nbsp;<b>Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n");   }
    $request->print('<h3>&nbsp;<span class="LC_info">Submission Record</span></h3>'."\n".
    '<h4>&nbsp;<b>Resource: </b>'.$env{'form.probTitle'}.'</h4>'."\n");
   
  if ($env{'form.handgrade'} eq 'no') {   if ($env{'form.handgrade'} eq 'no') {
     my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.      my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.
Line 1652  sub submission { Line 1788  sub submission {
     $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';      $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
  }   }
  my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};   my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};
   
  $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".   $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".
  '<input type="hidden" name="command"    value="handgrade" />'."\n".   '<input type="hidden" name="command"    value="handgrade" />'."\n".
  '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".
Line 1662  sub submission { Line 1797  sub submission {
  '<input type="hidden" name="refresh"    value="off" />'."\n".   '<input type="hidden" name="refresh"    value="off" />'."\n".
  '<input type="hidden" name="studentNo"  value="" />'."\n".   '<input type="hidden" name="studentNo"  value="" />'."\n".
  '<input type="hidden" name="gradeOpt"   value="" />'."\n".   '<input type="hidden" name="gradeOpt"   value="" />'."\n".
  '<input type="hidden" name="symb"       value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"       value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="showgrading" value="'.$env{'form.showgrading'}.'" />'."\n".   '<input type="hidden" name="showgrading" value="'.$env{'form.showgrading'}.'" />'."\n".
  '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".   '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".
  '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".   '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".
  '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".   '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".
  '<input type="hidden" name="section"    value="'.$env{'form.section'}.'" />'."\n".   &build_section_inputs().
  '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".   '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".
  '<input type="hidden" name="handgrade"  value="'.$env{'form.handgrade'}.'" />'."\n".   '<input type="hidden" name="handgrade"  value="'.$env{'form.handgrade'}.'" />'."\n".
  '<input type="hidden" name="NCT"'.   '<input type="hidden" name="NCT"'.
Line 1703  sub submission { Line 1838  sub submission {
 #  #
     $request->print(<<KEYWORDS);      $request->print(<<KEYWORDS);
 &nbsp;<b>Keyword Options:</b>&nbsp;  &nbsp;<b>Keyword Options:</b>&nbsp;
 <a href="javascript:keywords(document.SCORE)"; TARGET=_self>List</a>&nbsp; &nbsp;  <a href="javascript:keywords(document.SCORE);" target="_self">List</a>&nbsp; &nbsp;
 <a href="#" onMouseDown="javascript:getSel(); return false"  <a href="#" onMouseDown="javascript:getSel(); return false"
  CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;   CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;
 <a href="javascript:kwhighlight()"; TARGET=_self>Highlight Attribute</a><br /><br />  <a href="javascript:kwhighlight();" target="_self">Highlight Attribute</a><br /><br />
 KEYWORDS  KEYWORDS
 #  #
 # Load the other essays for similarity check  # Load the other essays for similarity check
 #  #
             my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);              my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
     my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/);      my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
     $apath=&Apache::lonnet::escape($apath);      $apath=&escape($apath);
     $apath=~s/\W/\_/gs;      $apath=~s/\W/\_/gs;
     %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);      %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
         }          }
     }      }
   
Line 1818  KEYWORDS Line 1953  KEYWORDS
     $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0];       $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; 
  } else {   } else {
     my %seenparts;      my %seenparts;
     for my $part (sort keys(%$handgrade)) {      my @part_response_id = &flatten_responseType($responseType);
  my ($partid,$respid) = split(/_/,$part);      foreach my $part (@part_response_id) {
    next if ($env{'form.lastSub'} eq 'hdgrade' 
    && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');
   
    my ($partid,$respid) = @{ $part };
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  if ($env{"form.$uname:$udom:$partid:submitted_by"}) {   if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
     if (exists($seenparts{$partid})) { next; }      if (exists($seenparts{$partid})) { next; }
Line 1828  KEYWORDS Line 1967  KEYWORDS
  ' <b>Collaborative submission by:</b> '.   ' <b>Collaborative submission by:</b> '.
  '<a href="javascript:viewSubmitter(\''.   '<a href="javascript:viewSubmitter(\''.
  $env{"form.$uname:$udom:$partid:submitted_by"}.   $env{"form.$uname:$udom:$partid:submitted_by"}.
  '\')"; TARGET=_self>'.   '\');" target="_self">'.
  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';   $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';
     $request->print($submitby);      $request->print($submitby);
     next;      next;
Line 1836  KEYWORDS Line 1975  KEYWORDS
  my $responsetype = $responseType->{$partid}->{$respid};   my $responsetype = $responseType->{$partid}->{$respid};
  if (!exists($record{"resource.$partid.$respid.submission"})) {   if (!exists($record{"resource.$partid.$respid.submission"})) {
     $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.      $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
  $display_part.' <font color="#999999">( ID '.$respid.   $display_part.' <span class="LC_internal_info">( ID '.$respid.
  ' )</font>&nbsp; &nbsp;'.   ' )</span>&nbsp; &nbsp;'.
  '<font color="red">Nothing submitted - no attempts</font><br /><br />';   '<span class="LC_warning">Nothing submitted - no attempts</span><br /><br />';
     next;      next;
  }   }
  foreach (@$string) {   foreach (@$string) {
     my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;      my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;
     if ($part ne ($partid.'_'.$respid)) { next; }      if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
     my ($ressub,$subval) = split(/:/,$_,2);      my ($ressub,$subval) = split(/:/,$_,2);
     # Similarity check      # Similarity check
     my $similar='';      my $similar='';
     if($env{'form.checkPlag'}){      if($env{'form.checkPlag'}){
  my ($oname,$odom,$ocrsid,$oessay,$osim)=   my ($oname,$odom,$ocrsid,$oessay,$osim)=
     &most_similar($uname,$udom,$subval);      &most_similar($uname,$udom,$subval,\%old_essays);
  if ($osim) {   if ($osim) {
     $osim=int($osim*100.0);      $osim=int($osim*100.0);
     $similar="<hr /><h3><font color=\"#FF0000\">Essay".      my %old_course_desc = 
  " is $osim% similar to an essay by ".   &Apache::lonnet::coursedescription($ocrsid,
  &Apache::loncommon::plainname($oname,$odom).     {'one_time' => 1});
  '</font></h3><blockquote><i>'.  
       $similar="<hr /><h3><span class=\"LC_warning\">".
    &mt('Essay is [_1]% similar to an essay by [_2] ([_3]:[_4]) in course [_5] (course id [_6]:[_7])',
       $osim,
       &Apache::loncommon::plainname($oname,$odom),
       $oname,$odom,
       $old_course_desc{'description'},
       $old_course_desc{'num'},
       $old_course_desc{'domain'}).
    '</span></h3><blockquote><i>'.
  &keywords_highlight($oessay).   &keywords_highlight($oessay).
  '</i></blockquote><hr />';   '</i></blockquote><hr />';
  }   }
Line 1863  KEYWORDS Line 2011  KEYWORDS
     my $order=&get_order($partid,$respid,$symb,$uname,$udom);      my $order=&get_order($partid,$respid,$symb,$uname,$udom);
     if ($env{'form.lastSub'} eq 'lastonly' ||       if ($env{'form.lastSub'} eq 'lastonly' || 
  ($env{'form.lastSub'} eq 'hdgrade' &&    ($env{'form.lastSub'} eq 'hdgrade' && 
  $$handgrade{$part} eq 'yes')) {   $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
  $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.   $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
     $display_part.' <font color="#999999">( ID '.$respid.      $display_part.' <span class="LC_internal_info">( ID '.$respid.
     ' )</font>&nbsp; &nbsp;';      ' )</span>&nbsp; &nbsp;';
  my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);   my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
  if (@$files) {   if (@$files) {
     $lastsubonly.='<br /><font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />';      $lastsubonly.='<br /><span class="LC_warning">Like all files provided by users, this file may contain virusses</span><br />';
     my $file_counter = 0;      my $file_counter = 0;
     foreach my $file (@$files) {      foreach my $file (@$files) {
         $file_counter ++;          $file_counter ++;
Line 1906  KEYWORDS Line 2054  KEYWORDS
     if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {      if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {
  my $toGrade.='<input type="button" value="Grade Student" '.   my $toGrade.='<input type="button" value="Grade Student" '.
     'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''      'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''
     .$counter.'\');" TARGET=_self> &nbsp;'."\n" if (&canmodify($usec));      .$counter.'\');" target="_self" /> &nbsp;'."\n" if (&canmodify($usec));
  $toGrade.='</td></tr></table></td></tr></table>'."\n";   $toGrade.='</td></tr></table></td></tr></table>'."\n";
  if (($env{'form.command'} eq 'submission') ||    if (($env{'form.command'} eq 'submission') || 
     ($env{'form.command'} eq 'processGroup' && $counter == $total)) {      ($env{'form.command'} eq 'processGroup' && $counter == $total)) {
Line 1930  KEYWORDS Line 2078  KEYWORDS
  $result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".   $result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
     '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";      '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
  $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.   $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
     ',\''.$msgfor.'\')"; TARGET=_self>'.      ',\''.$msgfor.'\');" target="_self">'.
     &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> ('.      &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a><label> ('.
     &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" />)'.      &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
     '<img src="'.$request->dir_config('lonIconsURL').      '<img src="'.$request->dir_config('lonIconsURL').
     '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".      '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".
     '<br />&nbsp;('.      '<br />&nbsp;('.
Line 1953  KEYWORDS Line 2101  KEYWORDS
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     for my $part_resp (sort(keys(%$handgrade))) {      my @part_response_id = &flatten_responseType($responseType);
  my ($partid,$respid) = split(/_/, $part_resp);      foreach my $part_response_id (@part_response_id) {
       my ($partid,$respid) = @{ $part_response_id };
    my $part_resp = join('_',@{ $part_response_id });
  next if ($seen{$partid} > 0);   next if ($seen{$partid} > 0);
  $seen{$partid}++;   $seen{$partid}++;
  next if ($$handgrade{$part_resp} =~ /:no$/ && $env{'form.lastSub'} =~ /^(hdgrade)$/);   next if ($$handgrade{$part_resp} ne 'yes' 
    && $env{'form.lastSub'} eq 'hdgrade');
  push @partlist,$partid;   push @partlist,$partid;
  push @gradePartRespid,$partid.'.'.$respid;   push @gradePartRespid,$partid.'.'.$respid;
  $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));   $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
Line 1979  KEYWORDS Line 2130  KEYWORDS
  my $endform='<table border="0"><tr><td>'."\n";   my $endform='<table border="0"><tr><td>'."\n";
  $endform.='<input type="button" value="Save & Next" '.   $endform.='<input type="button" value="Save & Next" '.
     'onClick="javascript:checksubmit(this.form,\'Save & Next\','.      'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
     $total.','.scalar(@partlist).');" TARGET=_self> &nbsp;'."\n";      $total.','.scalar(@partlist).');" target="_self" /> &nbsp;'."\n";
  my $ntstu ='<select name="NTSTU">'.   my $ntstu ='<select name="NTSTU">'.
     '<option>1</option><option>2</option>'.      '<option>1</option><option>2</option>'.
     '<option>3</option><option>5</option>'.      '<option>3</option><option>5</option>'.
     '<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="on">$nsel</;   $ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
  $endform.=$ntstu.'student(s) &nbsp;&nbsp;';   $endform.=$ntstu.'student(s) &nbsp;&nbsp;';
  $endform.='<input type="button" value="Previous" '.   $endform.='<input type="button" value="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="Next" '.      '<input type="button" value="Next" '.
     'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;';      'onClick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';
  $endform.='(Next and Previous (student) do not save the scores.)'."\n" ;   $endform.='(Next and Previous (student) do not save the scores.)'."\n" ;
           $endform.="<input type='hidden' value='".&get_increment().
               "' name='increment' />";
  $endform.='</td><tr></table></form>';   $endform.='</td><tr></table></form>';
  $endform.=&show_grading_menu_form($symb);   $endform.=&show_grading_menu_form($symb);
  $request->print($endform);   $request->print($endform);
Line 2007  sub get_last_submission { Line 2160  sub get_last_submission {
  my %lasthash=();   my %lasthash=();
  my ($version);   my ($version);
  for ($version=1;$version<=$$returnhash{'version'};$version++) {   for ($version=1;$version<=$$returnhash{'version'};$version++) {
     foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) {      foreach my $key (sort(split(/\:/,
  $lasthash{$_}=$$returnhash{$version.':'.$_};   $$returnhash{$version.':keys'}))) {
    $timestamp = scalar(localtime($$returnhash{$version.':timestamp'}));   $lasthash{$key}=$$returnhash{$version.':'.$key};
    $timestamp = 
       scalar(localtime($$returnhash{$version.':timestamp'}));
     }      }
  }   }
  foreach ((keys %lasthash)) {   foreach my $key (keys(%lasthash)) {
     if ($_ =~ /\.submission$/) {      next if ($key !~ /\.submission$/);
  my ($partid,$foo) = split(/submission$/,$_);  
  my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?      my ($partid,$foo) = split(/submission$/,$key);
     '<font color="red">Draft Copy</font> ' : '';      my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
  push @string, (join(':',$_,$draft.$lasthash{$_}));   '<span class="LC_warning">Draft Copy</span> ' : '';
     }      push(@string, join(':', $key, $draft.$lasthash{$key}));
  }   }
     }      }
     @string = $string[0] eq '' ? '<font color="red">Nothing submitted - no attempts.</font>' : @string;      if (!@string) {
     return \@string,\$timestamp;   $string[0] =
       '<span class="LC_warning">Nothing submitted - no attempts.</span>';
       }
       return (\@string,\$timestamp);
 }  }
   
 #--- High light keywords, with style choosen by user.  #--- High light keywords, with style choosen by user.
Line 2032  sub keywords_highlight { Line 2190  sub keywords_highlight {
     my $styleon   = $env{'form.kwstyle'} eq ''  ? '' : $env{'form.kwstyle'};      my $styleon   = $env{'form.kwstyle'} eq ''  ? '' : $env{'form.kwstyle'};
     (my $styleoff = $styleon) =~ s/\</\<\//;      (my $styleoff = $styleon) =~ s/\</\<\//;
     my @keylist   = split(/[,\s+]/,$env{'form.keywords'});      my @keylist   = split(/[,\s+]/,$env{'form.keywords'});
     foreach (@keylist) {      foreach my $keyword (@keylist) {
  $string =~ s/\b\Q$_\E(\b|\.)/<font color\=$env{'form.kwclr'} $size\>$styleon$_$styleoff<\/font>/gi;   $string =~ s/\b\Q$keyword\E(\b|\.)/<font color\=$env{'form.kwclr'} $size\>$styleon$keyword$styleoff<\/font>/gi;
     }      }
     return $string;      return $string;
 }  }
Line 2059  sub processHandGrade { Line 2217  sub processHandGrade {
  next;   next;
     }      }
     if ($errorflag eq 'not_allowed') {      if ($errorflag eq 'not_allowed') {
  $request->print("<font color=\"red\">Not allowed to modify grades for $uname:$udom</font>");   $request->print("<span class=\"LC_warning\">Not allowed to modify grades for $uname:$udom</span>");
  $ctr++;   $ctr++;
  next;   next;
     }      }
     my $includemsg = $env{'form.includemsg'.$ctr};      my $includemsg = $env{'form.includemsg'.$ctr};
     my ($subject,$message,$msgstatus) = ('','','');      my ($subject,$message,$msgstatus) = ('','','');
       my $restitle = &Apache::lonnet::gettitle($symb);
               my ($feedurl,$showsymb) =
    &get_feedurl_and_symb($symb,$uname,$udom);
       my $messagetail;
     if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {      if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
  $subject = $env{'form.msgsub'} if ($includemsg =~ /msgsub/);   $subject = $env{'form.msgsub'} if ($includemsg =~ /msgsub/);
  unless ($subject=~/\w/) { $subject=&mt('Grading Feedback'); }   unless ($subject=~/\w/) { $subject=&mt('Grading Feedback'); }
    $subject.=' ['.$restitle.']';
  my (@msgnum) = split(/,/,$includemsg);   my (@msgnum) = split(/,/,$includemsg);
  foreach (@msgnum) {   foreach (@msgnum) {
     $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');      $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
Line 2075  sub processHandGrade { Line 2238  sub processHandGrade {
  $message =&Apache::lonfeedback::clear_out_html($message);   $message =&Apache::lonfeedback::clear_out_html($message);
  if ($env{'form.withgrades'.$ctr}) {   if ($env{'form.withgrades'.$ctr}) {
     $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;      $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
     $message.=" for <a href=\"".      $messagetail = " for <a href=\"".
     &Apache::lonnet::clutter($url).                     $feedurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";
     "?symb=$symb\">$env{'form.probTitle'}</a>";   }
  }   $msgstatus = 
  $msgstatus = &Apache::lonmsg::user_normal_msg($uname,$udom,                      &Apache::lonmsg::user_normal_msg($uname,$udom,$subject,
       $subject.' ['.       $message.$messagetail,
       &Apache::lonnet::declutter($url).']',$message);                                                       undef,$feedurl,undef,
  $request->print('<br />'.&mt('Sending message to [_1]@[_2]',$uname,$udom).': '.                                                       undef,undef,$showsymb,
                                                        $restitle);
    $request->print('<br />'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '.
  $msgstatus);   $msgstatus);
     }      }
     if ($env{'form.collaborator'.$ctr}) {      if ($env{'form.collaborator'.$ctr}) {
Line 2094  sub processHandGrade { Line 2259  sub processHandGrade {
     &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,      &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
    $env{'form.unamedom'.$ctr},$part);     $env{'form.unamedom'.$ctr},$part);
  if ($errorflag eq 'not_allowed') {   if ($errorflag eq 'not_allowed') {
     $request->print("<font color=\"red\">Not allowed to modify grades for $collaborator:$udom</font>");      $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");
     next;      next;
  } else {   } elsif ($message ne '') {
     if ($message ne '') {      my ($baseurl,$showsymb) = 
  $msgstatus = &Apache::lonmsg::user_normal_msg($collaborator,$udom,$env{'form.msgsub'},$message);   &get_feedurl_and_symb($symb,$collaborator,
         $udom);
       if ($env{'form.withgrades'.$ctr}) {
    $messagetail = " for <a href=\"".
                                       $baseurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";
     }      }
       $msgstatus = 
    &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
  }   }
     }      }
  }   }
Line 2257  sub processHandGrade { Line 2428  sub processHandGrade {
  $ctr++;   $ctr++;
     }      }
     if ($total < 0) {      if ($total < 0) {
  my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";   my $the_end = '<h3><span class="LC_info">LON-CAPA User Message</span></h3><br />'."\n";
  $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";   $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";
  $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";   $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";
  $the_end.=&show_grading_menu_form($symb);   $the_end.=&show_grading_menu_form($symb);
Line 2269  sub processHandGrade { Line 2440  sub processHandGrade {
 #---- Save the score and award for each student, if changed  #---- Save the score and award for each student, if changed
 sub saveHandGrade {  sub saveHandGrade {
     my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;      my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
     my @v_flag;      my @version_parts;
     my $usec = &Apache::lonnet::getsection($domain,$stuname,      my $usec = &Apache::lonnet::getsection($domain,$stuname,
    $env{'request.course.id'});     $env{'request.course.id'});
     if (!&canmodify($usec)) { return('not_allowed'); }      if (!&canmodify($usec)) { return('not_allowed'); }
     my %record     = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
     my @parts_graded;      my @parts_graded;
     my %newrecord  = ();      my %newrecord  = ();
     my ($pts,$wgt) = ('','');      my ($pts,$wgt) = ('','');
     my %aggregate = ();      my %aggregate = ();
     my $aggregateflag = 0;      my $aggregateflag = 0;
   
     my @parts = split(/:/,$env{'form.partlist'.$newflg});      my @parts = split(/:/,$env{'form.partlist'.$newflg});
     foreach my $new_part (@parts) {      foreach my $new_part (@parts) {
  #collaborator may vary for different parts   #collaborator ($submi may vary for different parts
  if ($submitter && $new_part ne $part) { next; }   if ($submitter && $new_part ne $part) { next; }
  my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};   my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
  if ($dropMenu eq 'excused') {   if ($dropMenu eq 'excused') {
Line 2291  sub saveHandGrade { Line 2461  sub saveHandGrade {
  if (exists($record{'resource.'.$new_part.'.awarded'})) {   if (exists($record{'resource.'.$new_part.'.awarded'})) {
     $newrecord{'resource.'.$new_part.'.awarded'} = '';      $newrecord{'resource.'.$new_part.'.awarded'} = '';
  }   }
     $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";          $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
     }      }
  } elsif ($dropMenu eq 'reset status'   } elsif ($dropMenu eq 'reset status'
  && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts   && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
Line 2327  sub saveHandGrade { Line 2497  sub saveHandGrade {
     my $partial= $pts/$wgt;      my $partial= $pts/$wgt;
     if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {      if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
  #do not update score for part if not changed.   #do not update score for part if not changed.
                   &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
  next;   next;
     } else {      } else {
         push @parts_graded, $new_part;          push @parts_graded, $new_part;
Line 2351  sub saveHandGrade { Line 2522  sub saveHandGrade {
     $newrecord{'resource.'.$new_part.'.regrader'}=      $newrecord{'resource.'.$new_part.'.regrader'}=
  "$env{'user.name'}:$env{'user.domain'}";   "$env{'user.name'}:$env{'user.domain'}";
  }   }
  my ($partlist,$handgrade,$responseType) = &response_type($symb);  
  foreach my $part_resp (sort(keys(%$handgrade))) {  
     my ($part_id, $resp_id) = split(/_/,$part_resp);  
             if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) {  
                 # if multiple files are uploaded names will be 'returndoc2','returndoc3'  
                 my $file_counter = 1;  
                 while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {  
                     my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'};  
                     $newrecord{"resource.$new_part.$resp_id.handback"} = $env{'form.returndocorig'.$file_counter};  
                     # set the filename to match the submitted file name  
                     $env{'form.'.$newflg.'_'.$part_resp.'_returndoc1.filename'} = $env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter};  
                     my $result=&Apache::lonnet::userfileupload($newflg.'_'.$part_resp.'_returndoc'.$file_counter,'',  
          'portfolio',undef,undef,undef,$stuname,$domain);  
                    if ($result !~ m|^/uploaded/|) {  
                         $request->print('<font color="red"> An errror occured ('.$result.  
                     ') while trying to upload '.&display_file().'</font><br />');  
                 # $request->print(&done('Back'));  
             }  
                     $request->print("<br />".$fname." will be the uploaded file name");  
                     $request->print("<font color=\"red\">Will upload document</font>".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter});  
                     $file_counter++;  
                 }  
             }  
         }  
   
  # unless problem has been graded, set flag to version the submitted files   # unless problem has been graded, set flag to version the submitted files
  unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/  ||    unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/  || 
         $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' ||          $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' ||
         $dropMenu eq 'reset status')          $dropMenu eq 'reset status')
    {     {
     push (@v_flag,$new_part);      push (@version_parts,$new_part);
  }   }
     }      }
     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
   
     if (scalar(keys(%newrecord)) > 0) {      if (%newrecord) {
         if (scalar(@v_flag)) {          if (@version_parts) {
             &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@v_flag);              my @changed_keys = &version_portfiles(\%record, \@parts_graded, 
                                   $env{'request.course.id'}, $symb, $domain, $stuname, \@version_parts);
       @newrecord{@changed_keys} = @record{@changed_keys};
       foreach my $new_part (@version_parts) {
    &handback_files($request,$symb,$stuname,$domain,$newflg,
    $new_part,\%newrecord);
       }
         }          }
  &Apache::lonnet::cstore(\%newrecord,$symb,   &Apache::lonnet::cstore(\%newrecord,$symb,
  $env{'request.course.id'},$domain,$stuname);   $env{'request.course.id'},$domain,$stuname);
    &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb,
  my @ungraded_parts;       $cdom,$cnum,$domain,$stuname);
  foreach my $part (@parts) {  
     if ( !defined($record{'resource.'.$part.'.awarded'})  
  && !defined($newrecord{'resource.'.$part.'.awarded'}) ) {  
  push(@ungraded_parts, $part);  
     }  
  }  
  if ( !@ungraded_parts ) {  
     &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,  
    $cnum,$domain,$stuname);  
  }  
     }      }
     if ($aggregateflag) {      if ($aggregateflag) {
         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,          &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
Line 2413  sub saveHandGrade { Line 2555  sub saveHandGrade {
     return ('',$pts,$wgt);      return ('',$pts,$wgt);
 }  }
   
   sub check_and_remove_from_queue {
       my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_;
       my @ungraded_parts;
       foreach my $part (@{$parts}) {
    if (    $record->{   'resource.'.$part.'.awarded'} eq ''
        && $record->{   'resource.'.$part.'.solved' } ne 'excused'
        && $newrecord->{'resource.'.$part.'.awarded'} eq ''
        && $newrecord->{'resource.'.$part.'.solved' } ne 'excused'
    ) {
       push(@ungraded_parts, $part);
    }
       }
       if ( !@ungraded_parts ) {
    &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,
          $cnum,$domain,$stuname);
       }
   }
   
   sub handback_files {
       my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;
       my $portfolio_root = &propath($domain,$stuname).'/userfiles/portfolio';
       my ($partlist,$handgrade,$responseType) = &response_type($symb);
   
       my @part_response_id = &flatten_responseType($responseType);
       foreach my $part_response_id (@part_response_id) {
       my ($part_id,$resp_id) = @{ $part_response_id };
    my $part_resp = join('_',@{ $part_response_id });
               if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) {
                   # if multiple files are uploaded names will be 'returndoc2','returndoc3'
                   my $file_counter = 1;
    my $file_msg;
                   while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {
                       my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'};
                       my ($directory,$answer_file) = 
                           ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/);
                       my ($answer_name,$answer_ver,$answer_ext) =
           &file_name_version_ext($answer_file);
       my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
       my @dir_list = &Apache::lonnet::dirlist($portfolio_path,$domain,$stuname,$portfolio_root);
       my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
                       # fix file name
                       my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                       my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
                                              $newflg.'_'.$part_resp.'_returndoc'.$file_counter,
                                              $save_file_name);
                       if ($result !~ m|^/uploaded/|) {
                           $request->print('<span class="LC_error">An error occurred ('.$result.
                           ') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'</span><br />');
                       } else {
                           # mark the file as read only
                           my @files = ($save_file_name);
                           my @what = ($symb,$env{'request.course.id'},'handback');
                           &Apache::lonnet::mark_as_readonly($domain,$stuname,\@files,\@what);
    if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) {
       $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';
    }
                           $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;
    $file_msg.= "\n".'<br /><span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span><br />";
   
                       }
                       $request->print("<br />".$fname." will be the uploaded file name");
                       $request->print(" ".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter});
                       $file_counter++;
                   }
    my $subject = "File Handed Back by Instructor ";
    my $message = "A file has been returned that was originally submitted in reponse to: <br />";
    $message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />";
    $message .= ' The returned file(s) are named: '. $file_msg;
    $message .= " and can be found in your portfolio space.";
    my ($feedurl,$showsymb) = 
       &get_feedurl_and_symb($symb,$domain,$stuname);
                   my $restitle = &Apache::lonnet::gettitle($symb);
    my $msgstatus = 
                      &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject.
    ' (File Returned) ['.$restitle.']',$message,undef,
                            $feedurl,undef,undef,undef,$showsymb,$restitle);
               }
           }
       return;
   }
   
   sub get_feedurl_and_symb {
       my ($symb,$uname,$udom) = @_;
       my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
       $url = &Apache::lonnet::clutter($url);
       my $encrypturl=&Apache::lonnet::EXT('resource.0.encrypturl',
    $symb,$udom,$uname);
       if ($encrypturl =~ /^yes$/i) {
    &Apache::lonenc::encrypted(\$url,1);
    &Apache::lonenc::encrypted(\$symb,1);
       }
       return ($url,$symb);
   }
   
 sub get_submitted_files {  sub get_submitted_files {
     my ($udom,$uname,$partid,$respid,$record) = @_;      my ($udom,$uname,$partid,$respid,$record) = @_;
     my @files;      my @files;
Line 2491  sub get_last_resets { Line 2727  sub get_last_resets {
 sub version_portfiles {  sub version_portfiles {
     my ($record, $parts_graded, $courseid, $symb, $domain, $stu_name, $v_flag) = @_;      my ($record, $parts_graded, $courseid, $symb, $domain, $stu_name, $v_flag) = @_;
     my $version_parts = join('|',@$v_flag);      my $version_parts = join('|',@$v_flag);
       my @returned_keys;
     my $parts = join('|', @$parts_graded);      my $parts = join('|', @$parts_graded);
     my $portfolio_root = &Apache::loncommon::propath($domain,      my $portfolio_root = &propath($domain,$stu_name).
  $stu_name).   '/userfiles/portfolio';
  '/userfiles/portfolio';  
     foreach my $key (keys(%$record)) {      foreach my $key (keys(%$record)) {
         my $new_portfiles;          my $new_portfiles;
         if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {          if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
             my @v_portfiles;              my @versioned_portfiles;
             my @portfiles = split(/,/,$$record{$key});              my @portfiles = split(/\s*,\s*/,$$record{$key});
             foreach my $file (@portfiles) {              foreach my $file (@portfiles) {
                 &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);                  &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);
                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);                  my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
                 my $version = 0;  
  my ($answer_name,$answer_ver,$answer_ext) =   my ($answer_name,$answer_ver,$answer_ext) =
     &file_name_version_ext($answer_file);      &file_name_version_ext($answer_file);
                 my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stu_name,$portfolio_root);                  my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stu_name,$portfolio_root);
                 $version = &get_next_version($answer_name, $answer_ext, \@dir_list);                  my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
                 my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);                  my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);
                 if ($new_answer ne 'problem getting file') {                  if ($new_answer ne 'problem getting file') {
                     push(@v_portfiles, $directory.$new_answer);                      push(@versioned_portfiles, $directory.$new_answer);
                     &Apache::lonnet::mark_as_readonly($domain,$stu_name,                      &Apache::lonnet::mark_as_readonly($domain,$stu_name,
                         ['/portfolio'.$directory.$new_answer],                          [$directory.$new_answer],
                         [$symb,$env{'request.course.id'},'graded']);                          [$symb,$env{'request.course.id'},'graded']);
                 }                  }
                   
             }              }
             $$record{$key} = join(',',@v_portfiles);              $$record{$key} = join(',',@versioned_portfiles);
               push(@returned_keys,$key);
         }          }
     }       } 
     return 'ok';         return (@returned_keys);   
 }  }
   
 sub get_next_version {  sub get_next_version {
     my ($answer_name, $answer_ext, $dir_list);      my ($answer_name, $answer_ext, $dir_list) = @_;
     my $version;      my $version;
     foreach my $row (@$dir_list) {      foreach my $row (@$dir_list) {
         my ($file) = split(/\&/,$row,2);          my ($file) = split(/\&/,$row,2);
Line 2766  sub viewgrades { Line 3001  sub viewgrades {
                  $env{'course.'.$env{'request.course.id'}.'.domain'});                   $env{'course.'.$env{'request.course.id'}.'.domain'});
     &Apache::lonnet::clear_EXT_cache_status();      &Apache::lonnet::clear_EXT_cache_status();
   
     my $result='<h3><font color="#339933">'.&mt('Manual Grading').'</font></h3>';      my $result='<h3><span class="LC_info">'.&mt('Manual Grading').'</span></h3>';
     $result.='<font size=+1><b>Current Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n";      $result.='<h4><b>Current Resource: </b>'.$env{'form.probTitle'}.'</h4>'."\n";
   
     #view individual student submission form - called using Javascript viewOneStudent      #view individual student submission form - called using Javascript viewOneStudent
     $result.=&jscriptNform($symb);      $result.=&jscriptNform($symb);
   
     #beginning of class grading form      #beginning of class grading form
     $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".      $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="command" value="editgrades" />'."\n".   '<input type="hidden" name="command" value="editgrades" />'."\n".
  '<input type="hidden" name="section" value="'.$env{'form.section'}.'" />'."\n".   &build_section_inputs().
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="Status" value="'.$env{'form.Status'}.'" />'."\n".   '<input type="hidden" name="Status" value="'.$env{'form.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 $sectionClass;
       my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     if ($env{'form.section'} eq 'all') {      if ($env{'form.section'} eq 'all') {
  $sectionClass='Class </h3>';   $sectionClass='Class </h3>';
     } elsif ($env{'form.section'} eq 'none') {      } elsif ($env{'form.section'} eq 'none') {
  $sectionClass='Students in no Section </h3>';   $sectionClass=&mt('Students in no Section').'</h3>';
     } else {      } else {
  $sectionClass='Students in Section '.$env{'form.section'}.'</h3>';   $sectionClass=&mt('Students in Section(s) [_1]',$section_display).'</h3>';
     }      }
     $result.='<h3>Assign Common Grade To '.$sectionClass;      $result.='<h3>'.&mt('Assign Common Grade To [_1]',$sectionClass);
     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".      $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
  '<table border=0><tr bgcolor="#ffffdd"><td>';   '<table border=0><tr bgcolor="#ffffdd"><td>';
     #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) = &response_type($symb);      my ($partlist,$handgrade,$responseType) = &response_type($symb);
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     $result.='<table border="0">';      $result.='<table border="0">';
     my %seen = ();      my %seen = ();
     for (sort keys(%$handgrade)) {      my @part_response_id = &flatten_responseType($responseType);
  my ($partid,$respid) = split (/_/,$_,2);      foreach my $part_response_id (@part_response_id) {
       my ($partid,$respid) = @{ $part_response_id };
    my $part_resp = join('_',@{ $part_response_id });
  next if $seen{$partid};   next if $seen{$partid};
  $seen{$partid}++;   $seen{$partid}++;
  my $handgrade=$$handgrade{$_};   my $handgrade=$$handgrade{$part_resp};
  my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);   my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
  $weight{$partid} = $wgt eq '' ? '1' : $wgt;   $weight{$partid} = $wgt eq '' ? '1' : $wgt;
   
Line 2830  sub viewgrades { Line 3068  sub viewgrades {
  $result.= '</td><td><select name="SELVAL_'.$partid.'"'.   $result.= '</td><td><select name="SELVAL_'.$partid.'"'.
     'onChange="javascript:writeRadText(\''.$partid.'\','.      'onChange="javascript:writeRadText(\''.$partid.'\','.
  $weight{$partid}.')"> '.   $weight{$partid}.')"> '.
     '<option selected="on"> </option>'.      '<option selected="selected"> </option>'.
     '<option>excused</option>'.      '<option>excused</option>'.
     '<option>reset status</option></select></td>'.      '<option>reset status</option></select></td>'.
             '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";              '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";
Line 2838  sub viewgrades { Line 3076  sub viewgrades {
     }      }
     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".      $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
  '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';   '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
     $result.='<input type="button" value="Reset" '.      $result.='<input type="button" value="Revert to Default" '.
  'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self>';   'onClick="javascript:resetEntry('.$ctsparts.');" target="_self" />';
   
     #table listing all the students in a section/class      #table listing all the students in a section/class
     #header of table      #header of table
Line 2890  sub viewgrades { Line 3128  sub viewgrades {
     $result.='</table></td></tr></table>';      $result.='</table></td></tr></table>';
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";      $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
     $result.='<input type="button" value="Save" '.      $result.='<input type="button" value="Save" '.
  'onClick="javascript:submit();" TARGET=_self /></form>'."\n";   'onClick="javascript:submit();" target="_self" /></form>'."\n";
     if (scalar(%$fullname) eq 0) {      if (scalar(%$fullname) eq 0) {
  my $colspan=3+scalar(@parts);   my $colspan=3+scalar(@parts);
  $result='<font color="red">There are no students in section "'.$env{'form.section'}.   my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     '" with enrollment status "'.$env{'form.Status'}.'" to modify or grade.</font>';   $result='<span class="LC_warning">'.
       &mt('There are no students in section(s) [_1] with enrollment status [_2] to modify or grade',
           $section_display, $env{'form.Status'}).
       '</span>';
     }      }
     $result.=&show_grading_menu_form($symb);      $result.=&show_grading_menu_form($symb);
     return $result;      return $result;
Line 2910  sub viewstudentgrade { Line 3151  sub viewstudentgrade {
  '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.   '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.
  "\n".$ctr.'&nbsp;</td><td>&nbsp;'.   "\n".$ctr.'&nbsp;</td><td>&nbsp;'.
  '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.   '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
  '\')"; TARGET=_self>'.$fullname.'</a> '.   '\');" target="_self">'.$fullname.'</a> '.
  '<font color="#999999">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n";   '<span class="LC_internal_info">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</span></td>'."\n";
     $student=~s/:/_/; # colon doen't work in javascript for names      $student=~s/:/_/; # colon doen't work in javascript for names
     foreach my $apart (@$parts) {      foreach my $apart (@$parts) {
  my ($part,$type) = &split_part_type($apart);   my ($part,$type) = &split_part_type($apart);
Line 2948  sub viewstudentgrade { Line 3189  sub viewstudentgrade {
     $result.='&nbsp;<select name="'.      $result.='&nbsp;<select name="'.
  'GD_'.$student.'_'.$part.'_solved" '.   'GD_'.$student.'_'.$part.'_solved" '.
  'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";   'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
     $result.= (($status eq 'excused') ? '<option> </option><option selected="on">excused</option>'       $result.= (($status eq 'excused') ? '<option> </option><option selected="selected">excused</option>' 
  : '<option selected="on"> </option><option>excused</option>')."\n";   : '<option selected="selected"> </option><option>excused</option>')."\n";
     $result.='<option>reset status</option>';      $result.='<option>reset status</option>';
     $result.="</select>&nbsp;</td>\n";      $result.="</select>&nbsp;</td>\n";
  } else {   } else {
Line 2971  sub editgrades { Line 3212  sub editgrades {
     my ($request) = @_;      my ($request) = @_;
   
     my $symb=&get_symb($request);      my $symb=&get_symb($request);
     my $title='<h3><font color="#339933">Current Grade Status</font></h3>';      my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     $title.='<font size=+1><b>Current Resource: </b>'.$env{'form.probTitle'}.'</font><br />'."\n";      my $title='<h3><span class="LC_info">'.&mt('Current Grade Status').'</span></h3>';
     $title.='<font size=+1><b>Section: </b>'.$env{'form.section'}.'</font>'."\n";      $title.='<h4>'.&mt('<b>Current Resource: </b>[_1]',$env{'form.probTitle'}).'</h4><br />'."\n";
       $title.='<h4>'.&mt('<b>Section: </b>[_1]',$section_display).'</h4>'."\n";
   
     my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";      my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";
     $result.= '<table border="0"><tr bgcolor="#deffff">'.      $result.= '<table border="0"><tr bgcolor="#deffff">'.
Line 3041  sub editgrades { Line 3283  sub editgrades {
  my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
  if (!&canmodify($usec)) {   if (!&canmodify($usec)) {
     my $numcols=scalar(@partid)*4+2;      my $numcols=scalar(@partid)*4+2;
     $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";      $noupdate.=$line."<td colspan=\"$numcols\"><span class=\"LC_warning\">Not allowed to modify student</span></td></tr>";
     next;      next;
  }   }
         my %aggregate = ();          my %aggregate = ();
Line 3274  sub csvuploadmap_header { Line 3516  sub csvuploadmap_header {
     my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});      my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
     my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');      my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
     my $ignore=&mt('Ignore First Line');      my $ignore=&mt('Ignore First Line');
       $symb = &Apache::lonenc::check_encrypt($symb);
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <h3><font color="#339933">Uploading Class Grades</font></h3>  <h3><span class="LC_info">Uploading Class Grades</span></h3>
 $result  $result
 <hr />  <hr />
 <h3>Identify fields</h3>  <h3>Identify fields</h3>
Line 3362  sub upcsvScores_form { Line 3605  sub upcsvScores_form {
     $result.=$table;      $result.=$table;
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";      $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";      $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
     $result.='&nbsp;<b>Specify a file containing the class scores for current resource'.      $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource').
  '.</b></td></tr>'."\n";   '.</b></td></tr>'."\n";
     $result.='<tr bgcolor=#ffffe6><td>'."\n";      $result.='<tr bgcolor=#ffffe6><td>'."\n";
       my $upload=&mt("Upload Scores");
     my $upfile_select=&Apache::loncommon::upfile_select_html();      my $upfile_select=&Apache::loncommon::upfile_select_html();
     my $ignore=&mt('Ignore First Line');      my $ignore=&mt('Ignore First Line');
       $symb = &Apache::lonenc::check_encrypt($symb);
     $result.=<<ENDUPFORM;      $result.=<<ENDUPFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <input type="hidden" name="symb" value="$symb" />  <input type="hidden" name="symb" value="$symb" />
Line 3374  sub upcsvScores_form { Line 3619  sub upcsvScores_form {
 <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />  <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />  <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 $upfile_select  $upfile_select
 <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />  <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="$upload" />
 <label><input type="checkbox" name="noFirstLine" />$ignore</label>  <label><input type="checkbox" name="noFirstLine" />$ignore</label>
 </form>  </form>
 ENDUPFORM  ENDUPFORM
     $result.='</td></tr></table>'."\n";      $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV",
                              &mt("How do I create a CSV file from a spreadsheet"))
       .'</td></tr></table>'."\n";
     $result.='</td></tr></table><br /><br />'."\n";      $result.='</td></tr></table><br /><br />'."\n";
     $result.=&show_grading_menu_form($symb);      $result.=&show_grading_menu_form($symb);
     return $result;      return $result;
Line 3436  sub csvuploadoptions { Line 3683  sub csvuploadoptions {
     my $ignore=&mt('Ignore First Line');      my $ignore=&mt('Ignore First Line');
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <h3><font color="#339933">Uploading Class Grade Options</font></h3>  <h3><span class="LC_info">Uploading Class Grade Options</span></h3>
 <input type="hidden" name="command"    value="csvuploadassign" />  <input type="hidden" name="command"    value="csvuploadassign" />
 <!--  <!--
 <p>  <p>
Line 3494  sub csvuploadassign { Line 3741  sub csvuploadassign {
     my ($request)= @_;      my ($request)= @_;
     my ($symb)=&get_symb($request);      my ($symb)=&get_symb($request);
     if (!$symb) {return '';}      if (!$symb) {return '';}
       my $error_msg = '';
     &Apache::loncommon::load_tmp_file($request);      &Apache::loncommon::load_tmp_file($request);
     my @gradedata = &Apache::loncommon::upfile_record_sep();      my @gradedata = &Apache::loncommon::upfile_record_sep();
     if ($env{'form.noFirstLine'}) { shift(@gradedata); }      if ($env{'form.noFirstLine'}) { shift(@gradedata); }
Line 3546  sub csvuploadassign { Line 3794  sub csvuploadassign {
  my $part=$1;   my $part=$1;
  my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',   my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
       $symb,$domain,$username);        $symb,$domain,$username);
  $entries{$fields{$dest}}=~s/\s//g;                  if ($wgt) {
  my $pcr=$entries{$fields{$dest}} / $wgt;                      $entries{$fields{$dest}}=~s/\s//g;
  my $award='correct_by_override';                      my $pcr=$entries{$fields{$dest}} / $wgt;
  $grades{"resource.$part.awarded"}=$pcr;                      my $award='correct_by_override';
  $grades{"resource.$part.solved"}=$award;                      $grades{"resource.$part.awarded"}=$pcr;
  $points{$part}=1;                      $grades{"resource.$part.solved"}=$award;
                       $points{$part}=1;
                   } else {
                       $error_msg = "<br />" .
                           &mt("Some point values were assigned"
                               ." for problems with a weight "
                               ."of zero. These values were "
                               ."ignored.");
                   }
     } else {      } else {
  if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
  if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
Line 3561  sub csvuploadassign { Line 3817  sub csvuploadassign {
  $grades{$store_key}=$entries{$fields{$dest}};   $grades{$store_key}=$entries{$fields{$dest}};
     }      }
  }   }
  if (! %grades) { push(@skipped,"$username:$domain no data to store"); }   if (! %grades) { push(@skipped,"$username:$domain no data to save"); }
  $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";   $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
 # &Apache::lonnet::logthis(" storing ".(join('-',%grades)));  # &Apache::lonnet::logthis(" storing ".(join('-',%grades)));
  my $result=&Apache::lonnet::cstore(\%grades,$symb,   my $result=&Apache::lonnet::cstore(\%grades,$symb,
Line 3571  sub csvuploadassign { Line 3827  sub csvuploadassign {
     $request->print('.');      $request->print('.');
  } else {   } else {
     $request->print("<p>      $request->print("<p>
                               <font color='red'>                                <span class=\"LC_error\">
                                  Failed to store student $username\@$domain.                                   Failed to save student $username:$domain.
                                  Message when trying to store was ($result)                                   Message when trying to save was ($result)
                               </font>                                </span>
                              </p>" );                               </p>" );
  }   }
  $request->rflush();   $request->rflush();
  $countdone++;   $countdone++;
     }      }
     $request->print("<br />Stored $countdone students\n");      $request->print("<br />Saved $countdone students\n");
     if (@skipped) {      if (@skipped) {
  $request->print('<p><font size="+1"><b>Skipped Students</b></font></p>');   $request->print('<p><h4><b>Skipped Students</b></h4></p>');
  foreach my $student (@skipped) { $request->print("$student<br />\n"); }   foreach my $student (@skipped) { $request->print("$student<br />\n"); }
     }      }
     if (@notallowed) {      if (@notallowed) {
  $request->print('<p><font size="+1" color="red"><b>Students Not Allowed to Modify</b></font></p>');   $request->print('<p><span class="LC_error">Students Not Allowed to Modify</span></p>');
  foreach my $student (@notallowed) { $request->print("$student<br />\n"); }   foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
     }      }
     $request->print("<br />\n");      $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb));      $request->print(&show_grading_menu_form($symb));
     return '';      return $error_msg;
 }  }
 #------------- end of section for handling csv file upload ---------  #------------- end of section for handling csv file upload ---------
 #  #
Line 3625  LISTJAVASCRIPT Line 3881  LISTJAVASCRIPT
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
   
     my $result='<h3><font color="#339933">&nbsp;'.      my $result='<h3><span class="LC_info">&nbsp;'.
  'Manual Grading by Page or Sequence</font></h3>';   'Manual Grading by Page or Sequence</span></h3>';
   
     $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";      $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
     $result.='&nbsp;<b>Problems from:</b> <select name="selectpage">'."\n";      $result.='&nbsp;<b>Problems from:</b> <select name="selectpage">'."\n";
     my ($titles,$symbx) = &getSymbMap($request);      my ($titles,$symbx) = &getSymbMap();
     my ($curpage) =&Apache::lonnet::decode_symb($symb);       my ($curpage) =&Apache::lonnet::decode_symb($symb); 
 #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb);   #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
 #    my $type=($curpage =~ /\.(page|sequence)/);  #    my $type=($curpage =~ /\.(page|sequence)/);
Line 3638  LISTJAVASCRIPT Line 3894  LISTJAVASCRIPT
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<option value="'.$ctr.'" '.   $result.='<option value="'.$ctr.'" '.
     ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').      ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').
     '>'.$showtitle.'</option>'."\n";      '>'.$showtitle.'</option>'."\n";
  $ctr++;   $ctr++;
     }      }
Line 3653  LISTJAVASCRIPT Line 3909  LISTJAVASCRIPT
     $result.='<input type="hidden" name="page" />'."\n".      $result.='<input type="hidden" name="page" />'."\n".
  '<input type="hidden" name="title" />'."\n";   '<input type="hidden" name="title" />'."\n";
   
     $result.='&nbsp;<b>View Problems Text: </b><label><input type="radio" name="vProb" value="no" checked="on" /> no </label>'."\n".      $result.='&nbsp;<b>View Problems Text: </b><label><input type="radio" name="vProb" value="no" checked="checked" /> no </label>'."\n".
  '<label><input type="radio" name="vProb" value="yes" /> yes </label>'."<br />\n";   '<label><input type="radio" name="vProb" value="yes" /> yes </label>'."<br />\n";
   
     $result.='&nbsp;<b>Submission Details: </b>'.      $result.='&nbsp;<b>Submission Details: </b>'.
  '<label><input type="radio" name="lastSub" value="none" /> none</label>'."\n".   '<label><input type="radio" name="lastSub" value="none" /> none</label>'."\n".
  '<label><input type="radio" name="lastSub" value="datesub" checked /> by dates and submissions</label>'."\n".   '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> by dates and submissions</label>'."\n".
  '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n";   '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n";
       
     $result.='<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".      $result.=&build_section_inputs();
  '<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".      $result.='<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".
  '<input type="hidden" name="command" value="displayPage" />'."\n".   '<input type="hidden" name="command" value="displayPage" />'."\n".
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";
   
       $result.='&nbsp;<b>'.&mt('Use CODE:').' </b>'.
    '<input type="text" name="CODE" value="" /><br />'."\n";
   
     $result.='&nbsp;<input type="button" '.      $result.='&nbsp;<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);"value="Next->" /><br />'."\n";   'onClick="javascript:checkPickOne(this.form);"value="Next->" /><br />'."\n";
   
Line 3697  LISTJAVASCRIPT Line 3956  LISTJAVASCRIPT
  $studentTable.=($ptr%2 == 0 ? '</td></tr>' : '');   $studentTable.=($ptr%2 == 0 ? '</td></tr>' : '');
  $ptr++;   $ptr++;
     }      }
     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%2 == 0);      $studentTable.='</td><td>&nbsp;</td><td>&nbsp;</td></tr>' if ($ptr%2 == 0);
     $studentTable.='</td></tr></table></td></tr></table>'."\n";      $studentTable.='</table></td></tr></table>'."\n";
     $studentTable.='<input type="button" '.      $studentTable.='<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);"value="Next->" /></form>'."\n";   'onClick="javascript:checkPickOne(this.form);"value="Next->" /></form>'."\n";
   
Line 3709  LISTJAVASCRIPT Line 3968  LISTJAVASCRIPT
 }  }
   
 sub getSymbMap {  sub getSymbMap {
     my ($request) = @_;  
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
   
     my %symbx = ();      my %symbx = ();
Line 3721  sub getSymbMap { Line 3979  sub getSymbMap {
        1,0,1);         1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {      for my $sequence ($navmap->getById('0.0'), @sequences) {
  if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {   if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
     my $title = $minder.'.'.$sequence->compTitle();      my $title = $minder.'.'.
     push @titles, $title; # minder in case two titles are identical   &HTML::Entities::encode($sequence->compTitle(),'"\'&');
     $symbx{$title} = $sequence->symb();      push(@titles, $title); # minder in case two titles are identical
       $symbx{$title} = &HTML::Entities::encode($sequence->symb(),'"\'&');
     $minder++;      $minder++;
  }   }
     }      }
Line 3752  sub displayPage { Line 4011  sub displayPage {
     &Apache::lonnet::clear_EXT_cache_status();      &Apache::lonnet::clear_EXT_cache_status();
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print('<font color="red">Unable to view requested student.('.$env{'form.student'}.')</font>');   $request->print('<span class="LC_warning">Unable to view requested student.('.$env{'form.student'}.')</span>');
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;   return;
     }      }
     my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';      my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';
     $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom).      $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom).
  '</h3>'."\n";   '</h3>'."\n";
       if (&Apache::lonnet::validCODE($env{'form.CODE'})) {
    $result.='<h3>&nbsp;CODE: '.$env{'form.CODE'}.'</h3>'."\n";
       } else {
    delete($env{'form.CODE'});
       }
     &sub_page_js($request);      &sub_page_js($request);
     $request->print($result);      $request->print($result);
   
Line 3766  sub displayPage { Line 4030  sub displayPage {
     my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});      my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {      if (!$map) {
  $request->print('<font color="red">Unable to view requested sequence. ('.$resUrl.')</font>');   $request->print('<span class="LC_warning">Unable to view requested sequence. ('.$resUrl.')</span>');
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;    return; 
     }      }
Line 3779  sub displayPage { Line 4043  sub displayPage {
  '<input type="hidden" name="student" value="'.$env{'form.student'}.'" />'."\n".   '<input type="hidden" name="student" value="'.$env{'form.student'}.'" />'."\n".
  '<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".   '<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".
  '<input type="hidden" name="title"   value="'.$env{'form.title'}.'" />'."\n".   '<input type="hidden" name="title"   value="'.$env{'form.title'}.'" />'."\n".
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="overRideScore" value="no" />'."\n".   '<input type="hidden" name="overRideScore" value="no" />'."\n".
  '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n";   '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n";
   
     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').      if (defined($env{'form.CODE'})) {
    $studentTable.=
       '<input type="hidden" name="CODE" value="'.$env{'form.CODE'}.'" />'."\n";
       }
       my $checkIcon = '<img alt="'.&mt('Check Mark').
    '" src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
   
     $studentTable.='&nbsp;<b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon.      $studentTable.='&nbsp;<b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon.
Line 3801  sub displayPage { Line 4070  sub displayPage {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
   
         if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {          if (ref($curRes) && $curRes->is_problem()) {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
     $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.      $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
  (scalar(@{$parts}) == 1 ? '' : '<br />('.scalar(@{$parts}).'&nbsp;parts)').'</td>';   (scalar(@{$parts}) == 1 ? '' : '<br />('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
     $studentTable.='<td valign="top">';      $studentTable.='<td valign="top">';
       my %form = ('CODE' => $env{'form.CODE'},);
     if ($env{'form.vProb'} eq 'yes' ) {      if ($env{'form.vProb'} eq 'yes' ) {
  $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,   $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
      undef,'both');       undef,'both',\%form);
     } else {      } else {
  my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'});   my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
  $companswer =~ s|<form(.*?)>||g;   $companswer =~ s|<form(.*?)>||g;
  $companswer =~ s|</form>||g;   $companswer =~ s|</form>||g;
 # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>  # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
Line 3827  sub displayPage { Line 4097  sub displayPage {
   
     if ($env{'form.lastSub'} eq 'datesub') {      if ($env{'form.lastSub'} eq 'datesub') {
  if ($record{'version'} eq '') {   if ($record{'version'} eq '') {
     $studentTable.='<br />&nbsp;<font color="red">No recorded submission for this problem</font><br />';      $studentTable.='<br />&nbsp;<span class="LC_warning">No recorded submission for this problem</span><br />';
  } else {   } else {
     my %responseType = ();      my %responseType = ();
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
Line 3863  sub displayPage { Line 4133  sub displayPage {
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
   
     $studentTable.='</td></tr></table></td></tr></table>'."\n".      $studentTable.='</table></td></tr></table>'."\n".
  '<input type="button" value="Save" '.   '<input type="button" value="Save" '.
  'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.   'onClick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
  '</form>'."\n";   '</form>'."\n";
     $studentTable.=&show_grading_menu_form($symb);      $studentTable.=&show_grading_menu_form($symb);
     $request->print($studentTable);      $request->print($studentTable);
Line 3889  sub displaySubByDates { Line 4159  sub displaySubByDates {
     my %orders;      my %orders;
     $mark{'correct_by_student'} = $checkIcon;      $mark{'correct_by_student'} = $checkIcon;
     if (!exists($$record{'1:timestamp'})) {      if (!exists($$record{'1:timestamp'})) {
  return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />';   return '<br />&nbsp;<span class="LC_warning">Nothing submitted - no attempts</span><br />';
     }      }
   
     my $interaction;      my $interaction;
Line 3923  sub displaySubByDates { Line 4193  sub displaySubByDates {
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));                 : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
     #&Apache::lonnet::logthis("match $matchKey $responseId (".$$record{$version.':'.$matchKey});      #&Apache::lonnet::logthis("match $matchKey $responseId (".$$record{$version.':'.$matchKey});
     $displaySub[0].='<b>Part:</b>&nbsp;'.$display_part.'&nbsp;';      $displaySub[0].='<b>Part:</b>&nbsp;'.$display_part.'&nbsp;';
     $displaySub[0].='<font color="#999999">(ID&nbsp;'.      $displaySub[0].='<span class="LC_internal_info">(ID&nbsp;'.
  $responseId.')</font>&nbsp;<b>';   $responseId.')</span>&nbsp;<b>';
     if ($$record{"$where.$partid.tries"} eq '') {      if ($$record{"$where.$partid.tries"} eq '') {
  $displaySub[0].='Trial&nbsp;not&nbsp;counted';   $displaySub[0].='Trial&nbsp;not&nbsp;counted';
     } else {      } else {
Line 3989  sub updateGradeByPage { Line 4259  sub updateGradeByPage {
     my ($uname,$udom) = split(/:/,$env{'form.student'});      my ($uname,$udom) = split(/:/,$env{'form.student'});
     my $usec=$classlist->{$env{'form.student'}}[5];      my $usec=$classlist->{$env{'form.student'}}[5];
     if (!&canmodify($usec)) {      if (!&canmodify($usec)) {
  $request->print('<font color="red">Unable to modify requested student.('.$env{'form.student'}.'</font>');   $request->print('<span class="LC_warning">Unable to modify requested student.('.$env{'form.student'}.'</span>');
  $request->print(&show_grading_menu_form($env{'form.symb'}));   $request->print(&show_grading_menu_form($env{'form.symb'}));
  return;   return;
     }      }
     my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';      my $result='<h3><span class="LC_info">&nbsp;'.$env{'form.title'}.'</span></h3>';
     $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).      $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).
  '</h3>'."\n";   '</h3>'."\n";
   
Line 4003  sub updateGradeByPage { Line 4273  sub updateGradeByPage {
     my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});      my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {      if (!$map) {
  $request->print('<font color="red">Unable to grade requested sequence. ('.$resUrl.')</font>');   $request->print('<span class="LC_warning">Unable to grade requested sequence. ('.$resUrl.')</span>');
  my ($symb)=&get_symb($request);   my ($symb)=&get_symb($request);
  $request->print(&show_grading_menu_form($symb));   $request->print(&show_grading_menu_form($symb));
  return;    return; 
Line 4025  sub updateGradeByPage { Line 4295  sub updateGradeByPage {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
   
         if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {          if (ref($curRes) && $curRes->is_problem()) {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
Line 4080  sub updateGradeByPage { Line 4350  sub updateGradeByPage {
  $displayPts[1].='&nbsp;<b>Part:</b> '.$display_part.' = '.   $displayPts[1].='&nbsp;<b>Part:</b> '.$display_part.' = '.
      (($score eq 'excused') ? 'excused' : $newpts).       (($score eq 'excused') ? 'excused' : $newpts).
     '&nbsp;<br />';      '&nbsp;<br />';
   
  $question++;   $question++;
  next if ($dropMenu eq 'reset status' || ($newpts == $oldpts && $score ne 'excused'));   next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused'));
   
  $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';   $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
  $newrecord{'resource.'.$partid.'.solved'}   = $score if $score ne '';   $newrecord{'resource.'.$partid.'.solved'}   = $score if $score ne '';
Line 4092  sub updateGradeByPage { Line 4361  sub updateGradeByPage {
  $changeflag++;   $changeflag++;
     }      }
     if (scalar(keys(%newrecord)) > 0) {      if (scalar(keys(%newrecord)) > 0) {
    my %record = 
       &Apache::lonnet::restore($symbx,$env{'request.course.id'},
        $udom,$uname);
   
    if (&Apache::lonnet::validCODE($env{'form.CODE'})) {
       $newrecord{'resource.CODE'} = $env{'form.CODE'};
    } elsif (&Apache::lonnet::validCODE($record{'resource.CODE'})) {
       $newrecord{'resource.CODE'} = '';
    }
  &Apache::lonnet::cstore(\%newrecord,$symbx,$env{'request.course.id'},   &Apache::lonnet::cstore(\%newrecord,$symbx,$env{'request.course.id'},
  $udom,$uname);   $udom,$uname);
    %record = &Apache::lonnet::restore($symbx,
      $env{'request.course.id'},
      $udom,$uname);
    &check_and_remove_from_queue($parts,\%record,undef,$symbx,
        $cdom,$cnum,$udom,$uname);
     }      }
       
             if ($aggregateflag) {              if ($aggregateflag) {
                 &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,                  &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                       $env{'course.'.$env{'request.course.id'}.'.domain'},                        $env{'course.'.$env{'request.course.id'}.'.domain'},
Line 4128  sub updateGradeByPage { Line 4412  sub updateGradeByPage {
 #  #
 #------ start of section for handling grading by page/sequence ---------  #------ start of section for handling grading by page/sequence ---------
   
   =pod
   
   =head1 Bubble sheet grading routines
   
     For this documentation:
   
      'scanline' refers to the full line of characters
      from the file that we are parsing that represents one entire sheet
   
      'bubble line' refers to the data
      representing the line of bubbles that are on the physical bubble sheet
   
   
   The overall process is that a scanned in bubble sheet data is uploaded
   into a course. When a user wants to grade, they select a
   sequence/folder of resources, a file of bubble sheet info, and pick
   one of the predefined configurations for what each scanline looks
   like.
   
   Next each scanline is checked for any errors of either 'missing
   bubbles' (it's an error because it may have been mis-scanned
   because too light bubbling), 'double bubble' (each bubble line should
   have no more that one letter picked), invalid or duplicated CODE,
   invalid student ID
   
   If the CODE option is used that determines the randomization of the
   homework problems, either way the student ID is looked up into a
   username:domain.
   
   During the validation phase the instructor can choose to skip scanlines. 
   
   After the validation phase, there are now 3 bubble sheet files
   
     scantron_original_filename (unmodified original file)
     scantron_corrected_filename (file where the corrected information has replaced the original information)
     scantron_skipped_filename (contains the exact text of scanlines that where skipped)
   
   Also there is a separate hash nohist_scantrondata that contains extra
   correction information that isn't representable in the bubble sheet
   file (see &scantron_getfile() for more information)
   
   After all scanlines are either valid, marked as valid or skipped, then
   foreach line foreach problem in the picked sequence, an ssi request is
   made that simulates a user submitting their selected letter(s) against
   the homework problem.
   
   =over 4
   
   =cut
   
   
   =pod 
   
   =item defaultFormData
   
     Returns html hidden inputs used to hold context/default values.
   
    Arguments:
     $symb - $symb of the current resource 
   
   =cut
   
 sub defaultFormData {  sub defaultFormData {
     my ($symb)=@_;      my ($symb)=@_;
     return '      return '
       <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".        <input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
      '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".       '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
      '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";       '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
 }  }
   
   =pod 
   
   =item getSequenceDropDown
   
      Return html dropdown of possible sequences to grade
    
    Arguments:
      $symb - $symb of the current resource 
   
   =cut
   
 sub getSequenceDropDown {  sub getSequenceDropDown {
     my ($request,$symb)=@_;      my ($symb)=@_;
     my $result='<select name="selectpage">'."\n";      my $result='<select name="selectpage">'."\n";
     my ($titles,$symbx) = &getSymbMap($request);      my ($titles,$symbx) = &getSymbMap();
     my ($curpage)=&Apache::lonnet::decode_symb($symb);       my ($curpage)=&Apache::lonnet::decode_symb($symb); 
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
  $result.='<option value="'.$$symbx{$_}.'" '.   $result.='<option value="'.$$symbx{$_}.'" '.
     ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').      ($$symbx{$_} =~ /$curpage$/ ? 'selected="selected"' : '').
     '>'.$showtitle.'</option>'."\n";      '>'.$showtitle.'</option>'."\n";
  $ctr++;   $ctr++;
     }      }
Line 4153  sub getSequenceDropDown { Line 4510  sub getSequenceDropDown {
     return $result;      return $result;
 }  }
   
   
   =pod 
   
   =item scantron_filenames
   
      Returns a list of the scantron files in the current course 
   
   =cut
   
 sub scantron_filenames {  sub scantron_filenames {
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
     my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,      my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
     &Apache::loncommon::propath($cdom,$cname));      &propath($cdom,$cname));
     my @possiblenames;      my @possiblenames;
     foreach my $filename (sort(@files)) {      foreach my $filename (sort(@files)) {
  ($filename)=split(/&/,$filename);   ($filename)=split(/&/,$filename);
Line 4168  sub scantron_filenames { Line 4534  sub scantron_filenames {
     return @possiblenames;      return @possiblenames;
 }  }
   
   =pod 
   
   =item scantron_uploads
   
      Returns  html drop-down list of scantron files in current course.
   
    Arguments:
      $file2grade - filename to set as selected in the dropdown
   
   =cut
   
 sub scantron_uploads {  sub scantron_uploads {
     my ($file2grade) = @_;      my ($file2grade) = @_;
     my $result= '<select name="scantron_selectfile">';      my $result= '<select name="scantron_selectfile">';
     $result.="<option></option>";      $result.="<option></option>";
     foreach my $filename (sort(&scantron_filenames())) {      foreach my $filename (sort(&scantron_filenames())) {
  $result.="<option".($filename eq $file2grade ? ' selected="on"':'').">$filename</option>\n";   $result.="<option".($filename eq $file2grade ? ' selected="selected"':'').">$filename</option>\n";
     }      }
     $result.="</select>";      $result.="</select>";
     return $result;      return $result;
 }  }
   
   =pod 
   
   =item scantron_scantab
   
     Returns html drop down of the scantron formats in the scantronformat.tab
     file.
   
   =cut
   
 sub scantron_scantab {  sub scantron_scantab {
     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');      my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
     my $result='<select name="scantron_format">'."\n";      my $result='<select name="scantron_format">'."\n";
Line 4193  sub scantron_scantab { Line 4579  sub scantron_scantab {
     return $result;      return $result;
 }  }
   
   =pod 
   
   =item scantron_CODElist
   
     Returns html drop down of the saved CODE lists from current course,
     generated from earlier printings.
   
   =cut
   
 sub scantron_CODElist {  sub scantron_CODElist {
     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
Line 4207  sub scantron_CODElist { Line 4602  sub scantron_CODElist {
     return $namechoice;      return $namechoice;
 }  }
   
   =pod 
   
   =item scantron_CODEunique
   
     Returns the html for "Each CODE to be used once" radio.
   
   =cut
   
 sub scantron_CODEunique {  sub scantron_CODEunique {
     my $result='<nobr>      my $result='<span style="white-space: nowrap;">
                  <label><input type="radio" name="scantron_CODEunique"                   <label><input type="radio" name="scantron_CODEunique"
                         value="yes" checked="checked" /> Yes </label>                          value="yes" checked="checked" />'.&mt('Yes').' </label>
                 </nobr>                  </span>
                 <nobr>                  <span style="white-space: nowrap;">
                  <label><input type="radio" name="scantron_CODEunique"                   <label><input type="radio" name="scantron_CODEunique"
                         value="no" /> No </label>                          value="no" />'.&mt('No').' </label>
                 </nobr>';                  </span>';
     return $result;      return $result;
 }  }
   
   =pod 
   
   =item scantron_selectphase
   
     Generates the initial screen to start the bubble sheet process.
     Allows for - starting a grading run.
                - downloading existing scan data (original, corrected
                                                   or skipped info)
   
                - uploading new scan data
   
    Arguments:
     $r          - The Apache request object
     $file2grade - name of the file that contain the scanned data to score
   
   =cut
   
 sub scantron_selectphase {  sub scantron_selectphase {
     my ($r,$file2grade) = @_;      my ($r,$file2grade) = @_;
     my ($symb)=&get_symb($r);      my ($symb)=&get_symb($r);
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $sequence_selector=&getSequenceDropDown($r,$symb);      my $sequence_selector=&getSequenceDropDown($symb);
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $grading_menu_button=&show_grading_menu_form($symb);      my $grading_menu_button=&show_grading_menu_form($symb);
     my $file_selector=&scantron_uploads($file2grade);      my $file_selector=&scantron_uploads($file2grade);
Line 4231  sub scantron_selectphase { Line 4651  sub scantron_selectphase {
     my $CODE_selector=&scantron_CODElist();      my $CODE_selector=&scantron_CODElist();
     my $CODE_unique=&scantron_CODEunique();      my $CODE_unique=&scantron_CODEunique();
     my $result;      my $result;
     #FIXME allow instructor to be able to download the scantron file  
     # and to upload it,      # Chunk of form to prompt for a file to grade and how:
   
     $result.= <<SCANTRONFORM;      $result.= <<SCANTRONFORM;
     <table width="100%" border="0">      <table width="100%" border="0">
     <tr>      <tr>
Line 4265  sub scantron_selectphase { Line 4686  sub scantron_selectphase {
     <td> Options: </td>      <td> Options: </td>
             <td>              <td>
        <label><input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only previously skipped records</label> <br />         <label><input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only previously skipped records</label> <br />
                <label><input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove all exisiting corrections</label> <br />                 <label><input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove all existing corrections</label> <br />
                <label><input type="checkbox" name="scantron_options_hidden" value="ignore_hidden"/> Skip hidden resources when grading</label>                 <label><input type="checkbox" name="scantron_options_hidden" value="ignore_hidden"/> Skip hidden resources when grading</label>
     </td>      </td>
           </tr>            </tr>
Line 4285  SCANTRONFORM Line 4706  SCANTRONFORM
     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||      if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {          &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
   
    # Chunk of form to prompt for a scantron file upload.
   
         $r->print(<<SCANTRONFORM);          $r->print(<<SCANTRONFORM);
     <tr>      <tr>
       <td bgcolor="#777777">        <td bgcolor="#777777">
Line 4330  UPLOAD Line 4753  UPLOAD
     </tr>      </tr>
 SCANTRONFORM  SCANTRONFORM
     }      }
   
       # Chunk of the form that prompts to view a scoring office file,
       # corrected file, skipped records in a file.
   
     $r->print(<<SCANTRONFORM);      $r->print(<<SCANTRONFORM);
     <tr>      <tr>
       <form action='/adm/grades' name='scantron_download'>        <form action='/adm/grades' name='scantron_download'>
         <td bgcolor="#777777">          <td bgcolor="#777777">
     $default_form_data
           <input type="hidden" name="command" value="scantron_download" />            <input type="hidden" name="command" value="scantron_download" />
           <table width="100%" border="0">            <table width="100%" border="0">
             <tr bgcolor="#e6ffff">              <tr bgcolor="#e6ffff">
Line 4363  SCANTRONFORM Line 4791  SCANTRONFORM
     return      return
 }  }
   
   =pod
   
   =item get_scantron_config
   
      Parse and return the scantron configuration line selected as a
      hash of configuration file fields.
   
    Arguments:
       which - the name of the configuration to parse from the file.
   
   
    Returns:
               If the named configuration is not in the file, an empty
               hash is returned.
       a hash with the fields
         name         - internal name for the this configuration setup
         description  - text to display to operator that describes this config
         CODElocation - if 0 or the string 'none'
                             - no CODE exists for this config
                        if -1 || the string 'letter'
                             - a CODE exists for this config and is
                               a string of letters
                        Unsupported value (but planned for future support)
                             if a positive integer
                                  - The CODE exists as the first n items from
                                    the question section of the form
                             if the string 'number'
                                  - The CODE exists for this config and is
                                    a string of numbers
         CODEstart   - (only matter if a CODE exists) column in the line where
                        the CODE starts
         CODElength  - length of the CODE
         IDstart     - column where the student ID number starts
         IDlength    - length of the student ID info
         Qstart      - column where the information from the bubbled
                       'questions' start
         Qlength     - number of columns comprising a single bubble line from
                       the sheet. (usually either 1 or 10)
         Qon         - either a single character representing the character used
                       to signal a bubble was chosen in the positional setup, or
                       the string 'letter' if the letter of the chosen bubble is
                       in the final, or 'number' if a number representing the
                       chosen bubble is in the file (1->A 0->J)
         Qoff        - the character used to represent that a bubble was
                       left blank
         PaperID     - if the scanning process generates a unique number for each
                       sheet scanned the column that this ID number starts in
         PaperIDlength - number of columns that comprise the unique ID number
                         for the sheet of paper
         FirstName   - column that the first name starts in
         FirstNameLength - number of columns that the first name spans
    
         LastName    - column that the last name starts in
         LastNameLength - number of columns that the last name spans
   
   =cut
   
 sub get_scantron_config {  sub get_scantron_config {
     my ($which) = @_;      my ($which) = @_;
     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');      my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
Line 4395  sub get_scantron_config { Line 4880  sub get_scantron_config {
     return %config;      return %config;
 }  }
   
   =pod 
   
   =item username_to_idmap
   
       creates a hash keyed by student id with values of the corresponding
       student username:domain.
   
     Arguments:
   
       $classlist - reference to the class list hash. This is a hash
                    keyed by student name:domain  whose elements are references
                    to arrays containing various chunks of information
                    about the student. (See loncoursedata for more info).
   
     Returns
       %idmap - the constructed hash
   
   =cut
   
 sub username_to_idmap {  sub username_to_idmap {
     my ($classlist)= @_;      my ($classlist)= @_;
     my %idmap;      my %idmap;
Line 4405  sub username_to_idmap { Line 4909  sub username_to_idmap {
     return %idmap;      return %idmap;
 }  }
   
   =pod
   
   =item scantron_fixup_scanline
   
      Process a requested correction to a scanline.
   
     Arguments:
       $scantron_config   - hash from &get_scantron_config()
       $scan_data         - hash of correction information 
                             (see &scantron_getfile())
       $line              - existing scanline
       $whichline         - line number of the passed in scanline
       $field             - type of change to process 
                            (either 
                             'ID'     -> correct the student ID number
                             'CODE'   -> correct the CODE
                             'answer' -> fixup the submitted answers)
       
      $args               - hash of additional info,
                             - 'ID' 
                                  'newid' -> studentID to use in replacement
                                             of existing one
                             - 'CODE' 
                                  'CODE_ignore_dup' - set to true if duplicates
                                                      should be ignored.
                          'CODE' - is new code or 'use_unfound'
                                           if the existing unfound code should
                                           be used as is
                             - 'answer'
                                  'response' - new answer or 'none' if blank
                                  'question' - the bubble line to change
   
     Returns:
       $line - the modified scanline
   
     Side effects: 
       $scan_data - may be updated
   
   =cut
   
   
 sub scantron_fixup_scanline {  sub scantron_fixup_scanline {
     my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_;      my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_;
   
     if ($field eq 'ID') {      if ($field eq 'ID') {
  if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) {   if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) {
     return ($line,1,'New value too large');      return ($line,1,'New value too large');
Line 4450  sub scantron_fixup_scanline { Line 4996  sub scantron_fixup_scanline {
  $answer=$alphabet[$args->{'response'}];   $answer=$alphabet[$args->{'response'}];
     } elsif ($on eq 'number') {      } elsif ($on eq 'number') {
  $answer=$args->{'response'}+1;   $answer=$args->{'response'}+1;
    if ($answer == 10) { $answer = '0'; }
     } else {      } else {
  substr($answer,$args->{'response'},1)=$on;   substr($answer,$args->{'response'},1)=$on;
     }      }
Line 4462  sub scantron_fixup_scanline { Line 5009  sub scantron_fixup_scanline {
     return $line;      return $line;
 }  }
   
   =pod
   
   =item scan_data
   
       Edit or look up  an item in the scan_data hash.
   
     Arguments:
       $scan_data  - The hash (see scantron_getfile)
       $key        - shorthand of the key to edit (actual key is
                     scantronfilename_key).
       $data        - New value of the hash entry.
       $delete      - If true, the entry is removed from the hash.
   
     Returns:
       The new value of the hash table field (undefined if deleted).
   
   =cut
   
   
 sub scan_data {  sub scan_data {
     my ($scan_data,$key,$value,$delete)=@_;      my ($scan_data,$key,$value,$delete)=@_;
     my $filename=$env{'form.scantron_selectfile'};      my $filename=$env{'form.scantron_selectfile'};
Line 4472  sub scan_data { Line 5038  sub scan_data {
     return $scan_data->{$filename.'_'.$key};      return $scan_data->{$filename.'_'.$key};
 }  }
   
   =pod 
   
   =item scantron_parse_scanline
   
     Decodes a scanline from the selected scantron file
   
    Arguments:
       line             - The text of the scantron file line to process
       whichline        - Line number
       scantron_config  - Hash describing the format of the scantron lines.
       scan_data        - Hash of extra information about the scanline
                          (see scantron_getfile for more information)
       just_header      - True if should not process question answers but only
                          the stuff to the left of the answers.
    Returns:
      Hash containing the result of parsing the scanline
   
      Keys are all proceeded by the string 'scantron.'
   
          CODE    - the CODE in use for this scanline
          useCODE - 1 if the CODE is invalid but it usage has been forced
                    by the operator
          CODE_ignore_dup - 1 if the CODE is a duplicated use when unique
                               CODEs were selected, but the usage has been
                               forced by the operator
          ID  - student ID
          PaperID - if used, the ID number printed on the sheet when the 
                    paper was scanned
          FirstName - first name from the sheet
          LastName  - last name from the sheet
   
        if just_header was not true these key may also exist
   
          missingerror - a list of bubbled line numbers that had a blank bubble
                         that is considered an error (if the operator had already
                         okayed a blank bubble line as really being blank then
                         that bubble line number won't appear here.
          doubleerror  - a list of bubbled line numbers that had more than one
                         bubble filled in and has not been corrected by the
                         operator
          maxquest     - the number of the last bubble line that was parsed
   
          (<number> starts at 1)
          <number>.answer - zero or more letters representing the selected
                            letters from the scanline for the bubble line 
                            <number>.
                            if blank there was either no bubble or there where
                            multiple bubbles, (consult the keys missingerror and
                            doubleerror if this is an error condition)
   
   =cut
   
 sub scantron_parse_scanline {  sub scantron_parse_scanline {
     my ($line,$whichline,$scantron_config,$scan_data,$justHeader)=@_;      my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;
     my %record;      my %record;
     my $questions=substr($line,$$scantron_config{'Qstart'}-1);      my $questions=substr($line,$$scantron_config{'Qstart'}-1);  # Answers
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);      my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff
     if (!($$scantron_config{'CODElocation'} eq 0 ||      if (!($$scantron_config{'CODElocation'} eq 0 ||
   $$scantron_config{'CODElocation'} eq 'none')) {    $$scantron_config{'CODElocation'} eq 'none')) {
  if ($$scantron_config{'CODElocation'} < 0 ||   if ($$scantron_config{'CODElocation'} < 0 ||
Line 4506  sub scantron_parse_scanline { Line 5124  sub scantron_parse_scanline {
     $record{'scantron.LastName'}=      $record{'scantron.LastName'}=
  substr($data,$$scantron_config{'LastName'}-1,   substr($data,$$scantron_config{'LastName'}-1,
        $$scantron_config{'LastNamelength'});         $$scantron_config{'LastNamelength'});
     if ($justHeader) { return \%record; }      if ($just_header) { return \%record; }
   
     my @alphabet=('A'..'Z');      my @alphabet=('A'..'Z');
     my $questnum=0;      my $questnum=0;
Line 4516  sub scantron_parse_scanline { Line 5134  sub scantron_parse_scanline {
  substr($questions,0,$$scantron_config{'Qlength'})='';   substr($questions,0,$$scantron_config{'Qlength'})='';
  if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }   if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
  if ($$scantron_config{'Qon'} eq 'letter') {   if ($$scantron_config{'Qon'} eq 'letter') {
     if ($currentquest eq '?') {      if ($currentquest eq '?'
    || $currentquest eq '*') {
  push(@{$record{'scantron.doubleerror'}},$questnum);   push(@{$record{'scantron.doubleerror'}},$questnum);
  $record{"scantron.$questnum.answer"}='';   $record{"scantron.$questnum.answer"}='';
     } elsif (!$currentquest       } elsif (!defined($currentquest)
      || $currentquest eq $$scantron_config{'Qoff'}       || $currentquest eq $$scantron_config{'Qoff'}
      || $currentquest !~ /^[A-Z]$/) {       || $currentquest !~ /^[A-Z]$/) {
  $record{"scantron.$questnum.answer"}='';   $record{"scantron.$questnum.answer"}='';
Line 4530  sub scantron_parse_scanline { Line 5149  sub scantron_parse_scanline {
  $record{"scantron.$questnum.answer"}=$currentquest;   $record{"scantron.$questnum.answer"}=$currentquest;
     }      }
  } elsif ($$scantron_config{'Qon'} eq 'number') {   } elsif ($$scantron_config{'Qon'} eq 'number') {
     if ($currentquest eq '?') {      if ($currentquest eq '?'
    || $currentquest eq '*') {
  push(@{$record{'scantron.doubleerror'}},$questnum);   push(@{$record{'scantron.doubleerror'}},$questnum);
  $record{"scantron.$questnum.answer"}='';   $record{"scantron.$questnum.answer"}='';
  } elsif (!$currentquest       } elsif (!defined($currentquest)
  || $currentquest eq $$scantron_config{'Qoff'}        || $currentquest eq $$scantron_config{'Qoff'} 
  || $currentquest !~ /^\d$/) {       || $currentquest !~ /^\d$/) {
  $record{"scantron.$questnum.answer"}='';   $record{"scantron.$questnum.answer"}='';
  if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {   if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
     push(@{$record{"scantron.missingerror"}},$questnum);      push(@{$record{"scantron.missingerror"}},$questnum);
  }   }
     } else {      } else {
  $record{"scantron.$questnum.answer"}=   # wrap zero back to J
     $alphabet[$currentquest-1];   if ($currentquest eq '0') {
       $record{"scantron.$questnum.answer"}=
    $alphabet[9];
    } else {
       $record{"scantron.$questnum.answer"}=
    $alphabet[$currentquest-1];
    }
     }      }
  } else {   } else {
     my @array=split($$scantron_config{'Qon'},$currentquest,-1);      my @array=split($$scantron_config{'Qon'},$currentquest,-1);
Line 4571  sub scantron_parse_scanline { Line 5197  sub scantron_parse_scanline {
     return \%record;      return \%record;
 }  }
   
   =pod
   
   =item scantron_add_delay
   
      Adds an error message that occurred during the grading phase to a
      queue of messages to be shown after grading pass is complete
   
    Arguments:
      $delayqueue  - arrary ref of hash ref of error messages
      $scanline    - the scanline that caused the error
      $errormesage - the error message
      $errorcode   - a numeric code for the error
   
    Side Effects:
      updates the $delayqueue to have a new hash ref of the error
   
   =cut
   
 sub scantron_add_delay {  sub scantron_add_delay {
     my ($delayqueue,$scanline,$errormessage,$errorcode)=@_;      my ($delayqueue,$scanline,$errormessage,$errorcode)=@_;
     push(@$delayqueue,      push(@$delayqueue,
Line 4579  sub scantron_add_delay { Line 5223  sub scantron_add_delay {
  );   );
 }  }
   
   =pod
   
   =item scantron_find_student
   
      Finds the username for the current scanline
   
     Arguments:
      $scantron_record - hash result from scantron_parse_scanline
      $scan_data       - hash of correction information 
                         (see &scantron_getfile() form more information)
      $idmap           - hash from &username_to_idmap()
      $line            - number of current scanline
    
     Returns:
      Either 'username:domain' or undef if unknown
   
   =cut
   
 sub scantron_find_student {  sub scantron_find_student {
     my ($scantron_record,$scan_data,$idmap,$line)=@_;      my ($scantron_record,$scan_data,$idmap,$line)=@_;
     my $scanID=$$scantron_record{'scantron.ID'};      my $scanID=$$scantron_record{'scantron.ID'};
Line 4593  sub scantron_find_student { Line 5255  sub scantron_find_student {
     return undef;      return undef;
 }  }
   
   =pod
   
   =item scantron_filter
   
      Filter sub for lonnavmaps, filters out hidden resources if ignore
      hidden resources was selected
   
   =cut
   
 sub scantron_filter {  sub scantron_filter {
     my ($curres)=@_;      my ($curres)=@_;
   
Line 4609  sub scantron_filter { Line 5280  sub scantron_filter {
     return 0;      return 0;
 }  }
   
   =pod
   
   =item scantron_process_corrections
   
      Gets correction information out of submitted form data and corrects
      the scanline
   
   =cut
   
 sub scantron_process_corrections {  sub scantron_process_corrections {
     my ($r) = @_;      my ($r) = @_;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
Line 4659  sub scantron_process_corrections { Line 5339  sub scantron_process_corrections {
  }   }
     }      }
     if ($err) {      if ($err) {
  $r->print("<font color='red'>Unable to accept last correction, an error occurred :$errmsg:</font>");   $r->print("<span class=\"LC_warning\">Unable to accept last correction, an error occurred :$errmsg:</span>");
     } else {      } else {
  &scantron_put_line($scanlines,$scan_data,$which,$line,$skip);   &scantron_put_line($scanlines,$scan_data,$which,$line,$skip);
  &scantron_putfile($scanlines,$scan_data);   &scantron_putfile($scanlines,$scan_data);
     }      }
 }  }
   
   =pod
   
   =item reset_skipping_status
   
      Forgets the current set of remember skipped scanlines (and thus
      reverts back to considering all lines in the
      scantron_skipped_<filename> file)
   
   =cut
   
 sub reset_skipping_status {  sub reset_skipping_status {
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     &scan_data($scan_data,'remember_skipping',undef,1);      &scan_data($scan_data,'remember_skipping',undef,1);
     &scantron_putfile(undef,$scan_data);      &scantron_putfile(undef,$scan_data);
 }  }
   
 sub allow_skipping {  =pod
   
   =item start_skipping
   
      Marks a scanline to be skipped. 
   
   =cut
   
   sub start_skipping {
     my ($scan_data,$i)=@_;      my ($scan_data,$i)=@_;
     my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));      my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
     delete($remembered{$i});      if ($env{'form.scantron_options_redo'} =~ /^redo_/) {
    $remembered{$i}=2;
       } else {
    $remembered{$i}=1;
       }
     &scan_data($scan_data,'remember_skipping',join(':',%remembered));      &scan_data($scan_data,'remember_skipping',join(':',%remembered));
 }  }
   
   =pod
   
   =item should_be_skipped
   
      Checks whether a scanline should be skipped.
   
   =cut
   
 sub should_be_skipped {  sub should_be_skipped {
     my ($scan_data,$i)=@_;      my ($scanlines,$scan_data,$i)=@_;
     if ($env{'form.scantron_options_redo'} !~ /^redo_/) {      if ($env{'form.scantron_options_redo'} !~ /^redo_/) {
  # not redoing old skips   # not redoing old skips
    if ($scanlines->{'skipped'}[$i]) { return 1; }
  return 0;   return 0;
     }      }
     my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));      my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
     if (exists($remembered{$i})) { return 0; }  
       if (exists($remembered{$i}) && $remembered{$i} != 2 ) {
    return 0;
       }
     return 1;      return 1;
 }  }
   
   =pod
   
   =item remember_current_skipped
   
      Discovers what scanlines are in the scantron_skipped_<filename>
      file and remembers them into scan_data for later use.
   
   =cut
   
 sub remember_current_skipped {  sub remember_current_skipped {
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my %to_remember;      my %to_remember;
Line 4698  sub remember_current_skipped { Line 5421  sub remember_current_skipped {
     $to_remember{$i}=1;      $to_remember{$i}=1;
  }   }
     }      }
   
     &scan_data($scan_data,'remember_skipping',join(':',%to_remember));      &scan_data($scan_data,'remember_skipping',join(':',%to_remember));
     &scantron_putfile(undef,$scan_data);      &scantron_putfile(undef,$scan_data);
 }  }
   
   =pod
   
   =item check_for_error
   
       Checks if there was an error when attempting to remove a specific
       scantron_.. bubble sheet data file. Prints out an error if
       something went wrong.
   
   =cut
   
 sub check_for_error {  sub check_for_error {
     my ($r,$result)=@_;      my ($r,$result)=@_;
     if ($result ne 'ok' && $result ne 'not_found' ) {      if ($result ne 'ok' && $result ne 'not_found' ) {
  $r->print("An error occured ($result) when trying to Remove the existing corrections.");   $r->print("An error occurred ($result) when trying to Remove the existing corrections.");
     }      }
 }  }
   
   =pod
   
   =item scantron_warning_screen
   
      Interstitial screen to make sure the operator has selected the
      correct options before we start the validation phase.
   
   =cut
   
 sub scantron_warning_screen {  sub scantron_warning_screen {
     my ($button_text)=@_;      my ($button_text)=@_;
     my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});      my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my $CODElist="a";      my $CODElist;
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
  $scantron_config{'CODElength'}) {   $scantron_config{'CODElength'}) {
  $CODElist=$env{'form.scantron_CODElist'};   $CODElist=$env{'form.scantron_CODElist'};
  if ($CODElist eq '') { $CODElist='<font color="red">None</font>'; }   if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<span class="LC_warning">None</span>'; }
  $CODElist=   $CODElist=
     '<tr><td><b>List of CODES to validate against:</b></td><td><tt>'.      '<tr><td><b>List of CODES to validate against:</b></td><td><tt>'.
     $CODElist.'</tt></td></tr>';      $env{'form.scantron_CODElist'}.'</tt></td></tr>';
     }      }
     return (<<STUFF);      return (<<STUFF);
 <p>  <p>
 <font color="red">Please double check the information  <span class="LC_warning">Please double check the information
                  below before clicking on '$button_text'</font>                   below before clicking on '$button_text'</span>
 </p>  </p>
 <table>  <table>
 <tr><td><b>Sequence to be Graded:</b></td><td>$title</td></tr>  <tr><td><b>Sequence to be Graded:</b></td><td>$title</td></tr>
 <tr><td><b>Data File that will be used:</b></td><td><tt>$env{'form.scantron_selectfile'}</tt></td></tr>  <tr><td><b>Data File that will be used:</b></td><td><tt>$env{'form.scantron_selectfile'}</tt></td></tr>
 $CODElist  $CODElist
 </table>  </table>
 </font>  
 <br />  <br />
 <p> If this information is correct, please click on '$button_text'.</p>  <p> If this information is correct, please click on '$button_text'.</p>
 <p> If something is incorrect, please click the 'Grading Menu' button to start over.</p>  <p> If something is incorrect, please click the 'Grading Menu' button to start over.</p>
Line 4742  $CODElist Line 5484  $CODElist
 STUFF  STUFF
 }  }
   
   =pod
   
   =item scantron_do_warning
   
      Check if the operator has picked something for all required
      fields. Error out if something is missing.
   
   =cut
   
 sub scantron_do_warning {  sub scantron_do_warning {
     my ($r)=@_;      my ($r)=@_;
     my ($symb)=&get_symb($r);      my ($symb)=&get_symb($r);
Line 4753  sub scantron_do_warning { Line 5504  sub scantron_do_warning {
  $env{'form.scantron_format'} eq '' ) {   $env{'form.scantron_format'} eq '' ) {
  $r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>");   $r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>");
  if ( $env{'form.selectpage'} eq '') {   if ( $env{'form.selectpage'} eq '') {
     $r->print('<p><font color="red">You have not selected a Sequence to grade</font></p>');      $r->print('<p><span class="LC_error">You have not selected a Sequence to grade</span></p>');
  }    } 
  if ( $env{'form.scantron_selectfile'} eq '') {   if ( $env{'form.scantron_selectfile'} eq '') {
     $r->print('<p><font color="red">You have not selected a file that contains the student\'s response data.</font></p>');      $r->print('<p><span class="LC_error">You have not selected a file that contains the student\'s response data.</span></p>');
  }    } 
  if ( $env{'form.scantron_format'} eq '') {   if ( $env{'form.scantron_format'} eq '') {
     $r->print('<p><font color="red">You have not selected a the format of the student\'s response data.</font></p>');      $r->print('<p><span class="LC_error">You have not selected a the format of the student\'s response data.</span></p>');
  }    } 
     } else {      } else {
  my $warning=&scantron_warning_screen('Grading: Validate Records');   my $warning=&scantron_warning_screen('Grading: Validate Records');
Line 4769  $warning Line 5520  $warning
 <input type="hidden" name="command" value="scantron_validate" />  <input type="hidden" name="command" value="scantron_validate" />
 STUFF  STUFF
     }      }
     $r->print("</form><br />".&show_grading_menu_form($symb)."</body></html>");      $r->print("</form><br />".&show_grading_menu_form($symb));
     return '';      return '';
 }  }
   
   =pod
   
   =item scantron_form_start
   
       html hidden input for remembering all selected grading options
   
   =cut
   
 sub scantron_form_start {  sub scantron_form_start {
     my ($max_bubble)=@_;      my ($max_bubble)=@_;
     my $result= <<SCANTRONFORM;      my $result= <<SCANTRONFORM;
Line 4790  SCANTRONFORM Line 5549  SCANTRONFORM
     return $result;      return $result;
 }  }
   
   =pod
   
   =item scantron_validate_file
   
       Dispatch routine for doing validation of a bubble sheet data file.
   
       Also processes any necessary information resets that need to
       occur before validation begins (ignore previous corrections,
       restarting the skipped records processing)
   
   =cut
   
 sub scantron_validate_file {  sub scantron_validate_file {
     my ($r) = @_;      my ($r) = @_;
     my ($symb)=&get_symb($r);      my ($symb)=&get_symb($r);
Line 4797  sub scantron_validate_file { Line 5568  sub scantron_validate_file {
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
           
     # do the detection of only doing skipped records first befroe we delete      # do the detection of only doing skipped records first befroe we delete
     # them  when doing the corrections reset      # them when doing the corrections reset
     if ($env{'form.scantron_options_redo'} ne 'redo_skipped_ready') {      if ($env{'form.scantron_options_redo'} ne 'redo_skipped_ready') {
  &reset_skipping_status();   &reset_skipping_status();
     }      }
     if ($env{'form.scantron_options_redo'} eq 'redo_skipped') {      if ($env{'form.scantron_options_redo'} eq 'redo_skipped') {
  &remember_current_skipped();   &remember_current_skipped();
  &scantron_remove_file('skipped');  
  $env{'form.scantron_options_redo'}='redo_skipped_ready';   $env{'form.scantron_options_redo'}='redo_skipped_ready';
     }      }
   
Line 4817  sub scantron_validate_file { Line 5587  sub scantron_validate_file {
     if ($env{'form.scantron_corrections'}) {      if ($env{'form.scantron_corrections'}) {
  &scantron_process_corrections($r);   &scantron_process_corrections($r);
     }      }
     $r->print("<p>Gathering neccessary info.</p>");$r->rflush();      $r->print("<p>Gathering necessary info.</p>");$r->rflush();
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $max_bubble=&scantron_get_maxbubble();      my $max_bubble=&scantron_get_maxbubble();
Line 4870  STUFF Line 5640  STUFF
     $r->print(" this scanline saving it for later.");      $r->print(" this scanline saving it for later.");
  }   }
     }      }
     $r->print(" </form><br />".&show_grading_menu_form($symb).      $r->print(" </form><br />".&show_grading_menu_form($symb));
       "</body></html>");  
     return '';      return '';
 }  }
   
   
   =pod
   
   =item scantron_remove_file
   
      Removes the requested bubble sheet data file, makes sure that
      scantron_original_<filename> is never removed
   
   
   =cut
   
 sub scantron_remove_file {  sub scantron_remove_file {
     my ($which)=@_;      my ($which)=@_;
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
Line 4889  sub scantron_remove_file { Line 5669  sub scantron_remove_file {
     return &Apache::lonnet::removeuserfile($cname,$cdom,$file);      return &Apache::lonnet::removeuserfile($cname,$cdom,$file);
 }  }
   
   
   =pod
   
   =item scantron_remove_scan_data
   
      Removes all scan_data correction for the requested bubble sheet
      data file.  (In the case that both the are doing skipped records we need
      to remember the old skipped lines for the time being so that element
      persists for a while.)
   
   =cut
   
 sub scantron_remove_scan_data {  sub scantron_remove_scan_data {
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
Line 4911  sub scantron_remove_scan_data { Line 5703  sub scantron_remove_scan_data {
     return $result;      return $result;
 }  }
   
   
   =pod
   
   =item scantron_getfile
   
       Fetches the requested bubble sheet data file (all 3 versions), and
       the scan_data hash
     
     Arguments:
       None
   
     Returns:
       2 hash references
   
        - first one has 
            orig      -
            corrected -
            skipped   -  each of which points to an array ref of the specified
                         file broken up into individual lines
            count     - number of scanlines
    
        - second is the scan_data hash possible keys are
          ($number refers to scanline numbered $number and thus the key affects
           only that scanline
           $bubline refers to the specific bubble line element and the aspects
           refers to that specific bubble line element)
   
          $number.user - username:domain to use
          $number.CODE_ignore_dup 
                       - ignore the duplicate CODE error 
          $number.useCODE
                       - use the CODE in the scanline as is
          $number.no_bubble.$bubline
                       - it is valid that there is no bubbled in bubble
                         at $number $bubline
          remember_skipping
                       - a frozen hash containing keys of $number and values
                         of either 
                           1 - we are on a 'do skipped records pass' and plan
                               on processing this line
                           2 - we are on a 'do skipped records pass' and this
                               scanline has been marked to skip yet again
   
   =cut
   
 sub scantron_getfile {  sub scantron_getfile {
     #FIXME really would prefer a scantron directory      #FIXME really would prefer a scantron directory
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
Line 4943  sub scantron_getfile { Line 5780  sub scantron_getfile {
     return (\%scanlines,\%scan_data);      return (\%scanlines,\%scan_data);
 }  }
   
   =pod
   
   =item lonnet_putfile
   
      Wrapper routine to call &Apache::lonnet::finishuserfileupload
   
    Arguments:
      $contents - data to store
      $filename - filename to store $contents into
   
    Returns:
      result value from &Apache::lonnet::finishuserfileupload
   
   =cut
   
 sub lonnet_putfile {  sub lonnet_putfile {
     my ($contents,$filename)=@_;      my ($contents,$filename)=@_;
     my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
Line 4952  sub lonnet_putfile { Line 5804  sub lonnet_putfile {
   
 }  }
   
   =pod
   
   =item scantron_putfile
   
       Stores the current version of the bubble sheet data files, and the
       scan_data hash. (Does not modify the original version only the
       corrected and skipped versions.
   
    Arguments:
       $scanlines - hash ref that looks like the first return value from
                    &scantron_getfile()
       $scan_data - hash ref that looks like the second return value from
                    &scantron_getfile()
   
   =cut
   
 sub scantron_putfile {  sub scantron_putfile {
     my ($scanlines,$scan_data) = @_;      my ($scanlines,$scan_data) = @_;
     #FIXME really would prefer a scantron directory      #FIXME really would prefer a scantron directory
Line 4972  sub scantron_putfile { Line 5840  sub scantron_putfile {
     &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname);      &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname);
 }  }
   
   =pod
   
   =item scantron_get_line
   
      Returns the correct version of the scanline
   
    Arguments:
       $scanlines - hash ref that looks like the first return value from
                    &scantron_getfile()
       $scan_data - hash ref that looks like the second return value from
                    &scantron_getfile()
       $i         - number of the requested line (starts at 0)
   
    Returns:
      A scanline, (either the original or the corrected one if it
      exists), or undef if the requested scanline should be
      skipped. (Either because it's an skipped scanline, or it's an
      unskipped scanline and we are not doing a 'do skipped scanlines'
      pass.
   
   =cut
   
 sub scantron_get_line {  sub scantron_get_line {
     my ($scanlines,$scan_data,$i)=@_;      my ($scanlines,$scan_data,$i)=@_;
     if (&should_be_skipped($scan_data,$i)) { return undef; }      if (&should_be_skipped($scanlines,$scan_data,$i)) { return undef; }
     if ($scanlines->{'skipped'}[$i]) { return undef; }      #if ($scanlines->{'skipped'}[$i]) { return undef; }
     if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];}      if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];}
     return $scanlines->{'orig'}[$i];       return $scanlines->{'orig'}[$i]; 
 }  }
   
   =pod
   
   =item scantron_todo_count
   
       Counts the number of scanlines that need processing.
   
    Arguments:
       $scanlines - hash ref that looks like the first return value from
                    &scantron_getfile()
       $scan_data - hash ref that looks like the second return value from
                    &scantron_getfile()
   
    Returns:
       $count - number of scanlines to process
   
   =cut
   
 sub get_todo_count {  sub get_todo_count {
     my ($scanlines,$scan_data)=@_;      my ($scanlines,$scan_data)=@_;
     my $count=0;      my $count=0;
Line 4991  sub get_todo_count { Line 5898  sub get_todo_count {
     return $count;      return $count;
 }  }
   
   =pod
   
   =item scantron_put_line
   
       Updates the 'corrected' or 'skipped' versions of the bubble sheet
       data file.
   
    Arguments:
       $scanlines - hash ref that looks like the first return value from
                    &scantron_getfile()
       $scan_data - hash ref that looks like the second return value from
                    &scantron_getfile()
       $i         - line number to update
       $newline   - contents of the updated scanline
       $skip      - if true make the line for skipping and update the
                    'skipped' file
   
   =cut
   
 sub scantron_put_line {  sub scantron_put_line {
     my ($scanlines,$scan_data,$i,$newline,$skip)=@_;      my ($scanlines,$scan_data,$i,$newline,$skip)=@_;
     if ($skip) {      if ($skip) {
  $scanlines->{'skipped'}[$i]=$newline;   $scanlines->{'skipped'}[$i]=$newline;
  &allow_skipping($scan_data,$i);   &start_skipping($scan_data,$i);
  return;   return;
     }      }
     $scanlines->{'corrected'}[$i]=$newline;      $scanlines->{'corrected'}[$i]=$newline;
 }  }
   
   =pod
   
   =item scantron_clear_skip
   
      Remove a line from the 'skipped' file
   
    Arguments:
       $scanlines - hash ref that looks like the first return value from
                    &scantron_getfile()
       $scan_data - hash ref that looks like the second return value from
                    &scantron_getfile()
       $i         - line number to update
   
   =cut
   
   sub scantron_clear_skip {
       my ($scanlines,$scan_data,$i)=@_;
       if (exists($scanlines->{'skipped'}[$i])) {
    undef($scanlines->{'skipped'}[$i]);
    return 1;
       }
       return 0;
   }
   
   =pod
   
   =item scantron_filter_not_exam
   
      Filter routine used by &Apache::lonnavmaps::retrieveResources(), to
      filter out resources that are not marked as 'exam' mode
   
   =cut
   
 sub scantron_filter_not_exam {  sub scantron_filter_not_exam {
     my ($curres)=@_;      my ($curres)=@_;
           
Line 5017  sub scantron_filter_not_exam { Line 5976  sub scantron_filter_not_exam {
     return 0;      return 0;
 }  }
   
   =pod
   
   =item scantron_validate_sequence
   
       Validates the selected sequence, checking for resource that are
       not set to exam mode.
   
   =cut
   
 sub scantron_validate_sequence {  sub scantron_validate_sequence {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
   
Line 5032  sub scantron_validate_sequence { Line 6000  sub scantron_validate_sequence {
  my @resources=   my @resources=
     $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);      $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);
  if (@resources) {   if (@resources) {
     $r->print("<p>".&mt('Some resource in the sequece currently are not set to exam mode. Grading these resources currently may not work correctly.')."</p>");      $r->print("<p>".&mt('Some resources in the sequence currently are not set to exam mode. Grading these resources currently may not work correctly.')."</p>");
     return (1,$currentphase);      return (1,$currentphase);
  }   }
     }      }
Line 5040  sub scantron_validate_sequence { Line 6008  sub scantron_validate_sequence {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   =pod
   
   =item scantron_validate_ID
   
      Validates all scanlines in the selected file to not have any
      invalid or underspecified student IDs
   
   =cut
   
 sub scantron_validate_ID {  sub scantron_validate_ID {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
           
Line 5102  sub scantron_validate_ID { Line 6079  sub scantron_validate_ID {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   =pod
   
   =item scantron_get_correction
   
      Builds the interface screen to interact with the operator to fix a
      specific error condition in a specific scanline
   
    Arguments:
       $r           - Apache request object
       $i           - number of the current scanline
       $scan_record - hash ref as returned from &scantron_parse_scanline()
       $scan_config - hash ref as returned from &get_scantron_config()
       $line        - full contents of the current scanline
       $error       - error condition, valid values are
                      'incorrectCODE', 'duplicateCODE',
                      'doublebubble', 'missingbubble',
                      'duplicateID', 'incorrectID'
       $arg         - extra information needed
          For errors:
            - duplicateID   - paper number that this studentID was seen before on
            - duplicateCODE - array ref of the paper numbers this CODE was
                              seen on before
            - incorrectCODE - current incorrect CODE 
            - doublebubble  - array ref of the bubble lines that have double
                              bubble errors
            - missingbubble - array ref of the bubble lines that have missing
                              bubble errors
   
   =cut
   
 sub scantron_get_correction {  sub scantron_get_correction {
     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;      my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;
   
Line 5161  sub scantron_get_correction { Line 6168  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="on" '; }      if (!$i) { $checked=' checked="checked" '; }
     $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> Use the similar CODE <b><tt>".$testcode."</tt></b> instead.</label><input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />");      $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> Use the similar CODE <b><tt>".$testcode."</tt></b> instead.</label><input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />");
     $r->print("\n<br />");      $r->print("\n<br />");
     $i++;      $i++;
Line 5169  sub scantron_get_correction { Line 6176  sub scantron_get_correction {
     }      }
  }   }
  if ($$scan_record{'scantron.CODE'}=~/\S/ ) {   if ($$scan_record{'scantron.CODE'}=~/\S/ ) {
     my $checked; if (!$i) { $checked=' checked="on" '; }      my $checked; if (!$i) { $checked=' checked="checked" '; }
     $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> Use the CODE <b><tt>".$$scan_record{'scantron.CODE'}."</tt></b> that is was on the paper, ignoring the error.</label>");      $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> Use the CODE <b><tt>".$$scan_record{'scantron.CODE'}."</tt></b> that is was on the paper, ignoring the error.</label>");
     $r->print("\n<br />");      $r->print("\n<br />");
  }   }
Line 5186  function change_radio(field) { Line 6193  function change_radio(field) {
 </script>  </script>
 ENDSCRIPT  ENDSCRIPT
  my $href="/adm/pickcode?".   my $href="/adm/pickcode?".
    "form=".&Apache::lonnet::escape("scantronupload").     "form=".&escape("scantronupload").
    "&scantron_format=".&Apache::lonnet::escape($env{'form.scantron_format'}).     "&scantron_format=".&escape($env{'form.scantron_format'}).
    "&scantron_CODElist=".&Apache::lonnet::escape($env{'form.scantron_CODElist'}).     "&scantron_CODElist=".&escape($env{'form.scantron_CODElist'}).
    "&curCODE=".&Apache::lonnet::escape($$scan_record{'scantron.CODE'}).     "&curCODE=".&escape($$scan_record{'scantron.CODE'}).
    "&scantron_selectfile=".&Apache::lonnet::escape($env{'form.scantron_selectfile'});     "&scantron_selectfile=".&escape($env{'form.scantron_selectfile'});
  if ($env{'form.scantron_CODElist'} =~ /\S/) {    if ($env{'form.scantron_CODElist'} =~ /\S/) { 
     $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it.</label> Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />");      $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it.</label> Selected CODE is <input readonly='true' 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 />");
Line 5205  ENDSCRIPT Line 6212  ENDSCRIPT
  $r->print("<p>Please indicate which bubble should be used for grading</p>");   $r->print("<p>Please indicate which bubble should be used for grading</p>");
  foreach my $question (@{$arg}) {   foreach my $question (@{$arg}) {
     my $selected=$$scan_record{"scantron.$question.answer"};      my $selected=$$scan_record{"scantron.$question.answer"};
     &scantron_bubble_selector($r,$scan_config,$question,split('',$selected));      &scantron_bubble_selector($r,$scan_config,$question,
         split('',$selected));
  }   }
     } elsif ($error eq 'missingbubble') {      } elsif ($error eq 'missingbubble') {
  $r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n");   $r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n");
Line 5225  ENDSCRIPT Line 6233  ENDSCRIPT
   
 }  }
   
   =pod
   
   =item scantron_bubble_selector
     
      Generates the html radiobuttons to correct a single bubble line
      possibly showing the existing the selected bubbles if known
   
    Arguments:
       $r           - Apache request object
       $scan_config - hash from &get_scantron_config()
       $quest       - number of the bubble line to make a corrector for
       $selected    - array of letters of previously selected bubbles
       $lines       - if present, number of bubble lines to show
   
   =cut
   
 sub scantron_bubble_selector {  sub scantron_bubble_selector {
     my ($r,$scan_config,$quest,@selected)=@_;      my ($r,$scan_config,$quest,@selected, $lines)=@_;
     my $max=$$scan_config{'Qlength'};      my $max=$$scan_config{'Qlength'};
   
     my $scmode=$$scan_config{'Qon'};      my $scmode=$$scan_config{'Qon'};
     if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; }           if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; }     
   
     my @alphabet=('A'..'Z');  
     $r->print("<table border='1'><tr><td rowspan='2'>$quest</td>");      if (!defined($lines)) {
     for (my $i=0;$i<$max+1;$i++) {   $lines = 1;
  $r->print("\n".'<td align="center">');  
  if ($selected[0] eq $alphabet[$i]) { $r->print('X'); shift(@selected) }  
  else { $r->print('&nbsp;'); }  
  $r->print('</td>');  
     }  
     $r->print('</tr><tr>');  
     for (my $i=0;$i<$max;$i++) {  
  $r->print("\n".  
   '<td><label><input type="radio" name="scantron_correct_Q_'.  
   $quest.'" value="'.$i.'" />'.$alphabet[$i]."</label></td>");  
     }      }
     $r->print('<td><label><input type="radio" name="scantron_correct_Q_'.      my $total_lines = $lines*2;
       my @alphabet=('A'..'Z');
       $r->print("<table border='1'><tr><td rowspan='".$total_lines."'>$quest</td>");
   
       for (my $l = 0; $l < $lines; $l++) {
    if ($l != 0) {
       $r->print('<tr>');
    }
   
    # FIXME:  This loop probably has to be considerably more clever for
    #  multiline bubbles: User can multibubble by having bubbles in
    #  several lines.  User can skip lines legitimately etc. etc.
   
    for (my $i=0;$i<$max;$i++) {
       $r->print("\n".'<td align="center">');
       if ($selected[0] eq $alphabet[$i]) { 
    $r->print('X'); 
    shift(@selected) ;
       } else { 
    $r->print('&nbsp;'); 
       }
       $r->print('</td>');
       
    }
   
    if ($l == 0) {
       my $lspan = $total_lines * 2;   #  2 table rows per bubble line.
   
       $r->print('<td rowspan='.$lspan.'><label><input type="radio" name="scantron_correct_Q_'.
       $quest.'" value="none" /> No bubble </label></td>');        $quest.'" value="none" /> No bubble </label></td>');
     $r->print('</tr></table>');  
    }
   
    $r->print('</tr><tr>');
   
    # FIXME: This may have to be a bit more clever for
    #        multiline questions (different values e.g..).
   
    for (my $i=0;$i<$max;$i++) {
       $r->print("\n".
         '<td><label><input type="radio" name="scantron_correct_Q_'.
         $quest.'" value="'.$i.'" />'.$alphabet[$i]."</label></td>");
    }
    $r->print('</tr>');
   
       
       }
       $r->print('</table>');
 }  }
   
   =pod
   
   =item num_matches
   
      Counts the number of characters that are the same between the two arguments.
   
    Arguments:
      $orig - CODE from the scanline
      $code - CODE to match against
   
    Returns:
      $count - integer count of the number of same characters between the
               two arguments
   
   =cut
   
 sub num_matches {  sub num_matches {
     my ($orig,$code) = @_;      my ($orig,$code) = @_;
     my @code=split(//,$code);      my @code=split(//,$code);
Line 5262  sub num_matches { Line 6337  sub num_matches {
     return $same;      return $same;
 }  }
   
   =pod
   
   =item scantron_get_closely_matching_CODEs
   
      Cycles through all CODEs and finds the set that has the greatest
      number of same characters as the provided CODE
   
    Arguments:
      $allcodes - hash ref returned by &get_codes()
      $CODE     - CODE from the current scanline
   
    Returns:
      2 element list
       - first elements is number of how closely matching the best fit is 
         (5 means best set has 5 matching characters)
       - second element is an arrary ref containing the set of valid CODEs
         that best fit the passed in CODE
   
   =cut
   
 sub scantron_get_closely_matching_CODEs {  sub scantron_get_closely_matching_CODEs {
     my ($allcodes,$CODE)=@_;      my ($allcodes,$CODE)=@_;
     my @CODEs;      my @CODEs;
Line 5272  sub scantron_get_closely_matching_CODEs Line 6367  sub scantron_get_closely_matching_CODEs
     return ($#CODEs,$CODEs[-1]);      return ($#CODEs,$CODEs[-1]);
 }  }
   
   =pod
   
   =item get_codes
   
      Builds a hash which has keys of all of the valid CODEs from the selected
      set of remembered CODEs.
   
    Arguments:
     $old_name - name of the set of remembered CODEs
     $cdom     - domain of the course
     $cnum     - internal course name
   
    Returns:
     %allcodes - keys are the valid CODEs, values are all 1
   
   =cut
   
 sub get_codes {  sub get_codes {
     my ($old_name, $cdom, $cnum) = @_;      my ($old_name, $cdom, $cnum) = @_;
     if (!$old_name) {      if (!$old_name) {
Line 5294  sub get_codes { Line 6406  sub get_codes {
     return %allcodes;      return %allcodes;
 }  }
   
   =pod
   
   =item scantron_validate_CODE
   
      Validates all scanlines in the selected file to not have any
      invalid or underspecified CODEs and that none of the codes are
      duplicated if this was requested.
   
   =cut
   
 sub scantron_validate_CODE {  sub scantron_validate_CODE {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
Line 5345  sub scantron_validate_CODE { Line 6467  sub scantron_validate_CODE {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   =pod
   
   =item scantron_validate_doublebubble
   
      Validates all scanlines in the selected file to not have any
      bubble lines with multiple bubbles marked.
   
   =cut
   
 sub scantron_validate_doublebubble {  sub scantron_validate_doublebubble {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     #get student info      #get student info
Line 5368  sub scantron_validate_doublebubble { Line 6499  sub scantron_validate_doublebubble {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   =pod
   
   =item scantron_get_maxbubble
   
      Returns the maximum number of bubble lines that are expected to
      occur. Does this by walking the selected sequence rendering the
      resource and then checking &Apache::lonxml::get_problem_counter()
      for what the current value of the problem counter is.
   
      Caches the result to $env{'form.scantron_maxbubble'}
   
   =cut
   
 sub scantron_get_maxbubble {      sub scantron_get_maxbubble {    
   
     if (defined($env{'form.scantron_maxbubble'}) &&      if (defined($env{'form.scantron_maxbubble'}) &&
  $env{'form.scantron_maxbubble'}) {   $env{'form.scantron_maxbubble'}) {
  return $env{'form.scantron_maxbubble'};   return $env{'form.scantron_maxbubble'};
Line 5383  sub scantron_get_maxbubble { Line 6528  sub scantron_get_maxbubble {
   
     &Apache::lonxml::clear_problem_counter();      &Apache::lonxml::clear_problem_counter();
   
       my $uname       = $env{'form.student'};
       my $udom        = $env{'form.userdom'};
       my $cid         = $env{'request.course.id'};
       my $total_lines = 0;
       %bubble_lines_per_response = ();
   
     foreach my $resource (@resources) {      foreach my $resource (@resources) {
    my $symb = $resource->symb();
  my $result=&Apache::lonnet::ssi($resource->src(),   my $result=&Apache::lonnet::ssi($resource->src(),
  ('symb' => $resource->symb()));   ('symb' => $resource->symb()),
    ('grade_target' => 'analyze'),
    ('grade_courseid' => $cid),
    ('grade_domain' => $udom),
    ('grade_username' => $uname));
    my (undef, $an) =
       split(/_HASH_REF__/,$result, 2);
   
    my %analysis = &Apache::lonnet::str2hash($an);
   
   
   
    foreach my $part_id (@{$analysis{'parts'}}) {
       my $bubble_lines = $analysis{"$part_id.bubble_lines"}[0];
       if (!$bubble_lines) {
    $bubble_lines = 1;
       }
       $bubble_lines_per_response{"$symb.$part_id"} = $bubble_lines;
       $total_lines = $total_lines + $bubble_lines;
    }
   
     }      }
     &Apache::lonnet::delenv('scantron\.');      &Apache::lonnet::delenv('scantron\.');
     $env{'form.scantron_maxbubble'} =      $env{'form.scantron_maxbubble'} =
  &Apache::lonxml::get_problem_counter()-1;   $total_lines;
   
     return $env{'form.scantron_maxbubble'};      return $env{'form.scantron_maxbubble'};
 }  }
   
   =pod
   
   =item scantron_validate_missingbubbles
   
      Validates all scanlines in the selected file to not have any
      bubble lines with missing bubbles that haven't been verified as missing.
   
   =cut
   
 sub scantron_validate_missingbubbles {  sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     #get student info      #get student info
Line 5426  sub scantron_validate_missingbubbles { Line 6606  sub scantron_validate_missingbubbles {
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   =pod
   
   =item scantron_process_students
   
      Routine that does the actual grading of the bubble sheet information.
   
      The parsed scanline hash is added to %env 
   
      Then foreach unskipped scanline it does an &Apache::lonnet::ssi()
      foreach resource , with the form data of
   
    'submitted'     =>'scantron' 
    'grade_target'  =>'grade',
    'grade_username'=> username of student
    'grade_domain'  => domain of student
    'grade_courseid'=> of course
    'grade_symb'    => symb of resource to grade
   
       This triggers a grading pass. The problem grading code takes care
       of converting the bubbled letter information (now in %env) into a
       valid submission.
   
   =cut
   
 sub scantron_process_students {  sub scantron_process_students {
     my ($r) = @_;      my ($r) = @_;
     my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});      my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
Line 5487  SCANTRONFORM Line 6691  SCANTRONFORM
   
  &Apache::lonxml::clear_problem_counter();   &Apache::lonxml::clear_problem_counter();
   &Apache::lonnet::appenv(%$scan_record);    &Apache::lonnet::appenv(%$scan_record);
   
    if (&scantron_clear_skip($scanlines,$scan_data,$i)) {
       &scantron_putfile($scanlines,$scan_data);
    }
   
  my $i=0;   my $i=0;
  foreach my $resource (@resources) {   foreach my $resource (@resources) {
Line 5497  SCANTRONFORM Line 6705  SCANTRONFORM
       'grade_domain'  =>$udom,        'grade_domain'  =>$udom,
       'grade_courseid'=>$env{'request.course.id'},        'grade_courseid'=>$env{'request.course.id'},
       'grade_symb'    =>$resource->symb());        'grade_symb'    =>$resource->symb());
     if (exists($scan_record->{'scantron.CODE'}) &&      if (exists($scan_record->{'scantron.CODE'})
  $scan_record->{'scantron.CODE'}) {   && 
    &Apache::lonnet::validCODE($scan_record->{'scantron.CODE'})) {
  $form{'CODE'}=$scan_record->{'scantron.CODE'};   $form{'CODE'}=$scan_record->{'scantron.CODE'};
     } else {      } else {
  $form{'CODE'}='';   $form{'CODE'}='';
Line 5525  SCANTRONFORM Line 6734  SCANTRONFORM
     return '';      return '';
 }  }
   
   =pod
   
   =item scantron_upload_scantron_data
   
       Creates the screen for adding a new bubble sheet data file to a course.
   
   =cut
   
 sub scantron_upload_scantron_data {  sub scantron_upload_scantron_data {
     my ($r)=@_;      my ($r)=@_;
     $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));      $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));
Line 5561  UPLOAD Line 6778  UPLOAD
     return '';      return '';
 }  }
   
   =pod
   
   =item scantron_upload_scantron_data_save
   
      Adds a provided bubble information data file to the course if user
      has the correct privileges to do so.  
   
   =cut
   
 sub scantron_upload_scantron_data_save {  sub scantron_upload_scantron_data_save {
     my($r)=@_;      my($r)=@_;
     my ($symb)=&get_symb($r,1);      my ($symb)=&get_symb($r,1);
Line 5599  sub scantron_upload_scantron_data_save { Line 6825  sub scantron_upload_scantron_data_save {
     my $uploadedfile=$fname;      my $uploadedfile=$fname;
     $fname='scantron_orig_'.$fname;      $fname='scantron_orig_'.$fname;
     if (length($env{'form.upfile'}) < 2) {      if (length($env{'form.upfile'}) < 2) {
  $r->print("<font color='red'>Error:</font> The file you attempted to upload, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename.");   $r->print("<span class=\"LC_error\">Error:</span> The file you attempted to upload, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename.");
     } else {      } else {
  my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);   my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);
  if ($result =~ m|^/uploaded/|) {   if ($result =~ m|^/uploaded/|) {
     $r->print("<font color='green'>Success:</font> Successfully uploaded ".(length($env{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>");      $r->print("<span class=\"LC_success\">Success:</span> Successfully uploaded ".(length($env{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>");
  } else {   } else {
     $r->print("<font color='red'>Error:</font> An error (".$result.") occurred when attempting to upload the file, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>");      $r->print("<span class=\"LC_error\">Error:</span> An error (".$result.") occurred when attempting to upload the file, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>");
  }   }
     }      }
     if ($symb) {      if ($symb) {
Line 5616  sub scantron_upload_scantron_data_save { Line 6842  sub scantron_upload_scantron_data_save {
     return '';      return '';
 }  }
   
   =pod
   
   =item valid_file
   
      Validates that the requested bubble data file exists in the course.
   
   =cut
   
 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 5624  sub valid_file { Line 6858  sub valid_file {
     return 0;      return 0;
 }  }
   
   =pod
   
   =item scantron_download_scantron_data
   
      Shows a list of the three internal files (original, corrected,
      skipped) for a specific bubble sheet data file that exists in the
      course.
   
   =cut
   
 sub scantron_download_scantron_data {  sub scantron_download_scantron_data {
     my ($r)=@_;      my ($r)=@_;
     my $default_form_data=&defaultFormData(&get_symb($r,1));      my $default_form_data=&defaultFormData(&get_symb($r,1));
Line 5660  DOWNLOAD Line 6904  DOWNLOAD
     return '';      return '';
 }  }
   
   =pod
   
   =back
   
   =cut
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
Line 5670  DOWNLOAD Line 6920  DOWNLOAD
 sub show_grading_menu_form {  sub show_grading_menu_form {
     my ($symb)=@_;      my ($symb)=@_;
     my $result.='<br /><form action="/adm/grades" method="post">'."\n".      my $result.='<br /><form action="/adm/grades" method="post">'."\n".
  '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".   '<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".
  '<input type="hidden" name="command" value="gradingmenu" />'."\n".   '<input type="hidden" name="command" value="gradingmenu" />'."\n".
  '<input type="submit" name="submit" value="Grading Menu" />'."\n".   '<input type="submit" name="submit" value="Grading Menu" />'."\n".
Line 5734  sub gradingmenu { Line 6984  sub gradingmenu {
 </script>  </script>
 GRADINGMENUJS  GRADINGMENUJS
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my $result='<h3>&nbsp;<font color="#339933">Manual Grading/View Submission</font></h3>';      my $result='<h3>&nbsp;<span class="LC_info">Manual Grading/View Submission</span></h3>';
     my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);      my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);
     $result.=$table;      $result.=$table;
     my (undef,$sections) = &getclasslist('all','0');      my (undef,$sections) = &getclasslist('all','0');
Line 5745  GRADINGMENUJS Line 6995  GRADINGMENUJS
     my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});      my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
  '<input type="hidden" name="symb"        value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".   '<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".
  '<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".   '<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".
  '<input type="hidden" name="command"     value="" />'."\n".   '<input type="hidden" name="command"     value="" />'."\n".
Line 5760  GRADINGMENUJS Line 7010  GRADINGMENUJS
   
     $result.='<table width="100%" border="0">';      $result.='<table width="100%" border="0">';
     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n".      $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n".
  '&nbsp;'.&mt('Select Section').': <select name="section">'."\n";   '&nbsp;'.&mt('Select Section').': <select name="section" multiple="multiple" size="3">'."\n";
     if (ref($sections)) {      if (ref($sections)) {
  foreach (sort (@$sections)) {   foreach (sort (@$sections)) {
     $result.='<option value="'.$_.'" '.      $result.='<option value="'.$_.'" '.
  ($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n";   ($saveSec eq $_ ? 'selected="selected"':'').'>'.$_.'</option>'."\n";
  }   }
     }      }
     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</option></select> &nbsp; ';      $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="selected"' : ''). '>all</option></select> &nbsp; ';
   
     $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);      $result.=&mt('Student Status').':'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
   
     $result.='</td></tr>';      $result.='</td></tr>';
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td><label>'.      $result.='<tr bgcolor="#ffffe6"valign="top"><td><label>'.
  '<input type="radio" name="radioChoice" value="submission" '.   '<input type="radio" name="radioChoice" value="submission" '.
  ($saveCmd eq 'submission' ? 'checked' : '').' /> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').   ($saveCmd eq 'submission' ? 'checked="checked"' : '').' /> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').
  '</label> <select name="submitonly">'.   '</label> <select name="submitonly">'.
  '<option value="yes" '.   '<option value="yes" '.
  ($saveSub eq 'yes' ? 'selected="on"' : '').' />'.&mt('with submissions').'</option>'.   ($saveSub eq 'yes' ? 'selected="selected"' : '').'>'.&mt('with submissions').'</option>'.
  '<option value="queued" '.   '<option value="queued" '.
  ($saveSub eq 'queued' ? 'selected="on"' : '').' />'.&mt('in grading queue').'</option>'.   ($saveSub eq 'queued' ? 'selected="selected"' : '').'>'.&mt('in grading queue').'</option>'.
  '<option value="graded" '.   '<option value="graded" '.
  ($saveSub eq 'graded' ? 'selected="on"' : '').' />'.&mt('with ungraded submissions').'</option>'.   ($saveSub eq 'graded' ? 'selected="selected"' : '').'>'.&mt('with ungraded submissions').'</option>'.
  '<option value="incorrect" '.   '<option value="incorrect" '.
  ($saveSub eq 'incorrect' ? 'selected="on"' : '').' />'.&mt('with incorrect submissions').'</option>'.   ($saveSub eq 'incorrect' ? 'selected="selected"' : '').'>'.&mt('with incorrect submissions').'</option>'.
  '<option value="all" '.   '<option value="all" '.
  ($saveSub eq 'all' ? 'selected="on"' : '').' />'.&mt('with any status').'</option></select></td></tr>'."\n";   ($saveSub eq 'all' ? 'selected="selected"' : '').'>'.&mt('with any status').'</option></select></td></tr>'."\n";
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.      $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
  '<label><input type="radio" name="radioChoice" value="viewgrades" '.   '<label><input type="radio" name="radioChoice" value="viewgrades" '.
  ($saveCmd eq 'viewgrades' ? 'checked' : '').' /> '.   ($saveCmd eq 'viewgrades' ? 'checked="checked"' : '').' /> '.
  '<b>Current Resource:</b> For all students in selected section or course</label></td></tr>'."\n";   '<b>Current Resource:</b> For all students in selected section or course</label></td></tr>'."\n";
   
     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'.      $result.='<tr bgcolor="#ffffe6" valign="top"><td>'.
  '<label><input type="radio" name="radioChoice" value="pickStudentPage" '.   '<label><input type="radio" name="radioChoice" value="pickStudentPage" '.
  ($saveCmd eq 'pickStudentPage' ? 'checked' : '').' /> '.   ($saveCmd eq 'pickStudentPage' ? 'checked="checked"' : '').' /> '.
  'The <b>complete</b> set/page/sequence: For one student</label></td></tr>'."\n";   'The <b>complete</b> set/page/sequence: For one student</label></td></tr>'."\n";
   
     $result.='<tr bgcolor="#ffffe6"><td><br />'.      $result.='<tr bgcolor="#ffffe6"><td><br />'.
Line 5809  GRADINGMENUJS Line 7059  GRADINGMENUJS
  '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.   '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.
  ' '.&mt('scores from file').' </td></tr>'."\n";   ' '.&mt('scores from file').' </td></tr>'."\n";
   
       $result.='<tr bgcolor="#ffffe6"><td>'.
           '<input type="button" onClick="javascript:checkChoice(this.form,\'6\',\'processclicker\');" value="'.&mt('Process').'" />'.
           ' '.&mt('clicker file').' </td></tr>'."\n";
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.      $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
  '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.   '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.
  '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";   '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";
Line 5828  GRADINGMENUJS Line 7082  GRADINGMENUJS
  '<input type="button" onClick="javascript:this.form.command.value=\'codelist\';this.form.action=\'/adm/pickcode\';this.form.submit();'.   '<input type="button" onClick="javascript:this.form.command.value=\'codelist\';this.form.action=\'/adm/pickcode\';this.form.submit();'.
  '" value="'.&mt('View').'" /> saved CODEs.</td></tr>'."\n";   '" value="'.&mt('View').'" /> saved CODEs.</td></tr>'."\n";
   
     $result.='</form></td></tr></table>'."\n".      $result.='</table>'."\n".
  '</td></tr></table>'."\n".   '</td></tr></table>'."\n".
  '</td></tr></table>'."\n";   '</td></tr></table></form>'."\n";
     return $result;      return $result;
 }  }
   
Line 5856  sub init_perm { Line 7110  sub init_perm {
     }      }
 }  }
   
   sub gather_clicker_ids {
       my %clicker_ids;
   
       my $classlist = &Apache::loncoursedata::get_classlist();
   
       # Set up a couple variables.
       my $username_idx = &Apache::loncoursedata::CL_SNAME();
       my $domain_idx   = &Apache::loncoursedata::CL_SDOM();
   
       foreach my $student (keys(%$classlist)) {
   
           my $username = $classlist->{$student}->[$username_idx];
           my $domain   = $classlist->{$student}->[$domain_idx];
           my $clickers =
       (&Apache::lonnet::userenvironment($domain,$username,'clickers'))[1];
           foreach my $id (split(/\,/,$clickers)) {
               $id=~s/^[\#0]+//;
               $id=~s/[\-\:]//g;
               if (exists($clicker_ids{$id})) {
    $clicker_ids{$id}.=','.$username.':'.$domain;
               } else {
    $clicker_ids{$id}=$username.':'.$domain;
               }
           }
       }
       return %clicker_ids;
   }
   
   sub gather_adv_clicker_ids {
       my %clicker_ids;
       my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my %coursepersonnel=&Apache::lonnet::get_course_adv_roles($cdom.'/'.$cnum);
       foreach my $element (sort(keys(%coursepersonnel))) {
           foreach my $person (split(/\,/,$coursepersonnel{$element})) {
               my ($puname,$pudom)=split(/\:/,$person);
               my $clickers =
    (&Apache::lonnet::userenvironment($pudom,$puname,'clickers'))[1];
               foreach my $id (split(/\,/,$clickers)) {
    $id=~s/^[\#0]+//;
                   $id=~s/[\-\:]//g;
    if (exists($clicker_ids{$id})) {
       $clicker_ids{$id}.=','.$puname.':'.$pudom;
    } else {
       $clicker_ids{$id}=$puname.':'.$pudom;
    }
               }
           }
       }
       return %clicker_ids;
   }
   
   sub clicker_grading_parameters {
       return ('gradingmechanism' => 'scalar',
               'upfiletype' => 'scalar',
               'specificid' => 'scalar',
               'pcorrect' => 'scalar',
               'pincorrect' => 'scalar');
   }
   
   sub process_clicker {
       my ($r)=@_;
       my ($symb)=&get_symb($r);
       if (!$symb) {return '';}
       my $result=&checkforfile_js();
       $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
       my ($table) = &showResourceInfo($symb,$env{'form.probTitle'});
       $result.=$table;
       $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
       $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
       $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource').
           '.</b></td></tr>'."\n";
       $result.='<tr bgcolor=#ffffe6><td>'."\n";
   # Attempt to restore parameters from last session, set defaults if not present
       my %Saveable_Parameters=&clicker_grading_parameters();
       &Apache::loncommon::restore_course_settings('grades_clicker',
                                                    \%Saveable_Parameters);
       if (!$env{'form.pcorrect'}) { $env{'form.pcorrect'}=100; }
       if (!$env{'form.pincorrect'}) { $env{'form.pincorrect'}=100; }
       if (!$env{'form.gradingmechanism'}) { $env{'form.gradingmechanism'}='attendance'; }
       if (!$env{'form.upfiletype'}) { $env{'form.upfiletype'}='iclicker'; }
   
       my %checked;
       foreach my $gradingmechanism ('attendance','personnel','specific') {
          if ($env{'form.gradingmechanism'} eq $gradingmechanism) {
             $checked{$gradingmechanism}="checked='checked'";
          }
       }
   
       my $upload=&mt("Upload File");
       my $type=&mt("Type");
       my $attendance=&mt("Award points just for participation");
       my $personnel=&mt("Correctness determined from response by course personnel");
       my $specific=&mt("Correctness determined from response with clicker ID(s)"); 
       my $pcorrect=&mt("Percentage points for correct solution");
       my $pincorrect=&mt("Percentage points for incorrect solution");
       my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',
      ('iclicker' => 'i>clicker',
                                                       'interwrite' => 'interwrite PRS'));
       $symb = &Apache::lonenc::check_encrypt($symb);
       $result.=<<ENDUPFORM;
   <script type="text/javascript">
   function sanitycheck() {
   // Accept only integer percentages
      document.forms.gradesupload.pcorrect.value=Math.round(document.forms.gradesupload.pcorrect.value);
      document.forms.gradesupload.pincorrect.value=Math.round(document.forms.gradesupload.pincorrect.value);
   // Find out grading choice
      for (i=0; i<document.forms.gradesupload.gradingmechanism.length; i++) {
         if (document.forms.gradesupload.gradingmechanism[i].checked) {
            gradingchoice=document.forms.gradesupload.gradingmechanism[i].value;
         }
      }
   // By default, new choice equals user selection
      newgradingchoice=gradingchoice;
   // Not good to give more points for false answers than correct ones
      if (Math.round(document.forms.gradesupload.pcorrect.value)<Math.round(document.forms.gradesupload.pincorrect.value)) {
         document.forms.gradesupload.pcorrect.value=document.forms.gradesupload.pincorrect.value;
      }
   // If new choice is attendance only, and old choice was correctness-based, restore defaults
      if ((gradingchoice=='attendance') && (document.forms.gradesupload.waschecked.value!='attendance')) {
         document.forms.gradesupload.pcorrect.value=100;
         document.forms.gradesupload.pincorrect.value=100;
      }
   // If the values are different, cannot be attendance only
      if ((Math.round(document.forms.gradesupload.pcorrect.value)!=Math.round(document.forms.gradesupload.pincorrect.value)) &&
          (gradingchoice=='attendance')) {
          newgradingchoice='personnel';
      }
   // Change grading choice to new one
      for (i=0; i<document.forms.gradesupload.gradingmechanism.length; i++) {
         if (document.forms.gradesupload.gradingmechanism[i].value==newgradingchoice) {
            document.forms.gradesupload.gradingmechanism[i].checked=true;
         } else {
            document.forms.gradesupload.gradingmechanism[i].checked=false;
         }
      }
   // Remember the old state
      document.forms.gradesupload.waschecked.value=newgradingchoice;
   }
   </script>
   <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
   <input type="hidden" name="symb" value="$symb" />
   <input type="hidden" name="command" value="processclickerfile" />
   <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
   <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
   <input type="file" name="upfile" size="50" />
   <br /><label>$type: $selectform</label>
   <br /><label>$attendance: <input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" /></label>
   <br /><label>$personnel: <input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" /></label>
   <br /><label>$specific: <input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" /></label>
   <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />
   <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
   <br /><label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onChange="sanitycheck()" /></label>
   <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onChange="sanitycheck()" /></label>
   <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="$upload" />
   </form>
   ENDUPFORM
       $result.='</td></tr></table>'."\n".
                '</td></tr></table><br /><br />'."\n";
       $result.=&show_grading_menu_form($symb);
       return $result;
   }
   
   sub process_clicker_file {
       my ($r)=@_;
       my ($symb)=&get_symb($r);
       if (!$symb) {return '';}
   
       my %Saveable_Parameters=&clicker_grading_parameters();
       &Apache::loncommon::store_course_settings('grades_clicker',
                                                 \%Saveable_Parameters);
   
       my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
       if (($env{'form.gradingmechanism'} eq 'specific') && ($env{'form.specificid'}!~/\w/)) {
    $result.='<span class="LC_error">'.&mt('You need to specify a clicker ID for the correct answer').'</span>';
    return $result.&show_grading_menu_form($symb);
       }
       my %clicker_ids=&gather_clicker_ids();
       my %correct_ids;
       if ($env{'form.gradingmechanism'} eq 'personnel') {
    %correct_ids=&gather_adv_clicker_ids();
       }
       if ($env{'form.gradingmechanism'} eq 'specific') {
    foreach my $correct_id (split(/[\s\,]/,$env{'form.specificid'})) {;
      $correct_id=~tr/a-z/A-Z/;
      $correct_id=~s/\s//gs;
      $correct_id=~s/^[\#0]+//;
              $correct_id=~s/[\-\:]//g;
              if ($correct_id) {
         $correct_ids{$correct_id}='specified';
              }
           }
       }
       if ($env{'form.gradingmechanism'} eq 'attendance') {
    $result.=&mt('Score based on attendance only');
       } else {
    my $number=0;
    $result.='<p><b>'.&mt('Correctness determined by the following IDs').'</b>';
    foreach my $id (sort(keys(%correct_ids))) {
       $result.='<br /><tt>'.$id.'</tt> - ';
       if ($correct_ids{$id} eq 'specified') {
    $result.=&mt('specified');
       } else {
    my ($uname,$udom)=split(/\:/,$correct_ids{$id});
    $result.=&Apache::loncommon::plainname($uname,$udom);
       }
       $number++;
    }
           $result.="</p>\n";
    if ($number==0) {
       $result.='<span class="LC_error">'.&mt('No IDs found to determine correct answer').'</span>';
       return $result.&show_grading_menu_form($symb);
    }
       }
       if (length($env{'form.upfile'}) < 2) {
           $result.=&mt('[_1] Error: [_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.',
        '<span class="LC_error">',
        '</span>',
        '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>');
           return $result.&show_grading_menu_form($symb);
       }
   
   # Were able to get all the info needed, now analyze the file
   
       $result.=&Apache::loncommon::studentbrowser_javascript();
       $symb = &Apache::lonenc::check_encrypt($symb);
       my $heading=&mt('Scanning clicker file');
       $result.=(<<ENDHEADER);
   <br /><table width="100%" border="0"><tr><td bgcolor="#777777">
   <table width="100%" border="0"><tr bgcolor="#e6ffff"><td>
   <b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>
   <form method="post" action="/adm/grades" name="clickeranalysis">
   <input type="hidden" name="symb" value="$symb" />
   <input type="hidden" name="command" value="assignclickergrades" />
   <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
   <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
   <input type="hidden" name="gradingmechanism" value="$env{'form.gradingmechanism'}" />
   <input type="hidden" name="pcorrect" value="$env{'form.pcorrect'}" />
   <input type="hidden" name="pincorrect" value="$env{'form.pincorrect'}" />
   ENDHEADER
       my %responses;
       my @questiontitles;
       my $errormsg='';
       my $number=0;
       if ($env{'form.upfiletype'} eq 'iclicker') {
    ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);
       }
       if ($env{'form.upfiletype'} eq 'interwrite') {
           ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
       }
       $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.
                '<input type="hidden" name="number" value="'.$number.'" />'.
                &mt('Awarding [_1] percent for correct and [_2] percent for incorrect responses',
                    $env{'form.pcorrect'},$env{'form.pincorrect'}).
                '<br />';
   # Remember Question Titles
   # FIXME: Possibly need delimiter other than ":"
       for (my $i=0;$i<$number;$i++) {
           $result.='<input type="hidden" name="question:'.$i.'" value="'.
                    &HTML::Entities::encode($questiontitles[$i],'"&<>').'" />';
       }
       my $correct_count=0;
       my $student_count=0;
       my $unknown_count=0;
   # Match answers with usernames
   # FIXME: Possibly need delimiter other than ":"
       foreach my $id (keys(%responses)) {
          if ($correct_ids{$id}) {
             $result.="\n".'<input type="hidden" name="correct:'.$correct_count.':'.$correct_ids{$id}.'" value="'.$responses{$id}.'" />';
             $correct_count++;
          } elsif ($clicker_ids{$id}) {
             $result.="\n".'<input type="hidden" name="student:'.$clicker_ids{$id}.'" value="'.$responses{$id}.'" />';
             $student_count++;
          } else {
             $result.="\n<hr />".&mt('Unregistered Clicker')." <tt>".$id."</tt><br />";
             $result.="\n".'<input type="hidden" name="unknown:'.$id.'" value="'.$responses{$id}.'" />'.
                      "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                      "\n".&mt("Domain").": ".
                      &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
                      &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id);
             $unknown_count++;
          }
       }
       $result.='<hr />'.
                &mt('Found [_1] registered and [_2] unregistered clickers.',$student_count,$unknown_count);
       if ($env{'form.gradingmechanism'} ne 'attendance') {
          if ($correct_count==0) {
             $errormsg.="Found no correct answers answers for grading!";
          } elsif ($correct_count>1) {
             $result.='<br /><span class="LC_warning">'.&mt("Found [_1] entries for grading!",$correct_count).'</span>';
          }
       }
       if ($number<1) {
          $errormsg.="Found no questions.";
       }
       if ($errormsg) {
          $result.='<br /><span class="LC_error">'.&mt($errormsg).'</span>';
       } else {
          $result.='<br /><input type="submit" name="finalize" value="'.&mt('Finalize Grading').'" />';
       }
       $result.='</form></td></tr></table>'."\n".
                '</td></tr></table><br /><br />'."\n";
       return $result.&show_grading_menu_form($symb);
   }
   
   sub iclicker_eval {
       my ($questiontitles,$responses)=@_;
       my $number=0;
       my $errormsg='';
       foreach my $line (split(/[\n\r]/,$env{'form.upfile'})) {
           my %components=&Apache::loncommon::record_sep($line);
           my @entries=map {$components{$_}} (sort(keys(%components)));
    if ($entries[0] eq 'Question') {
       for (my $i=3;$i<$#entries;$i+=6) {
    $$questiontitles[$number]=$entries[$i];
    $number++;
       }
    }
    if ($entries[0]=~/^\#/) {
       my $id=$entries[0];
       my @idresponses;
       $id=~s/^[\#0]+//;
       for (my $i=0;$i<$number;$i++) {
    my $idx=3+$i*6;
    push(@idresponses,$entries[$idx]);
       }
       $$responses{$id}=join(',',@idresponses);
    }
       }
       return ($errormsg,$number);
   }
   
   sub interwrite_eval {
       my ($questiontitles,$responses)=@_;
       my $number=0;
       my $errormsg='';
       my $skipline=1;
       my $questionnumber=0;
       my %idresponses=();
       foreach my $line (split(/[\n\r]/,$env{'form.upfile'})) {
           my %components=&Apache::loncommon::record_sep($line);
           my @entries=map {$components{$_}} (sort(keys(%components)));
           if ($entries[1] eq 'Time') { $skipline=0; next; }
           if ($entries[1] eq 'Response') { $skipline=1; }
           next if $skipline;
           if ($entries[0]!=$questionnumber) {
              $questionnumber=$entries[0];
              $$questiontitles[$number]=&mt('Question [_1]',$questionnumber);
              $number++;
           }
           my $id=$entries[4];
           $id=~s/^[\#0]+//;
           $id=~s/^v\d*\://i;
           $id=~s/[\-\:]//g;
           $idresponses{$id}[$number]=$entries[6];
       }
       foreach my $id (keys %idresponses) {
          $$responses{$id}=join(',',@{$idresponses{$id}});
          $$responses{$id}=~s/^\s*\,//;
       }
       return ($errormsg,$number);
   }
   
   sub assign_clicker_grades {
       my ($r)=@_;
       my ($symb)=&get_symb($r);
       if (!$symb) {return '';}
   # See which part we are saving to
       my ($partlist,$handgrade,$responseType) = &response_type($symb);
   # FIXME: This should probably look for the first handgradeable part
       my $part=$$partlist[0];
   # Start screen output
       my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
   
       my $heading=&mt('Assigning grades based on clicker file');
       $result.=(<<ENDHEADER);
   <br /><table width="100%" border="0"><tr><td bgcolor="#777777">
   <table width="100%" border="0"><tr bgcolor="#e6ffff"><td>
   <b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>
   ENDHEADER
   # Get correct result
   # FIXME: Possibly need delimiter other than ":"
       my @correct=();
       my $gradingmechanism=$env{'form.gradingmechanism'};
       my $number=$env{'form.number'};
       if ($gradingmechanism ne 'attendance') {
          foreach my $key (keys(%env)) {
             if ($key=~/^form\.correct\:/) {
                my @input=split(/\,/,$env{$key});
                for (my $i=0;$i<=$#input;$i++) {
                    if (($correct[$i]) && ($input[$i]) &&
                        ($correct[$i] ne $input[$i])) {
                       $result.='<br /><span class="LC_warning">'.
                                &mt('More than one correct result given for question "[_1]": [_2] versus [_3].',
                                    $env{'form.question:'.$i},$correct[$i],$input[$i]).'</span>';
                    } elsif ($input[$i]) {
                       $correct[$i]=$input[$i];
                    }
                }
             }
          }
          for (my $i=0;$i<$number;$i++) {
             if (!$correct[$i]) {
                $result.='<br /><span class="LC_error">'.
                         &mt('No correct result given for question "[_1]"!',
                             $env{'form.question:'.$i}).'</span>';
             }
          }
          $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ($_?$_:'-') } @correct));
       }
   # Start grading
       my $pcorrect=$env{'form.pcorrect'};
       my $pincorrect=$env{'form.pincorrect'};
       my $storecount=0;
       foreach my $key (keys(%env)) {
          my $user='';
          if ($key=~/^form\.student\:(.*)$/) {
             $user=$1;
          }
          if ($key=~/^form\.unknown\:(.*)$/) {
             my $id=$1;
             if (($env{'form.uname'.$id}) && ($env{'form.udom'.$id})) {
                $user=$env{'form.uname'.$id}.':'.$env{'form.udom'.$id};
             }
          }
          if ($user) { 
             my @answer=split(/\,/,$env{$key});
             my $sum=0;
             for (my $i=0;$i<$number;$i++) {
                if ($answer[$i]) {
                   if ($gradingmechanism eq 'attendance') {
                      $sum+=$pcorrect;
                   } else {
                      if ($answer[$i] eq $correct[$i]) {
                         $sum+=$pcorrect;
                      } else {
                         $sum+=$pincorrect;
                      }
                   }
                }
             }
             my $ave=$sum/(100*$number);
   # Store
             my ($username,$domain)=split(/\:/,$user);
             my %grades=();
             $grades{"resource.$part.solved"}='correct_by_override';
             $grades{"resource.$part.awarded"}=$ave;
             $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
             my $returncode=&Apache::lonnet::cstore(\%grades,$symb,
                                                    $env{'request.course.id'},
                                                    $domain,$username);
             if ($returncode ne 'ok') {
                $result.="<br /><span class=\"LC_error\">Failed to save student $username:$domain. Message when trying to save was ($returncode)</span>";
             } else {
                $storecount++;
             }
          }
       }
   # We are done
       $result.='<br />'.&mt('Successfully stored grades for [_1] student(s).',$storecount).
                '</td></tr></table>'."\n".
                '</td></tr></table><br /><br />'."\n";
       return $result.&show_grading_menu_form($symb);
   }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
   
     &reset_perm();      &reset_caches();
     if ($env{'browser.mathml'}) {      if ($env{'browser.mathml'}) {
  &Apache::loncommon::content_type($request,'text/xml');   &Apache::loncommon::content_type($request,'text/xml');
     } else {      } else {
Line 5874  sub handler { Line 7593  sub handler {
     if ($#commands > 0) {      if ($#commands > 0) {
  &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));   &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
     }      }
     &send_header($request);      $request->print(&Apache::loncommon::start_page('Grading'));
     if ($symb eq '' && $command eq '') {      if ($symb eq '' && $command eq '') {
  if ($env{'user.adv'}) {   if ($env{'user.adv'}) {
     if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&      if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&
Line 5923  sub handler { Line 7642  sub handler {
     $request->print(&editgrades($request));      $request->print(&editgrades($request));
  } elsif ($command eq 'verify' && $perm{'vgr'}) {   } elsif ($command eq 'verify' && $perm{'vgr'}) {
     $request->print(&verifyreceipt($request));      $request->print(&verifyreceipt($request));
           } elsif ($command eq 'processclicker' && $perm{'mgr'}) {
               $request->print(&process_clicker($request));
           } elsif ($command eq 'processclickerfile' && $perm{'mgr'}) {
               $request->print(&process_clicker_file($request));
           } elsif ($command eq 'assignclickergrades' && $perm{'mgr'}) {
               $request->print(&assign_clicker_grades($request));
  } elsif ($command eq 'csvform' && $perm{'mgr'}) {   } elsif ($command eq 'csvform' && $perm{'mgr'}) {
     $request->print(&upcsvScores_form($request));      $request->print(&upcsvScores_form($request));
  } elsif ($command eq 'csvupload' && $perm{'mgr'}) {   } elsif ($command eq 'csvupload' && $perm{'mgr'}) {
Line 5965  sub handler { Line 7690  sub handler {
     $request->print("Access Denied ($command)");      $request->print("Access Denied ($command)");
  }   }
     }      }
     &send_footer($request);      $request->print(&Apache::loncommon::end_page());
       &reset_caches();
     return '';      return '';
 }  }
   
 sub send_header {  
     my ($request)= @_;  
     $request->print(&Apache::lontexconvert::header());  
 #  $request->print("  
 #<script>  
 #remotewindow=open('','homeworkremote');  
 #remotewindow.close();  
 #</script>");   
     $request->print(&Apache::loncommon::bodytag('Grading'));  
     $request->rflush();  
 }  
   
 sub send_footer {  
     my ($request)= @_;  
     $request->print('</body></html>');  
 }  
   
 1;  1;
   
 __END__;  __END__;

Removed from v.1.336  
changed lines
  Added in v.1.436


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