Diff for /loncom/homework/grades.pm between versions 1.399.2.1 and 1.432

version 1.399.2.1, 2007/06/13 17:20:12 version 1.432, 2007/08/21 22:21:54
Line 41  use Apache::Constants qw(:common); Line 41  use Apache::Constants qw(:common);
 use Apache::lonlocal;  use Apache::lonlocal;
 use Apache::lonenc;  use Apache::lonenc;
 use String::Similarity;  use String::Similarity;
 use lib '/home/httpd/lib/perl';  
 use LONCAPA;  use LONCAPA;
   
 use POSIX qw(floor);  use POSIX qw(floor);
   
 my %oldessays=();  
 my %perm=();  my %perm=();
   
 # ----- These first few routines are general use routines.----  # ----- These first few routines are general use routines.----
Line 94  sub get_symb { Line 92  sub get_symb {
     return ();      return ();
  }   }
     }      }
       &Apache::lonenc::check_decrypt(\$symb);
     return ($symb);      return ($symb);
 }  }
   
Line 180  sub showResourceInfo { Line 179  sub showResourceInfo {
  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;
     }      }
Line 493  sub jscriptNform { Line 492  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 538  sub compute_points { Line 537  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 555  sub most_similar { Line 554  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 618  sub verifyreceipt { Line 616  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 708  LISTJAVASCRIPT Line 706  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 725  LISTJAVASCRIPT Line 723  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".
Line 736  LISTJAVASCRIPT Line 733  LISTJAVASCRIPT
         '<option value=".25">Quarter Points</option>'.          '<option value=".25">Quarter Points</option>'.
         '<option value=".1">Tenths of a Point</option>'.          '<option value=".1">Tenths of a Point</option>'.
         '</select>'.          '</select>'.
           &build_section_inputs().
  '<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".  
  '<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 763  LISTJAVASCRIPT Line 759  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 1501  sub gradeBox { Line 1497  sub gradeBox {
  $result.= '<td><span style="white-space: nowrap;"><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.'\','.
     $thisweight.')" value="'.$thisweight.'" '.      $thisweight.')" value="'.$thisweight.'" '.
     ($score eq $thisweight ? 'checked':'').' /> '.$thisweight."</label></span></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;          $thisweight += $increment;
  $ctr++;   $ctr++;
Line 1519  sub gradeBox { Line 1515  sub gradeBox {
  '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";
Line 1663  sub download_all_link { Line 1659  sub download_all_link {
     return      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 1691  sub submission { Line 1700  sub submission {
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
   
       my %old_essays;
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
  &sub_page_js($request);   &sub_page_js($request);
Line 1742  sub submission { Line 1752  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 1752  sub submission { Line 1761  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 1793  sub submission { Line 1802  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
Line 1805  KEYWORDS Line 1814  KEYWORDS
     my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);      my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
     $apath=&escape($apath);      $apath=&escape($apath);
     $apath=~s/\W/\_/gs;      $apath=~s/\W/\_/gs;
     %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);      %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
         }          }
     }      }
   
Line 1922  KEYWORDS Line 1931  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 1943  KEYWORDS Line 1952  KEYWORDS
     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><span class=\"LC_warning\">Essay".      my %old_course_desc = 
  " is $osim% similar to an essay by ".   &Apache::lonnet::coursedescription($ocrsid,
  &Apache::loncommon::plainname($oname,$odom).     {'one_time' => 1});
   
       $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>'.   '</span></h3><blockquote><i>'.
  &keywords_highlight($oessay).   &keywords_highlight($oessay).
  '</i></blockquote><hr />';   '</i></blockquote><hr />';
Line 2000  KEYWORDS Line 2018  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 2024  KEYWORDS Line 2042  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><label> ('.      &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a><label> ('.
     &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.      &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
     '<img src="'.$request->dir_config('lonIconsURL').      '<img src="'.$request->dir_config('lonIconsURL').
Line 2076  KEYWORDS Line 2094  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().          $endform.="<input type='hidden' value='".&get_increment().
             "' name='increment' />";              "' name='increment' />";
Line 2169  sub processHandGrade { Line 2187  sub processHandGrade {
     }      }
     my $includemsg = $env{'form.includemsg'.$ctr};      my $includemsg = $env{'form.includemsg'.$ctr};
     my ($subject,$message,$msgstatus) = ('','','');      my ($subject,$message,$msgstatus) = ('','','');
             my $restitle = &Apache::lonnet::gettitle($symb);      my $restitle = &Apache::lonnet::gettitle($symb);
             my $encrypturl=&Apache::lonnet::EXT('resource.0.encrypturl',              my ($feedurl,$showsymb) =
                                                 $symb,$udom,$uname);   &get_feedurl_and_symb($symb,$uname,$udom);
             my ($feedurl,$baseurl,$showsymb,$messagetail);      my $messagetail;
             $feedurl = &Apache::lonnet::clutter($url);  
             if ($encrypturl =~ /^yes$/i) {  
                 $baseurl = &Apache::lonenc::encrypted($feedurl,1);  
                 $showsymb = &Apache::lonenc::encrypted($symb,1);  
             } else {  
                 $baseurl = $feedurl;  
                 $showsymb = $symb;  
             }  
     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'); }
Line 2193  sub processHandGrade { Line 2203  sub processHandGrade {
  if ($env{'form.withgrades'.$ctr}) {   if ($env{'form.withgrades'.$ctr}) {
     $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;      $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
     $messagetail = " for <a href=\"".      $messagetail = " for <a href=\"".
                    $baseurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";                     $feedurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";
  }   }
  $msgstatus =    $msgstatus = 
                     &Apache::lonmsg::user_normal_msg($uname,$udom,$subject,                      &Apache::lonmsg::user_normal_msg($uname,$udom,$subject,
      $message.$messagetail,       $message.$messagetail,
                                                      undef,$baseurl,undef,                                                       undef,$feedurl,undef,
                                                      undef,undef,$showsymb,                                                       undef,undef,$showsymb,
                                                      $restitle);                                                       $restitle);
  $request->print('<br />'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '.   $request->print('<br />'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '.
Line 2215  sub processHandGrade { Line 2225  sub processHandGrade {
  if ($errorflag eq 'not_allowed') {   if ($errorflag eq 'not_allowed') {
     $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");      $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) = 
                                 $encrypturl=   &get_feedurl_and_symb($symb,$collaborator,
                                   &Apache::lonnet::EXT('resource.0.encrypturl',        $udom);
                                                        $symb,$udom,$collaborator);      if ($env{'form.withgrades'.$ctr}) {
                                 if ($encrypturl =~ /^yes$/i) {   $messagetail = " for <a href=\"".
                                     $baseurl = &Apache::lonenc::encrypted($feedurl,1);  
                                     $showsymb = &Apache::lonenc::encrypted($symb,1);  
                                 } else {  
                                     $baseurl = $feedurl;  
                                     $showsymb = $symb;  
                                 }  
                                 if ($env{'form.withgrades'.$ctr}) {  
                                     $messagetail = " for <a href=\"".  
                                     $baseurl."?symb=$showsymb\">$env{'form.probTitle'}</a>";                                      $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);  
     }      }
       $msgstatus = 
    &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
  }   }
     }      }
  }   }
Line 2565  sub handback_files { Line 2565  sub handback_files {
                                            $newflg.'_'.$part_resp.'_returndoc'.$file_counter,                                             $newflg.'_'.$part_resp.'_returndoc'.$file_counter,
                                            $save_file_name);                                             $save_file_name);
                     if ($result !~ m|^/uploaded/|) {                      if ($result !~ m|^/uploaded/|) {
                         $request->print('<span class="LC_error">An errror occured ('.$result.                          $request->print('<span class="LC_error">An error occurred ('.$result.
                         ') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'</span><br />');                          ') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'</span><br />');
                     } else {                      } else {
                         # mark the file as read only                          # mark the file as read only
Line 2588  sub handback_files { Line 2588  sub handback_files {
  $message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />";   $message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />";
  $message .= ' The returned file(s) are named: '. $file_msg;   $message .= ' The returned file(s) are named: '. $file_msg;
  $message .= " and can be found in your portfolio space.";   $message .= " and can be found in your portfolio space.";
  my $url = (&Apache::lonnet::decode_symb($symb))[2];   my ($feedurl,$showsymb) = 
  my $feedurl = &Apache::lonnet::clutter($url);      &get_feedurl_and_symb($symb,$domain,$stuname);
                 my $encrypturl=&Apache::lonnet::EXT('resource.0.encrypturl',  
                                                     $symb,$domain,$stuname);  
                 my ($baseurl,$showsymb);  
                 if ($encrypturl =~ /^yes$/i) {  
                     $baseurl = &Apache::lonenc::encrypted($feedurl,1);  
                     $showsymb = &Apache::lonenc::encrypted($symb,1);  
                 } else {  
                     $baseurl = $feedurl;  
                     $showsymb = $symb;  
                 }  
                 my $restitle = &Apache::lonnet::gettitle($symb);                  my $restitle = &Apache::lonnet::gettitle($symb);
  my $msgstatus =    my $msgstatus = 
                    &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject.                     &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject.
  ' (File Returned) ['.$restitle.']',$message,undef,   ' (File Returned) ['.$restitle.']',$message,undef,
                          $baseurl,undef,undef,undef,$showsymb,$restitle);                           $feedurl,undef,undef,undef,$showsymb,$restitle);
             }              }
         }          }
     return;      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 2970  sub viewgrades { Line 2973  sub viewgrades {
   
     #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.
Line 3028  sub viewgrades { Line 3032  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 3037  sub viewgrades { Line 3041  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="Revert to Default" '.      $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 3088  sub viewgrades { Line 3092  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='<span class="LC_warning">There are no students in section "'.$env{'form.section'}.   $result='<span class="LC_warning">There are no students in section "'.$env{'form.section'}.
Line 3108  sub viewstudentgrade { Line 3112  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> '.
  '<span class="LC_internal_info">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</span></td>'."\n";   '<span class="LC_internal_info">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</span></td>'."\n";
     $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) {
Line 3146  sub viewstudentgrade { Line 3150  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 3472  sub csvuploadmap_header { Line 3476  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><span class="LC_info">Uploading Class Grades</span></h3>  <h3><span class="LC_info">Uploading Class Grades</span></h3>
Line 3566  sub upcsvScores_form { Line 3571  sub upcsvScores_form {
     my $upload=&mt("Upload Scores");      my $upload=&mt("Upload Scores");
     my $upfile_select=&Apache::loncommon::upfile_select_html();      my $upfile_select=&Apache::loncommon::upfile_select_html();
     my $ignore=&mt('Ignore First Line');      my $ignore=&mt('Ignore First Line');
       $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 3840  LISTJAVASCRIPT Line 3846  LISTJAVASCRIPT
   
     $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 3848  LISTJAVASCRIPT Line 3854  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 3863  LISTJAVASCRIPT Line 3869  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>'.      $result.='&nbsp;<b>'.&mt('Use CODE:').' </b>'.
Line 3922  LISTJAVASCRIPT Line 3928  LISTJAVASCRIPT
 }  }
   
 sub getSymbMap {  sub getSymbMap {
     my ($request) = @_;  
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
   
     my %symbx = ();      my %symbx = ();
Line 3998  sub displayPage { Line 4003  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";
   
Line 4367  sub updateGradeByPage { Line 4372  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 missed 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 is 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 4392  sub getSequenceDropDown { Line 4470  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'};
Line 4407  sub scantron_filenames { Line 4494  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 4432  sub scantron_scantab { Line 4539  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 4446  sub scantron_CODElist { Line 4562  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='<span style="white-space: nowrap;">      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>
                 </span>                  </span>
                 <span style="white-space: nowrap;">                  <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>
                 </span>';                  </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 4470  sub scantron_selectphase { Line 4611  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 4504  sub scantron_selectphase { Line 4646  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 4524  SCANTRONFORM Line 4666  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 4569  UPLOAD Line 4713  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'>
Line 4603  SCANTRONFORM Line 4751  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 4635  sub get_scantron_config { Line 4840  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 4645  sub username_to_idmap { Line 4869  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 4703  sub scantron_fixup_scanline { Line 4969  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 4713  sub scan_data { Line 4998  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 4747  sub scantron_parse_scanline { Line 5084  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 4820  sub scantron_parse_scanline { Line 5157  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 4828  sub scantron_add_delay { Line 5183  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 4842  sub scantron_find_student { Line 5215  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 4858  sub scantron_filter { Line 5240  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 4915  sub scantron_process_corrections { Line 5306  sub scantron_process_corrections {
     }      }
 }  }
   
   =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);
 }  }
   
   =pod
   
   =item start_skipping
   
      Marks a scanline to be skipped. 
   
   =cut
   
 sub start_skipping {  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'));
Line 4932  sub start_skipping { Line 5341  sub start_skipping {
     &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 ($scanlines,$scan_data,$i)=@_;      my ($scanlines,$scan_data,$i)=@_;
     if ($env{'form.scantron_options_redo'} !~ /^redo_/) {      if ($env{'form.scantron_options_redo'} !~ /^redo_/) {
Line 4947  sub should_be_skipped { Line 5364  sub should_be_skipped {
     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 4960  sub remember_current_skipped { Line 5386  sub remember_current_skipped {
     &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'});
Line 4999  $CODElist Line 5444  $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 5030  STUFF Line 5484  STUFF
     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 5047  SCANTRONFORM Line 5509  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 5054  sub scantron_validate_file { Line 5528  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();
     }      }
Line 5073  sub scantron_validate_file { Line 5547  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 5130  STUFF Line 5604  STUFF
     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 5144  sub scantron_remove_file { Line 5629  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 5166  sub scantron_remove_scan_data { Line 5663  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 5198  sub scantron_getfile { Line 5740  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 5207  sub lonnet_putfile { Line 5764  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 5227  sub scantron_putfile { Line 5800  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($scanlines,$scan_data,$i)) { return undef; }      if (&should_be_skipped($scanlines,$scan_data,$i)) { return undef; }
Line 5235  sub scantron_get_line { Line 5830  sub scantron_get_line {
     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 5246  sub get_todo_count { Line 5858  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) {
Line 5256  sub scantron_put_line { Line 5887  sub scantron_put_line {
     $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 {  sub scantron_clear_skip {
     my ($scanlines,$scan_data,$i)=@_;      my ($scanlines,$scan_data,$i)=@_;
     if (exists($scanlines->{'skipped'}[$i])) {      if (exists($scanlines->{'skipped'}[$i])) {
Line 5265  sub scantron_clear_skip { Line 5911  sub scantron_clear_skip {
     return 0;      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 5281  sub scantron_filter_not_exam { Line 5936  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 5304  sub scantron_validate_sequence { Line 5968  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 5366  sub scantron_validate_ID { Line 6039  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 5425  sub scantron_get_correction { Line 6128  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 5433  sub scantron_get_correction { Line 6136  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 5469  ENDSCRIPT Line 6172  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 5489  ENDSCRIPT Line 6193  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 5526  sub num_matches { Line 6297  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 5536  sub scantron_get_closely_matching_CODEs Line 6327  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 5558  sub get_codes { Line 6366  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 5609  sub scantron_validate_CODE { Line 6427  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 5632  sub scantron_validate_doublebubble { Line 6459  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'}) {
Line 5658  sub scantron_get_maxbubble { Line 6498  sub scantron_get_maxbubble {
     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 5690  sub scantron_validate_missingbubbles { Line 6539  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 5794  SCANTRONFORM Line 6667  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 5830  UPLOAD Line 6711  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 5885  sub scantron_upload_scantron_data_save { Line 6775  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 5893  sub valid_file { Line 6791  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 5929  DOWNLOAD Line 6837  DOWNLOAD
     return '';      return '';
 }  }
   
   =pod
   
   =back
   
   =cut
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
Line 5939  DOWNLOAD Line 6853  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 6014  GRADINGMENUJS Line 6928  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 6029  GRADINGMENUJS Line 6943  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 6078  GRADINGMENUJS Line 6992  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 6097  GRADINGMENUJS Line 7015  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 6125  sub init_perm { Line 7043  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];
   
Line 6192  sub handler { Line 7575  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'}) {

Removed from v.1.399.2.1  
changed lines
  Added in v.1.432


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