Diff for /loncom/homework/grades.pm between versions 1.121 and 1.342

version 1.121, 2003/07/21 13:32:49 version 1.342, 2006/03/28 00:46:20
Line 25 Line 25
 #  #
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 # 2/9,2/13 Guy Albertelli  
 # 6/8 Gerd Kortemeyer  
 # 7/26 H.K. Ng  
 # 8/20 Gerd Kortemeyer  
 # Year 2002  
 # June-August H.K. Ng  
 # Year 2003  
 # February, March H.K. Ng  
 #  
   
 package Apache::grades;  package Apache::grades;
 use strict;  use strict;
Line 47  use Apache::lonhomework; Line 38  use Apache::lonhomework;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use Apache::lonmsg qw(:user_normal_msg);  use Apache::lonmsg qw(:user_normal_msg);
 use Apache::Constants qw(:common);  use Apache::Constants qw(:common);
   use Apache::lonlocal;
 use String::Similarity;  use String::Similarity;
   use POSIX qw(floor);
   
 my %oldessays=();  my %oldessays=();
 my %perm=();  my %perm=();
   
 # ----- These first few routines are general use routines.----  # ----- These first few routines are general use routines.----
 #  #
 # --- Retrieve the parts that matches stores_\d+ from the metadata file.---  # --- Retrieve the parts from the metadata file.---
 sub getpartlist {  sub getpartlist {
     my ($url) = @_;      my ($symb) = @_;
     my @parts =();      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));      my $partorder = &Apache::lonnet::metadata($url, 'partorder');
     foreach my $key (@metakeys) {      my @parts;
  if ( $key =~ m/stores_(\w+)_.*/) {      if ($partorder) {
     push(@parts,$key);   for my $part (split (/,/,$partorder)) {
       if (!&Apache::loncommon::check_if_partid_hidden($part,$symb)) {
    push(@parts, $part);
       }
    }    
       } else {
    my $metadata = &Apache::lonnet::metadata($url, 'packages');
    foreach (split(/\,/,$metadata)) {
       if ($_ =~ /^part_(.*)$/) {
    if (!&Apache::loncommon::check_if_partid_hidden($1,$symb)) {
       push(@parts, $1);
    }
       }
  }   }
     }      }
     return @parts;      my @stores;
       foreach my $part (@parts) {
    my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
    foreach my $key (@metakeys) {
       if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); }
    }
       }
       return @stores;
 }  }
   
 # --- Get the symbolic name of a problem and the url  # --- Get the symbolic name of a problem and the url
 sub get_symb_and_url {  sub get_symb {
     my ($request) = @_;      my ($request,$silent) = @_;
     (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;      (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
     my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));      my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }      if ($symb eq '') { 
     return ($symb,$url);   if (!$silent) {
 }      $request->print("Unable to handle ambiguous references:$url:.");
       return ();
 # --- Retrieve the fullname for a user. Return lastname, first middle ---   }
 # --- Generation is attached next to the lastname if it exists. ---      }
 sub get_fullname {      return ($symb);
     my ($uname,$udom) = @_;  }
     my %name=&Apache::lonnet::get('environment', ['lastname','generation',  
   'firstname','middlename'],  #--- Format fullname, username:domain if different for display
                                   $udom,$uname);  #--- Use anywhere where the student names are listed
     my $fullname;  sub nameUserString {
     my ($tmp) = keys(%name);      my ($type,$fullname,$uname,$udom) = @_;
     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {      if ($type eq 'header') {
         $fullname = &Apache::loncoursedata::ProcessFullName   return '<b>&nbsp;Fullname&nbsp;</b><font color="#999999">(Username)</font>';
             (@name{qw/lastname generation firstname middlename/});  
     } else {      } else {
         &Apache::lonnet::logthis('grades.pm: no name data for '.$uname.   return '&nbsp;'.$fullname.'<font color="#999999">&nbsp;('.$uname.
                                  '@'.$udom.':'.$tmp);      ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</font>';
     }      }
     return $fullname;  
 }  }
   
 #--- Get the partlist and the response type for a given problem. ---  #--- Get the partlist and the response type for a given problem. ---
 #--- Indicate if a response type is coded handgraded or not. ---  #--- Indicate if a response type is coded handgraded or not. ---
 sub response_type {  sub response_type {
     my ($url) = shift;      my ($symb) = shift;
       my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     my $allkeys = &Apache::lonnet::metadata($url,'keys');      my $allkeys = &Apache::lonnet::metadata($url,'keys');
       my %vPart;
       foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
    $vPart{$partid}=1;
       }
     my %seen = ();      my %seen = ();
     my (@partlist,%handgrade);      my (@partlist,%handgrade,%responseType);
     foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {      foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
  if (/^\w+response_\w+.*/) {   if (/^\w+response_.*/ || /^Task_/) {
     my ($responsetype,$part) = split(/_/,$_,2);      my ($responsetype,$part) = split(/_/,$_,2);
     my ($partid,$respid) = split(/_/,$part);      my ($partid,$respid) = split(/_/,$part);
       if ($responsetype eq 'Task') { $respid='0'; }
       if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) {
    next;
       }
       if (%vPart && !exists($vPart{$partid})) {
    next;
       }
     $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!      $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!
     $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');      my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb);
       $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); 
       if (!exists($responseType{$partid})) { $responseType{$partid}={}; }
       $responseType{$partid}->{$respid}=$responsetype;
     next if ($seen{$partid} > 0);      next if ($seen{$partid} > 0);
     $seen{$partid}++;      $seen{$partid}++;
     push @partlist,$partid;      push @partlist,$partid;
  }   }
     }      }
     return \@partlist,\%handgrade;      return (\@partlist,\%handgrade,\%responseType);
   }
   
   sub get_display_part {
       my ($partID,$symb)=@_;
       my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
       if (defined($display) and $display ne '') {
    $display.= " (<font color=\"#999900\">id $partID</font>)";
       } else {
    $display=$partID;
       }
       return $display;
 }  }
   
 #--- Show resource title  #--- Show resource title
 #--- and parts and response type  #--- and parts and response type
 sub showResourceInfo {  sub showResourceInfo {
     my ($url,$probTitle) = @_;      my ($symb,$probTitle,$checkboxes) = @_;
       my $col=3;
       if ($checkboxes) { $col=4; }
     my $result ='<table border="0">'.      my $result ='<table border="0">'.
  '<tr><td colspan=3><font size=+1><b>Current Resource: </b>'.$probTitle.'</font></td></tr>'."\n";   '<tr><td colspan="'.$col.'"><font size="+1"><b>'.&mt('Current Resource').': </b>'.
     my ($partlist,$handgrade) = &response_type($url);   $probTitle.'</font></td></tr>'."\n";
     my ($resptype,$hdgrade)=('','no');      my ($partlist,$handgrade,$responseType) = &response_type($symb);
     for (sort keys(%$handgrade)) {      my %resptype = ();
  my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});      my $hdgrade='no';
  $resptype = $responsetype;      my %partsseen;
       for my $part_resID (sort keys(%$handgrade)) {
    my $handgrade=$$handgrade{$part_resID};
    my ($partID,$resID) = split(/_/,$part_resID);
    my $responsetype = $responseType->{$partID}->{$resID};
  $hdgrade = $handgrade if ($handgrade eq 'yes');   $hdgrade = $handgrade if ($handgrade eq 'yes');
  $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.   $result.='<tr>';
    if ($checkboxes) {
       if (exists($partsseen{$partID})) {
    $result.="<td>&nbsp;</td>";
       } else {
    $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='on' /></td>";
       }
       $partsseen{$partID}=1;
    }
    my $display_part=&get_display_part($partID,$symb);
    $result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'.
       $resID.'</font></td>'.
     '<td><b>Type: </b>'.$responsetype.'</td></tr>';      '<td><b>Type: </b>'.$responsetype.'</td></tr>';
 #    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';  #    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';
     }      }
     $result.='</table>'."\n";      $result.='</table>'."\n";
     return $result,$resptype,$hdgrade,$partlist,$handgrade;      return $result,$responseType,$hdgrade,$partlist,$handgrade;
 }  }
   
   
   sub get_order {
       my ($partid,$respid,$symb,$uname,$udom)=@_;
       my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
       $url=&Apache::lonnet::clutter($url);
       my $subresult=&Apache::lonnet::ssi($url,
          ('grade_target' => 'analyze'),
          ('grade_domain' => $udom),
          ('grade_symb' => $symb),
          ('grade_courseid' => 
           $env{'request.course.id'}),
          ('grade_username' => $uname));
       (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
       my %analyze=&Apache::lonnet::str2hash($subresult);
       return ($analyze{"$partid.$respid.shown"});
   }
 #--- Clean response type for display  #--- Clean response type for display
 #--- Currently filters option response type only.  #--- Currently filters option/rank/radiobutton/match/essay/Task
   #        response types only.
 sub cleanRecord {  sub cleanRecord {
     my ($answer,$response) = @_;      my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,
     if ($response eq 'option') {   $uname,$udom) = @_;
  my (@IDs,@ans);      my $grayFont = '<font color="#999999">';
  foreach (split(/\&/,&Apache::lonnet::unescape($answer))) {      if ($response =~ /^(option|rank)$/) {
     my ($optionID,$ans) = split(/=/);   my %answer=&Apache::lonnet::str2hash($answer);
     push @IDs,$optionID.'</font>';   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
     push @ans,$ans;   my ($toprow,$bottomrow);
  }   foreach my $foil (@$order) {
  my $grayFont = '<font color="#999999">';      if ($grading{$foil} == 1) {
  return '<table border="1">'.   $toprow.='<td><b>'.$answer{$foil}.'&nbsp;</b></td>';
     '<tr valign="top"><td>Answer</td><td>'.      } else {
     (join '</td><td>',@ans).'</td></tr>'.   $toprow.='<td><i>'.$answer{$foil}.'&nbsp;</i></td>';
     '<tr valign="top"><td>'.$grayFont.'Option ID</font></td><td>'.$grayFont.      }
     (join '</td><td>'.$grayFont,@IDs).'</font></td></tr>'.      $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
     '</table>';   }
    return '<blockquote><table border="1">'.
       '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
       '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
       $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
       } elsif ($response eq 'match') {
    my %answer=&Apache::lonnet::str2hash($answer);
    my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
    my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
    my ($toprow,$middlerow,$bottomrow);
    foreach my $foil (@$order) {
       my $item=shift(@items);
       if ($grading{$foil} == 1) {
    $toprow.='<td><b>'.$item.'&nbsp;</b></td>';
    $middlerow.='<td><b>'.$grayFont.$answer{$foil}.'&nbsp;</font></b></td>';
       } else {
    $toprow.='<td><i>'.$item.'&nbsp;</i></td>';
    $middlerow.='<td><i>'.$grayFont.$answer{$foil}.'&nbsp;</font></i></td>';
       }
       $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
    }
    return '<blockquote><table border="1">'.
       '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
       '<tr valign="top"><td>'.$grayFont.'Item ID</font></td>'.
       $middlerow.'</tr>'.
       '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
       $bottomrow.'</tr>'.'</table></blockquote>';
       } elsif ($response eq 'radiobutton') {
    my %answer=&Apache::lonnet::str2hash($answer);
    my ($toprow,$bottomrow);
    my $correct=($order->[0])+1;
    for (my $i=1;$i<=$#$order;$i++) {
       my $foil=$order->[$i];
       if (exists($answer{$foil})) {
    if ($i == $correct) {
       $toprow.='<td><b>true</b></td>';
    } else {
       $toprow.='<td><i>true</i></td>';
    }
       } else {
    $toprow.='<td>false</td>';
       }
       $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
    }
    return '<blockquote><table border="1">'.
       '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
       '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
       $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
       } elsif ($response eq 'essay') {
    if (! exists ($env{'form.'.$symb})) {
       my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
     $env{'course.'.$env{'request.course.id'}.'.domain'},
     $env{'course.'.$env{'request.course.id'}.'.num'});
   
       my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
       $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
       $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
       $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
       $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
       $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
    }
    $answer =~ s-\n-<br />-g;
    return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
       } elsif ( $response eq 'organic') {
    my $result='Smile representation: "<tt>'.$answer.'</tt>"';
    my $jme=$record->{$version."resource.$partid.$respid.molecule"};
    $result.=&Apache::chemresponse::jme_img($jme,$answer,400);
    return $result;
       } elsif ( $response eq 'Task') {
    if ( $answer eq 'SUBMITTED') {
       my $files = $record->{$version."resource.$respid.$partid.bridgetask.portfiles"};
       my $result = &Apache::bridgetask::file_list($files,$uname,$udom);
       return $result;
    } elsif ( grep(/^\Q$version\E.*?\.instance$/, keys(%{$record})) ) {
       my @matches = grep(/^\Q$version\E.*?\.instance$/,
          keys(%{$record}));
       return join('<br />',($version,@matches));
          
          
    } else {
       my $result =
    '<p>'
    .&mt('Overall result: [_1]',
        $record->{$version."resource.$respid.$partid.status"})
    .'</p>';
       
       $result .= '<ul>';
       my @grade = grep(/^\Q${version}resource.$respid.$partid.\E[^.]*[.]status$/,
        keys(%{$record}));
       foreach my $grade (sort(@grade)) {
    my ($dim) = ($grade =~/[.]([^.]+)[.]status$/);
    $result.= '<li>'.&mt("Dimension: [_1], status [_2] ",
        $dim, $record->{$grade}).
     '</li>';
       }
       $result.='</ul>';
       return $result;
    }
          
     }      }
     return $answer;      return $answer;
 }  }
Line 186  sub commonJSfunctions { Line 354  sub commonJSfunctions {
  }   }
     }      }
  } else {   } else {
     if (selectOne.selected) return selectOne.value;              // only one value it must be the selected one
       return selectOne.value;
  }   }
     }      }
 </script>  </script>
Line 197  COMMONJSFUNCTIONS Line 366  COMMONJSFUNCTIONS
 #--- section, ids and fullnames for each user.  #--- section, ids and fullnames for each user.
 sub getclasslist {  sub getclasslist {
     my ($getsec,$filterlist) = @_;      my ($getsec,$filterlist) = @_;
     $getsec = $getsec eq '' ? 'all' : $getsec;      my @getsec;
       if (!ref($getsec)) {
    if ($getsec ne '' && $getsec ne 'all') {
       @getsec=($getsec);
    }
       } else {
    @getsec=@{$getsec};
       }
       if (grep(/^all$/,@getsec)) { undef(@getsec); }
   
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     # Bail out if we were unable to get the classlist      # Bail out if we were unable to get the classlist
     return if (! defined($classlist));      return if (! defined($classlist));
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
     foreach (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
         # the following undefs are for 'domain', and 'username' respectively.          my $end      = 
  my (undef,undef,$end,$start,$id,$section,$fullname,$status)=              $classlist->{$student}->[&Apache::loncoursedata::CL_END()];
             @{$classlist->{$_}};          my $start    = 
               $classlist->{$student}->[&Apache::loncoursedata::CL_START()];
           my $id       = 
               $classlist->{$student}->[&Apache::loncoursedata::CL_ID()];
           my $section  = 
               $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
           my $fullname = 
               $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()];
           my $status   = 
               $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()];
  # filter students according to status selected   # filter students according to status selected
  if ($filterlist && $ENV{'form.Status'} ne 'Any') {   if ($filterlist && $env{'form.Status'} ne 'Any') {
     if ($ENV{'form.Status'} ne $status) {      if ($env{'form.Status'} ne $status) {
  delete ($classlist->{$_});   delete ($classlist->{$student});
  next;   next;
     }      }
  }   }
  $section = ($section ne '' ? $section : 'no');   $section = ($section ne '' ? $section : 'none');
  if (&canview($section)) {   if (&canview($section)) {
     if ($getsec eq 'all' || $getsec eq $section) {      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
  $sections{$section}++;   $sections{$section}++;
  $fullnames{$_}=$fullname;   $fullnames{$student}=$fullname;
     } else {      } else {
  delete($classlist->{$_});   delete($classlist->{$student});
     }      }
  } else {   } else {
     delete($classlist->{$_});      delete($classlist->{$student});
  }   }
     }      }
     my %seen = ();      my %seen = ();
Line 274  sub canview { Line 461  sub canview {
   
 #--- Retrieve the grade status of a student for all the parts  #--- Retrieve the grade status of a student for all the parts
 sub student_gradeStatus {  sub student_gradeStatus {
     my ($url,$symb,$udom,$uname,$partlist) = @_;      my ($symb,$udom,$uname,$partlist) = @_;
     my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);      my %record     = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
     my %partstatus = ();      my %partstatus = ();
     foreach (@$partlist) {      foreach (@$partlist) {
  my ($status,$foo)    = split(/_/,$record{"resource.$_.solved"},2);   my ($status,undef)   = split(/_/,$record{"resource.$_.solved"},2);
  $status              = 'nothing' if ($status eq '');   $status              = 'nothing' if ($status eq '');
  $partstatus{$_}      = $status;   $partstatus{$_}      = $status;
  my $subkey           = "resource.$_.submitted_by";   my $subkey           = "resource.$_.submitted_by";
Line 291  sub student_gradeStatus { Line 478  sub student_gradeStatus {
 # Use by verifyscript and viewgrades  # Use by verifyscript and viewgrades
 # Shows a student's view of problem and submission  # Shows a student's view of problem and submission
 sub jscriptNform {  sub jscriptNform {
     my ($url,$symb) = @_;      my ($symb) = @_;
     my $jscript='<script type="text/javascript" language="javascript">'."\n".      my $jscript='<script type="text/javascript" language="javascript">'."\n".
  '    function viewOneStudent(user,domain) {'."\n".   '    function viewOneStudent(user,domain) {'."\n".
  ' document.onestudent.student.value = user;'."\n".   ' document.onestudent.student.value = user;'."\n".
Line 301  sub jscriptNform { Line 488  sub jscriptNform {
  '</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="'.$symb.'" />'."\n".
  '<input type="hidden" name="url"     value="'.$url.'" />'."\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="command" value="submission" />'."\n".   '<input type="hidden" name="command" value="submission" />'."\n".
  '<input type="hidden" name="student" value="" />'."\n".   '<input type="hidden" name="student" value="" />'."\n".
  '<input type="hidden" name="userdom" value="" />'."\n".   '<input type="hidden" name="userdom" value="" />'."\n".
Line 311  sub jscriptNform { Line 498  sub jscriptNform {
     return $jscript;      return $jscript;
 }  }
   
   # Given the score (as a number [0-1] and the weight) what is the final
   # point value? This function will round to the nearest tenth, third,
   # or quarter if one of those is within the tolerance of .00001.
   sub compute_points {
       my ($score, $weight) = @_;
       
       my $tolerance = .00001;
       my $points = $score * $weight;
   
       # Check for nearness to 1/x.
       my $check_for_nearness = sub {
           my ($factor) = @_;
           my $num = ($points * $factor) + $tolerance;
           my $floored_num = floor($num);
           if ($num - $floored_num < 2 * $tolerance * $factor) {
               return $floored_num / $factor;
           }
           return $points;
       };
   
       $points = $check_for_nearness->(10);
       $points = $check_for_nearness->(3);
       $points = $check_for_nearness->(4);
       
       return $points;
   }
   
 #------------------ End of general use routines --------------------  #------------------ End of general use routines --------------------
   
 #  #
Line 324  sub most_similar { Line 538  sub most_similar {
   
     $uessay=~s/\W+/ /gs;      $uessay=~s/\W+/ /gs;
   
   # ignore empty submissions (occuring when only files are sent)
   
       unless ($uessay=~/\w+/) { return ''; }
   
 # these will be returned. Do not care if not at least 50 percent similar  # these will be returned. Do not care if not at least 50 percent similar
     my $limit=0.6;      my $limit=0.6;
     my $sname='';      my $sname='';
Line 364  sub most_similar { Line 582  sub most_similar {
 sub verifyreceipt {  sub verifyreceipt {
     my $request  = shift;      my $request  = shift;
   
     my $courseid = $ENV{'request.course.id'};      my $courseid = $env{'request.course.id'};
     my $receipt  = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.      my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.
  $ENV{'form.receipt'};   $env{'form.receipt'};
     $receipt     =~ s/[^\-\d]//g;      $receipt     =~ s/[^\-\d]//g;
     my $url      = $ENV{'form.url'};      my $symb     = &Apache::lonnet::symbread();
     my $symb     = $ENV{'form.symb'};  
     unless ($symb) {  
  $symb    = &Apache::lonnet::symbread($url);  
     }  
   
     my $title.='<h3><font color="#339933">Verifying Submission Receipt '.      my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
  $receipt.'</h3></font>'."\n".   $receipt.'</h3></font>'."\n".
  '<font size=+1><b>Resource: </b>'.$ENV{'form.probTitle'}.'</font><br><br>'."\n";   '<font size=+1><b>Resource: </b>'.$env{'form.probTitle'}.'</font><br /><br />'."\n";
   
     my ($string,$contents,$matches) = ('','',0);      my ($string,$contents,$matches) = ('','',0);
     my (undef,undef,$fullname) = &getclasslist('all','0');      my (undef,undef,$fullname) = &getclasslist('all','0');
       
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {      my $receiptparts=0;
       if ($env{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; }
       my $parts=['0'];
       if ($receiptparts) { ($parts)=&response_type($symb); }
       foreach (sort 
        {
    if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
        return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
    }
    return $a cmp $b;
        } (keys(%$fullname))) {
  my ($uname,$udom)=split(/\:/);   my ($uname,$udom)=split(/\:/);
  if ($receipt eq    foreach my $part (@$parts) {
     &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {      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></tr>'."\n";      '<td>&nbsp;'.$udom.'&nbsp;</td>';
        if ($receiptparts) {
     $matches++;      $contents.='<td>&nbsp;'.$part.'&nbsp;</td>';
    }
    $contents.='</tr>'."\n";
   
    $matches++;
       }
  }   }
     }      }
     if ($matches == 0) {      if ($matches == 0) {
  $string = $title.'No match found for the above receipt.';   $string = $title.'No match found for the above receipt.';
     } else {      } else {
  $string = &jscriptNform($url,$symb).$title.   $string = &jscriptNform($symb).$title.
     'The above receipt matches the following student'.      'The above receipt matches the following student'.
     ($matches <= 1 ? '.' : 's.')."\n".      ($matches <= 1 ? '.' : 's.')."\n".
     '<table border="0"><tr><td bgcolor="#777777">'."\n".      '<table border="0"><tr><td bgcolor="#777777">'."\n".
     '<table border="0"><tr bgcolor="#e6ffff">'."\n".      '<table border="0"><tr bgcolor="#e6ffff">'."\n".
     '<td><b>&nbsp;Fullname&nbsp;</b></td>'."\n".      '<td><b>&nbsp;Fullname&nbsp;</b></td>'."\n".
     '<td><b>&nbsp;Username&nbsp;</b></td>'."\n".      '<td><b>&nbsp;Username&nbsp;</b></td>'."\n".
     '<td><b>&nbsp;Domain&nbsp;</b></td></tr>'."\n".      '<td><b>&nbsp;Domain&nbsp;</b></td>';
     $contents.   if ($receiptparts) {
       $string.='<td>&nbsp;Problem Part&nbsp;</td>';
    }
    $string.='</tr>'."\n".$contents.
     '</table></td></tr></table>'."\n";      '</table></td></tr></table>'."\n";
     }      }
     return $string.&show_grading_menu_form($symb,$url);      return $string.&show_grading_menu_form($symb);
 }  }
   
 #--- This is called by a number of programs.  #--- This is called by a number of programs.
Line 418  sub verifyreceipt { Line 650  sub verifyreceipt {
 sub listStudents {  sub listStudents {
     my ($request) = shift;      my ($request) = shift;
   
     my ($symb,$url) = &get_symb_and_url($request);      my ($symb) = &get_symb($request);
     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"};
     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
     my $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'};      my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
   
     my $viewgrade = $ENV{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';      my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
     $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ?       $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
  &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};   &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
   
     my $result='<h3><font color="#339933">&nbsp;'.$viewgrade.      my $result='<h3><font color="#339933">&nbsp;'.$viewgrade.
  ' Submissions for a Student or a Group of Students</font></h3>';   ' Submissions for a Student or a Group of Students</font></h3>';
   
     my ($table,$resptype,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'});      my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
     $result.=$table;  
   
     $request->print(<<LISTJAVASCRIPT);      $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
Line 453  sub listStudents { Line 684  sub listStudents {
     sense = "the student";      sense = "the student";
  }   }
  if (ctr == 0) {   if (ctr == 0) {
     alert("Please select "+sense+" before clicking on the $viewgrade button.");      alert("Please select "+sense+" before clicking on the Next button.");
     return false;      return false;
  }   }
  document.gradesub.submit();   document.gradesub.submit();
Line 470  LISTJAVASCRIPT Line 701  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' : '';
     my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';      my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".      my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
  '&nbsp;<b>View Problem Text: </b><input type="radio" name="vProb" value="no" checked /> no '."\n".   "\n".$table.
  '<input type="radio" name="vProb" value="yes" /> one student '."\n".   '&nbsp;<b>View Problem Text: </b><label><input type="radio" name="vProb" value="no" checked="on" /> no </label>'."\n".
  '<input type="radio" name="vProb" value="all" /> all students <br />'."\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".
    '&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="all" checked="on" /> 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.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only'."\n";   $gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";
     }      }
   
     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.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last sub only'."\n".      $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".
  '<input type="radio" name="lastSub" value="last" /> last sub & parts info'."\n".   '<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".
  '<input type="radio" name="lastSub" value="all" /> all details'."\n".   '<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n".
    '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n".
  '<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".   '<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="response"    value="'.$ENV{'form.response'}.'" />'."\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="url"  value="'.$url.'" />'."\n".  
  '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
  '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
   
     $gradeTable.='<b>Student Status:</b> '.      if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
  &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />';   $gradeTable.='<input type="hidden" name="Status"   value="'.$env{'form.Status'}.'" />'."\n";
       } else {
    $gradeTable.='<b>Student Status:</b> '.
       &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />';
       }
   
     $gradeTable.='To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n".      $gradeTable.='To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '.
  'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n".   'next to the student\'s name(s). Then click on the Next button.<br />'."\n".
  ' on the check boxes for the group of students.<br />'."\n".  
  '<input type="hidden" name="command" value="processGroup" />'."\n";   '<input type="hidden" name="command" value="processGroup" />'."\n";
   
   # checkall buttons
       $gradeTable.=&check_script('gradesub', 'stuinfo');
     $gradeTable.='<input type="button" '."\n".      $gradeTable.='<input type="button" '."\n".
  'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".   'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
  'value="'.$viewgrade.'" />'."\n";   'value="Next->" /> <br />'."\n";
       $gradeTable.=&check_buttons();
     my (undef, undef, $fullname) = &getclasslist($getsec,'1');        $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="on" />Check For Plagiarism</label>';
       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">';
     my $loop = 0;      my $loop = 0;
     while ($loop < 2) {      while ($loop < 2) {
  $gradeTable.='<td><b>&nbsp;Select&nbsp;</b></td><td><b>&nbsp;Fullname&nbsp;</b>'.   $gradeTable.='<td><b>&nbsp;No.</b>&nbsp;</td><td><b>&nbsp;Select&nbsp;</b></td>'.
     '<font color="#999999">(Username)</font>&nbsp;</td>';      '<td>'.&nameUserString('header').'&nbsp;Section/Group</td>';
  if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {   if ($env{'form.showgrading'} eq 'yes' 
       && $submitonly ne 'queued'
       && $submitonly ne 'all') {
     foreach (sort(@$partlist)) {      foreach (sort(@$partlist)) {
  $gradeTable.='<td><b>&nbsp;Part '.(split(/_/))[0].' Status&nbsp;</b></td>';   my $display_part=&get_display_part((split(/_/))[0],$symb);
    $gradeTable.='<td><b>&nbsp;Part: '.$display_part.
       ' Status&nbsp;</b></td>';
     }      }
    } elsif ($submitonly eq 'queued') {
       $gradeTable.='<td><b>&nbsp;'.&mt('Queue Status').'&nbsp;</b></td>';
  }   }
  $loop++;   $loop++;
   # $gradeTable.='<td></td>' if ($loop%2 ==1);
     }      }
     $gradeTable.='</tr>'."\n";      $gradeTable.='</tr>'."\n";
   
     my $ctr = 0;      my $ctr = 0;
     foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {      foreach my $student (sort 
    {
        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
    return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
        }
        return $a cmp $b;
    }
    (keys(%$fullname))) {
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
   
  my %status = ();   my %status = ();
  if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {  
     (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);   if ($submitonly eq 'queued') {
     my $statusflg = '';      my %queue_status = 
    &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
    $udom,$uname);
       next if (!defined($queue_status{'gradingqueue'}));
       $status{'gradingqueue'} = $queue_status{'gradingqueue'};
    }
   
    if ($env{'form.showgrading'} eq 'yes' 
       && $submitonly ne 'queued'
       && $submitonly ne 'all') {
       (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);
       my $submitted = 0;
       my $graded = 0;
       my $incorrect = 0;
     foreach (keys(%status)) {      foreach (keys(%status)) {
  $statusflg = 1 if ($status{$_} ne 'nothing');   $submitted = 1 if ($status{$_} ne 'nothing');
    $graded = 1 if ($status{$_} =~ /^ungraded/);
    $incorrect = 1 if ($status{$_} =~ /^incorrect/);
   
  my ($foo,$partid,$foo1) = split(/\./,$_);   my ($foo,$partid,$foo1) = split(/\./,$_);
  if ($status{'resource.'.$partid.'.submitted_by'} ne '') {   if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
     $statusflg = '';      $submitted = 0;
       my ($part)=split(/\./,$partid);
     $gradeTable.='<input type="hidden" name="'.      $gradeTable.='<input type="hidden" name="'.
  $student.':submitted_by" value="'.   $student.':'.$part.':submitted_by" value="'.
  $status{'resource.'.$partid.'.submitted_by'}.'" />';   $status{'resource.'.$partid.'.submitted_by'}.'" />';
  }   }
     }      }
     next if ($statusflg eq '' && $submitonly eq 'yes');      
       next if (!$submitted && ($submitonly eq 'yes' ||
        $submitonly eq 'incorrect' ||
        $submitonly eq 'graded'));
       next if (!$graded && ($submitonly eq 'graded'));
       next if (!$incorrect && $submitonly eq 'incorrect');
  }   }
   
  $ctr++;   $ctr++;
    my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
   
  if ( $perm{'vgr'} eq 'F' ) {   if ( $perm{'vgr'} eq 'F' ) {
     $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1);      $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1);
     $gradeTable.='<td align="center"><input type=checkbox name="stuinfo" value="'.      $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.
  $student.':'.$$fullname{$student}.'&nbsp;"></td>'."\n".                 '<td align="center"><label><input type=checkbox name="stuinfo" value="'.
  '<td>&nbsp;'.$$fullname{$student}.'&nbsp;'."\n".                 $student.':'.$$fullname{$student}.':::SECTION'.$section.
  '<font color="#999999">('.$uname.')</font></td>'."\n";         ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
          &nameUserString(undef,$$fullname{$student},$uname,$udom).
          '&nbsp;'.$section.'</td>'."\n";
   
     if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {      if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  foreach (sort keys(%status)) {   foreach (sort keys(%status)) {
     next if (/^resource.*?submitted_by$/);      next if (/^resource.*?submitted_by$/);
     $gradeTable.='<td align="middle">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";      $gradeTable.='<td align="center">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";
  }   }
     }      }
   #    $gradeTable.='<td></td>' if ($ctr%2 ==1);
     $gradeTable.='</tr>'."\n" if ($ctr%2 ==0);      $gradeTable.='</tr>'."\n" if ($ctr%2 ==0);
  }   }
     }      }
     if ($ctr%2 ==1) {      if ($ctr%2 ==1) {
  $gradeTable.='<td>&nbsp;</td><td>&nbsp;</td>';   $gradeTable.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>';
     if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {      if ($env{'form.showgrading'} eq 'yes' 
    && $submitonly ne 'queued'
    && $submitonly ne 'all') {
  foreach (@$partlist) {   foreach (@$partlist) {
     $gradeTable.='<td>&nbsp;</td>';      $gradeTable.='<td>&nbsp;</td>';
  }   }
       } elsif ($submitonly eq 'queued') {
    $gradeTable.='<td>&nbsp;</td>';
     }      }
  $gradeTable.='</tr>';   $gradeTable.='</tr>';
     }      }
   
     $gradeTable.='</table></td></tr></table>'.      $gradeTable.='</table></td></tr></table>'."\n".
  '<input type="button" '.   '<input type="button" '.
  'onClick="javascript:checkSelect(this.form.stuinfo);" '.   'onClick="javascript:checkSelect(this.form.stuinfo);" '.
  'value="'.$viewgrade.'" /></form>'."\n";   'value="Next->" /></form>'."\n";
     if ($ctr == 0) {      if ($ctr == 0) {
  my $num_students=(scalar(keys(%$fullname)));   my $num_students=(scalar(keys(%$fullname)));
  if ($num_students eq 0) {   if ($num_students eq 0) {
     $gradeTable='<br />&nbsp;<font color="red">There are no students currently enrolled.</font>';      $gradeTable='<br />&nbsp;<font color="red">There are no students currently enrolled.</font>';
  } else {   } else {
       my $submissions='submissions';
       if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }
       if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }
       if ($submitonly eq 'queued'   ) { $submissions = 'queued submissions'; }
     $gradeTable='<br />&nbsp;<font color="red">'.      $gradeTable='<br />&nbsp;<font color="red">'.
  'No submissions found for this resource for any students. ('.$num_students.   'No '.$submissions.' found for this resource for any students. ('.$num_students.
  ' checked for submissions</font><br />';   ' students checked for '.$submissions.')</font><br />';
  }   }
     } elsif ($ctr == 1) {      } elsif ($ctr == 1) {
  $gradeTable =~ s/type=checkbox/type=checkbox checked/;   $gradeTable =~ s/type=checkbox/type=checkbox checked/;
     }      }
     $gradeTable.=&show_grading_menu_form($symb,$url);      $gradeTable.=&show_grading_menu_form($symb);
     $request->print($gradeTable);      $request->print($gradeTable);
     return '';      return '';
 }  }
   
 #---- Called from the listStudents routine  #---- Called from the listStudents routine
   
   sub check_script {
       my ($form, $type)=@_;
       my $chkallscript='<script type="text/javascript">
       function checkall() {
           for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
               ele = document.forms.'.$form.'.elements[i];
               if (ele.name == "'.$type.'") {
               document.forms.'.$form.'.elements[i].checked=true;
                                          }
           }
       }
   
       function checksec() {
           for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
               ele = document.forms.'.$form.'.elements[i];
              string = document.forms.'.$form.'.chksec.value;
              if
             (ele.value.indexOf(":::SECTION"+string)>0) {
                 document.forms.'.$form.'.elements[i].checked=true;
               }
           }
       }
   
   
       function uncheckall() {
           for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
               ele = document.forms.'.$form.'.elements[i];
               if (ele.name == "'.$type.'") {
               document.forms.'.$form.'.elements[i].checked=false;
                                          }
           }
       }
   
   </script>'."\n";
       return $chkallscript;
   }
   
   sub check_buttons {
       my $buttons.='<input type="button" onclick="checkall()" value="Check All" />';
       $buttons.='<input type="button" onclick="uncheckall()" value="Uncheck All" />&nbsp;';
       $buttons.='<input type="button" onclick="checksec()" value="Check Section/Group" />';
       $buttons.='<input type="text" size="5" name="chksec" />&nbsp;';
       return $buttons;
   }
   
 #     Displays the submissions for one student or a group of students  #     Displays the submissions for one student or a group of students
 sub processGroup {  sub processGroup {
     my ($request)  = shift;      my ($request)  = shift;
     my $ctr        = 0;      my $ctr        = 0;
     my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}      my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
       : ($ENV{'form.stuinfo'}));  
     my $total      = scalar(@stuchecked)-1;      my $total      = scalar(@stuchecked)-1;
   
     foreach (@stuchecked) {      foreach (@stuchecked) {
  my ($uname,$udom,$fullname) = split(/:/);   my ($uname,$udom,$fullname) = split(/:/);
  $ENV{'form.student'}        = $uname;   $env{'form.student'}        = $uname;
  $ENV{'form.userdom'}        = $udom;   $env{'form.userdom'}        = $udom;
  $ENV{'form.fullname'}       = $fullname;   $env{'form.fullname'}       = $fullname;
  &submission($request,$ctr,$total);   &submission($request,$ctr,$total);
  $ctr++;   $ctr++;
     }      }
Line 624  sub sub_page_js { Line 960  sub sub_page_js {
     $request->print(<<SUBJAVASCRIPT);      $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function updateRadio(formname,id,weight) {      function updateRadio(formname,id,weight) {
  var gradeBox = eval("formname.GD_BOX"+id);   var gradeBox = formname["GD_BOX"+id];
  var radioButton = eval("formname.RADVAL"+id);   var radioButton = formname["RADVAL"+id];
  var oldpts = eval("formname.oldpts"+id+".value");   var oldpts = formname["oldpts"+id].value;
  var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts;   var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts;
  gradeBox.value = pts;   gradeBox.value = pts;
  var resetbox = false;   var resetbox = false;
Line 648  sub sub_page_js { Line 984  sub sub_page_js {
     var resp = confirm("You entered a value ("+pts+      var resp = confirm("You entered a value ("+pts+
        ") greater than the weight for the part. Accept?");         ") greater than the weight for the part. Accept?");
     if (resp == false) {      if (resp == false) {
  gradeBox.value = "";   gradeBox.value = oldpts;
  return;   return;
     }      }
  }   }
Line 660  sub sub_page_js { Line 996  sub sub_page_js {
     }      }
  }   }
  updateSelect(formname,id);   updateSelect(formname,id);
  var stores = eval("formname.stores"+id);   formname["stores"+id].value = "0";
  stores.value = "0";  
     }      }
   
     function writeBox(formname,id,pts) {      function writeBox(formname,id,pts) {
  var gradeBox = eval("formname.GD_BOX"+id);   var gradeBox = formname["GD_BOX"+id];
  if (checkSolved(formname,id) == 'update') {   if (checkSolved(formname,id) == 'update') {
     gradeBox.value = pts;      gradeBox.value = pts;
  } else {   } else {
     var oldpts = eval("formname.oldpts"+id+".value");      var oldpts = formname["oldpts"+id].value;
     gradeBox.value = oldpts;      gradeBox.value = oldpts;
     var radioButton = eval("formname.RADVAL"+id);      var radioButton = formname["RADVAL"+id];
     for (var i=0; i<radioButton.length; i++) {      for (var i=0; i<radioButton.length; i++) {
  radioButton[i].checked=false;   radioButton[i].checked=false;
  if (i == oldpts) {   if (i == oldpts) {
Line 679  sub sub_page_js { Line 1014  sub sub_page_js {
  }   }
     }      }
  }   }
  var stores = eval("formname.stores"+id);   formname["stores"+id].value = "0";
  stores.value = "0";  
  updateSelect(formname,id);   updateSelect(formname,id);
  return;   return;
     }      }
Line 690  sub sub_page_js { Line 1024  sub sub_page_js {
     updateSelect(formname,id);      updateSelect(formname,id);
     return;      return;
  }   }
  gradeSelect = eval("formname.GD_SEL"+id);   gradeSelect = formname["GD_SEL"+id];
  for (var i=0; i<gradeSelect.length; i++) {   for (var i=0; i<gradeSelect.length; i++) {
     if (gradeSelect[i].selected) {      if (gradeSelect[i].selected) {
  var selectx=i;   var selectx=i;
     }      }
  }   }
  var stores = eval("formname.stores"+id);   var stores = formname["stores"+id];
  if (selectx == stores.value) { return };   if (selectx == stores.value) { return };
  var gradeBox = eval("formname.GD_BOX"+id);   var gradeBox = formname["GD_BOX"+id];
  gradeBox.value = "";   gradeBox.value = "";
  var radioButton = eval("formname.RADVAL"+id);   var radioButton = formname["RADVAL"+id];
  for (var i=0; i<radioButton.length; i++) {   for (var i=0; i<radioButton.length; i++) {
     radioButton[i].checked=false;      radioButton[i].checked=false;
  }   }
Line 708  sub sub_page_js { Line 1042  sub sub_page_js {
     }      }
   
     function checkSolved(formname,id) {      function checkSolved(formname,id) {
  if (eval("formname.solved"+id+".value") == "correct_by_student" && formname.overRideScore.value == 'no') {   if (formname["solved"+id].value == "correct_by_student" && formname.overRideScore.value == 'no') {
     var reply = confirm("This problem has been graded correct by the computer. Do you want to change the score?");      var reply = confirm("This problem has been graded correct by the computer. Do you want to change the score?");
     if (!reply) {return "noupdate";}      if (!reply) {return "noupdate";}
     formname.overRideScore.value = 'yes';      formname.overRideScore.value = 'yes';
Line 717  sub sub_page_js { Line 1051  sub sub_page_js {
     }      }
   
     function updateSelect(formname,id) {      function updateSelect(formname,id) {
  var gradeSelect = eval("formname.GD_SEL"+id);   formname["GD_SEL"+id][0].selected = true;
  gradeSelect[0].selected = true;  
  return;   return;
     }      }
   
Line 728  sub sub_page_js { Line 1061  sub sub_page_js {
  if (val == "Save & Next") {   if (val == "Save & Next") {
     for (i=0;i<=total;i++) {      for (i=0;i<=total;i++) {
  for (j=0;j<parttot;j++) {   for (j=0;j<parttot;j++) {
     var partid = eval("formname.partid"+i+"_"+j+".value");      var partid = formname["partid"+i+"_"+j].value;
     var selopt = eval("formname.GD_SEL"+i+"_"+partid);      if (formname["GD_SEL"+i+"_"+partid][0].selected) {
     if (selopt[0].selected) {   var points = formname["GD_BOX"+i+"_"+partid].value;
  var points = eval("formname.GD_BOX"+i+"_"+partid+".value");  
  if (points == "") {   if (points == "") {
     var name = eval("formname.name"+i+".value");      var name = formname["name"+i].value;
     var resp = confirm("You did not assign a score for "+name+", part "+partid+". Continue?");      var studentID = (name != '' ? name : formname["unamedom"+i].value);
       var resp = confirm("You did not assign a score for "+studentID+
          ", part "+partid+". Continue?");
     if (resp == false) {      if (resp == false) {
  eval("formname.GD_BOX"+i+"_"+partid+".focus()");   formname["GD_BOX"+i+"_"+partid].focus();
  return false;   return false;
     }      }
  }   }
Line 761  sub sub_page_js { Line 1095  sub sub_page_js {
  noscore = new Array(100);   noscore = new Array(100);
  var ptr = 0;   var ptr = 0;
  for (i=1;i<total;i++) {   for (i=1;i<total;i++) {
     var partid = eval("formname.q_"+i+".value");      var partid = formname["q_"+i].value;
     var selopt = eval("formname.GD_SEL"+i+"_"+partid);      if (formname["GD_SEL"+i+"_"+partid][0].selected) {
     if (selopt[0].selected) {   var points = formname["GD_BOX"+i+"_"+partid].value;
  var points = eval("formname.GD_BOX"+i+"_"+partid+".value");   var status = formname["solved"+i+"_"+partid].value;
  var status = eval("formname.solved"+i+"_"+partid+".value");  
  if (points == "" && status != "correct_by_student") {   if (points == "" && status != "correct_by_student") {
     noscore[ptr] = i;      noscore[ptr] = i;
     ptr++;      ptr++;
Line 802  sub sub_page_kw_js { Line 1135  sub sub_page_kw_js {
     my $request = shift;      my $request = shift;
     my $iconpath = $request->dir_config('lonIconsURL');      my $iconpath = $request->dir_config('lonIconsURL');
     &commonJSfunctions($request);      &commonJSfunctions($request);
       my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
       $docopen=~s/^document\.//;
     $request->print(<<SUBJAVASCRIPT);      $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
   
 //===================== Show list of keywords ====================  //===================== Show list of keywords ====================
   function keywords(keyform) {    function keywords(formname) {
     var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keyform.value);      var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);
     if (nret==null) return;      if (nret==null) return;
     keyform.value = nret;      formname.keywords.value = nret;
   
     document.SCORE.refresh.value = "on";      if (formname.keywords.value != "") {
     if (document.SCORE.keywords.value != "") {   formname.refresh.value = "on";
  document.SCORE.submit();   formname.submit();
     }      }
     return;      return;
   }    }
Line 839  sub sub_page_kw_js { Line 1174  sub sub_page_kw_js {
     }      }
     var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);      var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
     if (nret==null) return;      if (nret==null) return;
     var curlist = document.SCORE.keywords.value;      document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
     document.SCORE.keywords.value = curlist+" "+nret;  
     document.SCORE.refresh.value = "on";  
     if (document.SCORE.keywords.value != "") {      if (document.SCORE.keywords.value != "") {
    document.SCORE.refresh.value = "on";
  document.SCORE.submit();   document.SCORE.submit();
     }      }
     return;      return;
Line 859  sub sub_page_kw_js { Line 1193  sub sub_page_kw_js {
     var Nmsg  = msgform.savemsgN.value;      var Nmsg  = msgform.savemsgN.value;
     savedMsgHeader(Nmsg,usrctr,fullname);      savedMsgHeader(Nmsg,usrctr,fullname);
     var subject = msgform.msgsub.value;      var subject = msgform.msgsub.value;
     var rtrchk  = eval("document.SCORE.includemsg"+usrctr);      var msgchk = document.SCORE["includemsg"+usrctr].value;
     var msgchk = rtrchk.value;  
     re = /msgsub/;      re = /msgsub/;
     var shwsel = "";      var shwsel = "";
     if (re.test(msgchk)) { shwsel = "checked" }      if (re.test(msgchk)) { shwsel = "checked" }
     displaySubject(subject,shwsel);      subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject);
       displaySubject(checkEntities(subject),shwsel);
     for (var i=1; i<=Nmsg; i++) {      for (var i=1; i<=Nmsg; i++) {
  var testpt = "savemsg"+i+",";   var testmsg = "savemsg"+i+",";
  re = /testpt/;   re = new RegExp(testmsg,"g");
  shwsel = "";   shwsel = "";
  if (re.test(msgchk)) { shwsel = "checked" }   if (re.test(msgchk)) { shwsel = "checked" }
  var message = eval("document.SCORE.savemsg"+i+".value");   var message = document.SCORE["savemsg"+i].value;
  displaySavedMsg(i,message,shwsel);   message = (document.SCORE["shownOnce"+i].value == 0 ? checkEntities(message) : message);
    displaySavedMsg(i,message,shwsel); //I do not get it. w/o checkEntities on saved messages,
                                      //any &lt; is already converted to <, etc. However, only once!!
     }      }
     newmsg = eval("document.SCORE.newmsg"+usrctr+".value");      newmsg = document.SCORE["newmsg"+usrctr].value;
     shwsel = "";      shwsel = "";
     re = /newmsg/;      re = /newmsg/;
     if (re.test(msgchk)) { shwsel = "checked" }      if (re.test(msgchk)) { shwsel = "checked" }
Line 882  sub sub_page_kw_js { Line 1218  sub sub_page_kw_js {
     return;      return;
   }    }
   
     function checkEntities(strx) {
       if (strx.length == 0) return strx;
       var orgStr = ["&", "<", ">", '"']; 
       var newStr = ["&amp;", "&lt;", "&gt;", "&quot;"];
       var counter = 0;
       while (counter < 4) {
    strx = strReplace(strx,orgStr[counter],newStr[counter]);
    counter++;
       }
       return strx;
     }
   
     function strReplace(strx, orgStr, newStr) {
       return strx.split(orgStr).join(newStr);
     }
   
   function savedMsgHeader(Nmsg,usrctr,fullname) {    function savedMsgHeader(Nmsg,usrctr,fullname) {
     var height = 70*Nmsg+250;      var height = 70*Nmsg+250;
     var scrollbar = "no";      var scrollbar = "no";
Line 889  sub sub_page_kw_js { Line 1241  sub sub_page_kw_js {
  height = 600;   height = 600;
  scrollbar = "yes";   scrollbar = "yes";
     }      }
 //    if (window.pWin) {window.pWin.close(); window.pWin=null}  
     var xpos = (screen.width-600)/2;      var xpos = (screen.width-600)/2;
     xpos = (xpos < 0) ? '0' : xpos;      xpos = (xpos < 0) ? '0' : xpos;
     var ypos = (screen.height-height)/2-30;      var ypos = (screen.height-height)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;      ypos = (ypos < 0) ? '0' : ypos;
   
     pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);      pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
     pWin.focus();      pWin.focus();
     pDoc = pWin.document;      pDoc = pWin.document;
       pDoc.$docopen;
     pDoc.write("<html><head>");      pDoc.write("<html><head>");
     pDoc.write("<title>Message Central</title>");      pDoc.write("<title>Message Central</title>");
   
     pDoc.write("<script language=javascript>");      pDoc.write("<script language=javascript>");
     pDoc.write("function checkInput() {");      pDoc.write("function checkInput() {");
     pDoc.write("  opener.document.SCORE.msgsub.value = document.msgcenter.msgsub.value;");      pDoc.write("  opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);");
     pDoc.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");      pDoc.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");
     pDoc.write("  var usrctr = document.msgcenter.usrctr.value;");      pDoc.write("  var usrctr = document.msgcenter.usrctr.value;");
     pDoc.write("  var newval = eval(\\"opener.document.SCORE.newmsg\\"+usrctr);");      pDoc.write("  var newval = opener.document.SCORE[\\"newmsg\\"+usrctr];");
     pDoc.write("  newval.value = document.msgcenter.newmsg.value;");      pDoc.write("  newval.value = opener.checkEntities(document.msgcenter.newmsg.value);");
   
     pDoc.write("  var msgchk = \\"\\";");      pDoc.write("  var msgchk = \\"\\";");
     pDoc.write("  if (document.msgcenter.subchk.checked) {");      pDoc.write("  if (document.msgcenter.subchk.checked) {");
Line 915  sub sub_page_kw_js { Line 1267  sub sub_page_kw_js {
     pDoc.write("  }");      pDoc.write("  }");
     pDoc.write("  var includemsg = 0;");      pDoc.write("  var includemsg = 0;");
     pDoc.write("  for (var i=1; i<=nmsg; i++) {");      pDoc.write("  for (var i=1; i<=nmsg; i++) {");
     pDoc.write("      var opnmsg = eval(\\"opener.document.SCORE.savemsg\\"+i);");      pDoc.write("      var opnmsg = opener.document.SCORE[\\"savemsg\\"+i];");
     pDoc.write("      var frmmsg = eval(\\"document.msgcenter.msg\\"+i);");      pDoc.write("      var frmmsg = document.msgcenter[\\"msg\\"+i];");
     pDoc.write("      opnmsg.value = frmmsg.value;");      pDoc.write("      opnmsg.value = opener.checkEntities(frmmsg.value);");
     pDoc.write("      var chkbox = eval(\\"document.msgcenter.msgn\\"+i);");      pDoc.write("      var showflg = opener.document.SCORE[\\"shownOnce\\"+i];");
       pDoc.write("      showflg.value = \\"1\\";");
       pDoc.write("      var chkbox = document.msgcenter[\\"msgn\\"+i];");
     pDoc.write("      if (chkbox.checked) {");      pDoc.write("      if (chkbox.checked) {");
     pDoc.write("         msgchk += \\"savemsg\\"+i+\\",\\";");      pDoc.write("         msgchk += \\"savemsg\\"+i+\\",\\";");
     pDoc.write("         includemsg = 1;");      pDoc.write("         includemsg = 1;");
Line 928  sub sub_page_kw_js { Line 1282  sub sub_page_kw_js {
     pDoc.write("     msgchk += \\"newmsg\\"+usrctr;");      pDoc.write("     msgchk += \\"newmsg\\"+usrctr;");
     pDoc.write("     includemsg = 1;");      pDoc.write("     includemsg = 1;");
     pDoc.write("  }");      pDoc.write("  }");
     pDoc.write("  imgformname = eval(\\"opener.document.SCORE.mailicon\\"+usrctr);");      pDoc.write("  imgformname = opener.document.SCORE[\\"mailicon\\"+usrctr];");
     pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");      pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");
     pDoc.write("  var includemsg = eval(\\"opener.document.SCORE.includemsg\\"+usrctr);");      pDoc.write("  var includemsg = opener.document.SCORE[\\"includemsg\\"+usrctr];");
     pDoc.write("  includemsg.value = msgchk;");      pDoc.write("  includemsg.value = msgchk;");
   
     pDoc.write("  self.close()");      pDoc.write("  self.close()");
Line 944  sub sub_page_kw_js { Line 1298  sub sub_page_kw_js {
   
     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");      pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");      pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
     pDoc.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><br><br>");      pDoc.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><br /><br />");
   
     pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");      pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
     pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");      pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
Line 979  sub sub_page_kw_js { Line 1333  sub sub_page_kw_js {
     pDoc.write("</table>");      pDoc.write("</table>");
     pDoc.write("</td></tr></table>&nbsp;");      pDoc.write("</td></tr></table>&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");      pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
     pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");      pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
     pDoc.write("</form>");      pDoc.write("</form>");
     pDoc.write("</body></html>");      pDoc.write("</body></html>");
       pDoc.close();
 }  }
   
 //====================== Script for keyword highlight options ==============  //====================== Script for keyword highlight options ==============
Line 1022  sub sub_page_kw_js { Line 1377  sub sub_page_kw_js {
     var ypos = (screen.height-330)/2-30;      var ypos = (screen.height-330)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;      ypos = (ypos < 0) ? '0' : ypos;
   
     hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos);      hwdWin = window.open('', 'KeywordHighlightCentral', 'resizeable=yes,toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos);
     hwdWin.focus();      hwdWin.focus();
     var hDoc = hwdWin.document;      var hDoc = hwdWin.document;
       hDoc.$docopen;
     hDoc.write("<html><head>");      hDoc.write("<html><head>");
     hDoc.write("<title>Highlight Central</title>");      hDoc.write("<title>Highlight Central</title>");
   
Line 1046  sub sub_page_kw_js { Line 1402  sub sub_page_kw_js {
     hDoc.write("</head><body bgcolor=white>");      hDoc.write("</head><body bgcolor=white>");
   
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");      hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
     hDoc.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><br><br>");      hDoc.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><br /><br />");
   
     hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");      hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
     hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");      hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
Line 1070  sub sub_page_kw_js { Line 1426  sub sub_page_kw_js {
     hDoc.write("</table>");      hDoc.write("</table>");
     hDoc.write("</td></tr></table>&nbsp;");      hDoc.write("</td></tr></table>&nbsp;");
     hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");      hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
     hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");      hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
     hDoc.write("</form>");      hDoc.write("</form>");
     hDoc.write("</body></html>");      hDoc.write("</body></html>");
       hDoc.close();
   }    }
   
 </script>  </script>
Line 1082  SUBJAVASCRIPT Line 1439  SUBJAVASCRIPT
 #--- displays the grading box, used in essay type problem and grading by page/sequence  #--- displays the grading box, used in essay type problem and grading by page/sequence
 sub gradeBox {  sub gradeBox {
     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;      my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
   
     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').      my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
   
     my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);      my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
     my $wgtmsg = ($wgt > 0 ? '(problem weight)' :       my $wgtmsg = ($wgt > 0 ? '(problem weight)' : 
   '<font color="red">problem weight assigned by computer</font>');    '<font color="red">problem weight assigned by computer</font>');
     $wgt       = ($wgt > 0 ? $wgt : '1');      $wgt       = ($wgt > 0 ? $wgt : '1');
     my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?      my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
   '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);    '' : &compute_points($$record{'resource.'.$partid.'.awarded'},$wgt));
     my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";      my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
       my $display_part=&get_display_part($partid,$symb);
       my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
          [$partid]);
       my $aggtries = $$record{'resource.'.$partid.'.tries'};
       if ($last_resets{$partid}) {
           $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);
       }
     $result.='<table border="0"><tr><td>'.      $result.='<table border="0"><tr><td>'.
  '<b>Part </b>'.$partid.' <b>Points: </b></td><td>'."\n";   '<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n";
   
     my $ctr = 0;      my $ctr = 0;
     $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across      $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
     while ($ctr<=$wgt) {      while ($ctr<=$wgt) {
  $result.= '<td><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.   $result.= '<td><nobr><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
     'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.      'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
     $ctr.')" value="'.$ctr.'" '.      $ctr.')" value="'.$ctr.'" '.
     ($score eq $ctr ? 'checked':'').' /> '.$ctr."</td>\n";      ($score eq $ctr ? 'checked':'').' /> '.$ctr."</label></nobr></td>\n";
  $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');   $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
  $ctr++;   $ctr++;
     }      }
     $result.='</tr></table>';      $result.='</tr></table>';
   
     $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>'."\n";      $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>'."\n";
     $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.      $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
  ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.   ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
Line 1117  sub gradeBox { Line 1476  sub gradeBox {
     $result.='<td>/'.$wgt.' '.$wgtmsg.      $result.='<td>/'.$wgt.' '.$wgtmsg.
  ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').   ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
  ' </td><td>'."\n";   ' </td><td>'."\n";
   
     $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.      $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
  'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";   'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {      if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
  $result.='<option> </option>'.   $result.='<option> </option>'.
     '<option selected="on">excused</option></select>'."\n";      '<option selected="on">excused</option>';
     } else {      } else {
  $result.='<option selected="on"> </option>'.   $result.='<option selected="on"> </option>'.
     '<option>excused</option></select>'."\n";      '<option>excused</option>';
     }      }
       $result.='<option>reset status</option></select>'."\n";
     $result.="&nbsp&nbsp\n";      $result.="&nbsp&nbsp\n";
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".      $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
  '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".   '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
  '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.   '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
  $$record{'resource.'.$partid.'.solved'}.'" />'."\n";   $$record{'resource.'.$partid.'.solved'}.'" />'."\n".
           '<input type="hidden" name="totaltries'.$counter.'_'.$partid.'" value="'.
           $$record{'resource.'.$partid.'.tries'}.'" />'."\n".
           '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.
           $aggtries.'" />'."\n";
     $result.='</td></tr></table>'."\n";      $result.='</td></tr></table>'."\n";
       $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record);
     return $result;      return $result;
 }  }
   
   sub handback_box {
       my ($symb,$uname,$udom,$counter,$partid,$record) = @_;
       my ($partlist,$handgrade,$responseType) = &response_type($symb);
       my (@respids);
       foreach my $part_resp (sort(keys(%$handgrade))) {
           my ($part,$resp) = split(/_/,$part_resp);
           if ($part eq $partid) {
               push @respids,$resp;
           }
       }
       my $result;
       foreach my $respid (@respids) {
    my $prefix = $counter.'_'.$partid.'_'.$respid.'_';
    my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
    next if (!@$files);
    my $file_counter = 1;
    foreach my $file (@$files) {
       my ($file_disp) = ($file =~ m|.+/(.+)$|);
       $result.=&mt('Return commented version of [_1] to student.',
    '<span class="filename">'.$file_disp.'</span>');
       $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";
       $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';
       $file_counter++;
    }
       }
       return $result;    
   }
   
 sub show_problem {  sub show_problem {
     my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_;      my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode) = @_;
     my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,      my $rendered;
       $ENV{'request.course.id'});      &Apache::lonxml::remember_problem_counter();
       if ($mode eq 'both' or $mode eq 'text') {
    $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
        $env{'request.course.id'});
       }
     if ($removeform) {      if ($removeform) {
  $rendered=~s|<form(.*?)>||g;   $rendered=~s|<form(.*?)>||g;
  $rendered=~s|</form>||g;   $rendered=~s|</form>||g;
  $rendered=~s|name="submit"|name="would_have_been_submit"|g;   $rendered=~s|name="submit"|name="would_have_been_submit"|g;
     }      }
     my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,      my $companswer;
    $ENV{'request.course.id'});      if ($mode eq 'both' or $mode eq 'answer') {
    &Apache::lonxml::restore_problem_counter();
    $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
       $env{'request.course.id'});
       }
     if ($removeform) {      if ($removeform) {
  $companswer=~s|<form(.*?)>||g;   $companswer=~s|<form(.*?)>||g;
  $companswer=~s|</form>||g;   $companswer=~s|</form>||g;
  $rendered=~s|name="submit"|name="would_have_been_submit"|g;   $companswer=~s|name="submit"|name="would_have_been_submit"|g;
     }      }
     my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';      my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
     $result.='<table border="0" width="100%">';      $result.='<table border="0" width="100%">';
     $result.='<tr><td bgcolor="#e6ffff"><b> View of the problem - '.$ENV{'form.fullname'}.      if ($viewon) {
  '</b></td></tr>' if ($viewon);   $result.='<tr><td bgcolor="#e6ffff"><b> ';
     $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';   if ($mode eq 'both' or $mode eq 'text') {
     $result.='<b>Correct answer:</b><br />'.$companswer;      $result.='View of the problem - ';
    } else {
       $result.='Correct answer: ';
    }
    $result.=$env{'form.fullname'}.'</b></td></tr>';
       }
       if ($mode eq 'both') {
    $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';
    $result.='<b>Correct answer:</b><br />'.$companswer;
       } elsif ($mode eq 'text') {
    $result.='<tr><td bgcolor="#ffffff">'.$rendered;
       } elsif ($mode eq 'answer') {
    $result.='<tr><td bgcolor="#ffffff">'.$companswer;
       }
     $result.='</td></tr></table>';      $result.='</td></tr></table>';
     $result.='</td></tr></table><br />';      $result.='</td></tr></table><br />';
     return $result;      return $result;
Line 1167  sub show_problem { Line 1580  sub show_problem {
 sub submission {  sub submission {
     my ($request,$counter,$total) = @_;      my ($request,$counter,$total) = @_;
   
     (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;      my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});
     my ($uname,$udom)     = ($ENV{'form.student'},$ENV{'form.userdom'});      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     $udom = ($udom eq '' ? $ENV{'user.domain'} : $udom); #has form.userdom changed for a student?      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     my $usec = &Apache::lonnet::getsection($udom,$uname,$ENV{'request.course.id'});      $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
     $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';  
   
     my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));      my $symb = &get_symb($request); 
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print('<font color="red">Unable to view requested student.('.   $request->print('<font color="red">Unable to view requested student.('.
  $uname.$udom.$usec.$ENV{'request.course.id'}.')</font>');   $uname.'@'.$udom.' in section '.$usec.' in course id '.
  $request->print(&show_grading_menu_form($symb,$url));   $env{'request.course.id'}.')</font>');
    $request->print(&show_grading_menu_form($symb));
  return;   return;
     }      }
   
     $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'all' : $ENV{'form.lastSub'});      if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
     my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');      if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
       if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
       my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
       my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
    '/check.gif" height="16" border="0" />';
   
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
  &sub_page_js($request);   &sub_page_js($request);
  &sub_page_kw_js($request) if ($ENV{'form.handgrade'} eq 'yes');   &sub_page_kw_js($request) if ($env{'form.handgrade'} eq 'yes');
  $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ?    $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
     &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};      &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
   
  $request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".   $request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".
  '<font size=+1>&nbsp;<b>Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n");   '<font size=+1>&nbsp;<b>Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n");
   
  if ($ENV{'form.handgrade'} eq 'no') {   if ($env{'form.handgrade'} eq 'no') {
     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').  
  '/check.gif" height="16" border="0" />';  
     my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.      my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.
  $checkIcon.' symbol.'."\n";   $checkIcon.' symbol.'."\n";
     $request->print($checkMark);      $request->print($checkMark);
Line 1206  sub submission { Line 1621  sub submission {
   
  # option to display problem, only once else it cause problems    # option to display problem, only once else it cause problems 
         # with the form later since the problem has a form.          # with the form later since the problem has a form.
  if ($ENV{'form.vProb'} eq 'yes' or !$ENV{'form.vProb'}) {   if ($env{'form.vProb'} eq 'yes' or $env{'form.vAns'} eq 'yes') {
     $request->print(&show_problem($request,$symb,$uname,$udom,0,1));      my $mode;
       if ($env{'form.vProb'} eq 'yes' && $env{'form.vAns'} eq 'yes') {
    $mode='both';
       } elsif ($env{'form.vProb'} eq 'yes') {
    $mode='text';
       } elsif ($env{'form.vAns'} eq 'yes') {
    $mode='answer';
       }
       &Apache::lonxml::clear_problem_counter();
       $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));
  }   }
   
  # kwclr is the only variable that is guaranteed to be non blank    # kwclr is the only variable that is guaranteed to be non blank 
         # if this subroutine has been called once.          # if this subroutine has been called once.
  my %keyhash = ();   my %keyhash = ();
  if ($ENV{'form.kwclr'} eq '' && $ENV{'form.handgrade'} eq 'yes') {   if ($env{'form.kwclr'} eq '' && $env{'form.handgrade'} eq 'yes') {
     %keyhash = &Apache::lonnet::dump('nohist_handgrade',      %keyhash = &Apache::lonnet::dump('nohist_handgrade',
      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},       $env{'course.'.$env{'request.course.id'}.'.domain'},
      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});       $env{'course.'.$env{'request.course.id'}.'.num'});
   
     my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};  
     $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';  
     $ENV{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';  
     $ENV{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';  
     $ENV{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';  
     $ENV{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ?   
  $keyhash{$symb.'_subject'} : $ENV{'form.probTitle'};  
     $ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';  
   
       my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
       $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
       $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
       $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
       $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
       $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ? 
    $keyhash{$symb.'_subject'} : $env{'form.probTitle'};
       $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">'."\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".
  '<input type="hidden" name="Status"     value="'.$ENV{'form.Status'}.'" />'."\n".   '<input type="hidden" name="Status"     value="'.$env{'form.Status'}.'" />'."\n".
  '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".   '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\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="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="'.$symb.'" />'."\n".
  '<input type="hidden" name="url"        value="'.$url.'" />'."\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="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".   '<input type="hidden" name="section"    value="'.$env{'form.section'}.'" />'."\n".
  '<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n".   '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".
  '<input type="hidden" name="response"   value="'.$ENV{'form.response'}.'">'."\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="keywords"   value="'.$ENV{'form.keywords'}.'" />'."\n".  
  '<input type="hidden" name="kwclr"      value="'.$ENV{'form.kwclr'}.'" />'."\n".  
  '<input type="hidden" name="kwsize"     value="'.$ENV{'form.kwsize'}.'" />'."\n".  
  '<input type="hidden" name="kwstyle"    value="'.$ENV{'form.kwstyle'}.'" />'."\n".  
  '<input type="hidden" name="msgsub"     value="'.$ENV{'form.msgsub'}.'" />'."\n".  
  '<input type="hidden" name="savemsgN"   value="'.$ENV{'form.savemsgN'}.'" />'."\n".  
  '<input type="hidden" name="NCT"'.   '<input type="hidden" name="NCT"'.
  ' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : $total+1).'" />'."\n");   ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");
    if ($env{'form.handgrade'} eq 'yes') {
       $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".
       '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".
       '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".
       '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n".
       '<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".
       '<input type="hidden" name="shownSub" value="0" />'."\n".
       '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");
       foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
    $request->print('<input type="hidden" name="vPart" value="'.$partid.'" />'."\n");
       }
    }
   
  my ($cts,$prnmsg) = (1,'');   my ($cts,$prnmsg) = (1,'');
  while ($cts <= $ENV{'form.savemsgN'}) {   while ($cts <= $env{'form.savemsgN'}) {
     $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.      $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
  ($keyhash{$symb.'_savemsg'.$cts} eq '' ?    (!exists($keyhash{$symb.'_savemsg'.$cts}) ? 
  &Apache::lonfeedback::clear_out_html($ENV{'form.savemsg'.$cts}) :   &Apache::lonfeedback::clear_out_html($env{'form.savemsg'.$cts}) :
  &Apache::lonfeedback::clear_out_html($keyhash{$symb.'_savemsg'.$cts})).   &Apache::lonfeedback::clear_out_html($keyhash{$symb.'_savemsg'.$cts})).
  '" />'."\n";   '" />'."\n".
    '<input type="hidden" name="shownOnce'.$cts.'" value="0" />'."\n";
     $cts++;      $cts++;
  }   }
  $request->print($prnmsg);   $request->print($prnmsg);
   
  if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') {   if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') {
 #  #
 # Print out the keyword options line  # Print out the keyword options line
 #  #
     $request->print(<<KEYWORDS);      $request->print(<<KEYWORDS);
 &nbsp;<b>Keyword Options:</b>&nbsp;  &nbsp;<b>Keyword Options:</b>&nbsp;
 <a href="javascript:keywords(document.SCORE.keywords)"; 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 />
Line 1282  KEYWORDS Line 1711  KEYWORDS
 #  #
 # Load the other essays for similarity check  # Load the other essays for similarity check
 #  #
             my $essayurl=&Apache::lonnet::declutter($url);              my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
     my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/);      my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/);
     $apath=&Apache::lonnet::escape($apath);      $apath=&Apache::lonnet::escape($apath);
     $apath=~s/\W/\_/gs;      $apath=~s/\W/\_/gs;
Line 1290  KEYWORDS Line 1719  KEYWORDS
         }          }
     }      }
   
     if ($ENV{'form.vProb'} eq 'all') {      if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {
  $request->print('<br /><br /><br />') if ($counter > 0);   $request->print('<br /><br /><br />') if ($counter > 0);
  $request->print(&show_problem($request,$symb,$uname,$udom,1,1));   my $mode;
    if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {
       $mode='both';
    } elsif ($env{'form.vProb'} eq 'all' ) {
       $mode='text';
    } elsif ($env{'form.vAns'} eq 'all') {
       $mode='answer';
    }
    &Apache::lonxml::clear_problem_counter();
    $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode));
     }      }
     my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);  
     my ($partlist,$handgrade) = &response_type($url);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
       my ($partlist,$handgrade,$responseType) = &response_type($symb);
   
     # Display student info      # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));      $request->print(($counter == 0 ? '' : '<br />'));
     my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n".      my $result='<table border="0" width="100%"><tr><td bgcolor="#777777">'."\n".
  '<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n";   '<table border="0" width="100%"><tr bgcolor="#edffff"><td>'."\n";
   
     $result.='<b>Fullname: </b>'.$ENV{'form.fullname'}.      $result.='<b>Fullname: </b>'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'<br />'."\n";
  '<font color="#999999">&nbsp; &nbsp;Username: '.$uname.  
  ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').'</font><br />'."\n";  
     $result.='<input type="hidden" name="name'.$counter.      $result.='<input type="hidden" name="name'.$counter.
  '" value="'.$ENV{'form.fullname'}.'" />'."\n";   '" value="'.$env{'form.fullname'}.'" />'."\n";
   
     # If any part of the problem is an essay-response (handgraded), then check for collaborators      # If any part of the problem is an essay-response (handgraded), then check for collaborators
     my @col_fullnames;      my @col_fullnames;
     my ($classlist,$fullname);      my ($classlist,$fullname);
     if ($ENV{'form.handgrade'} eq 'yes') {      if ($env{'form.handgrade'} eq 'yes') {
  ($classlist,undef,$fullname) = &getclasslist('all','0');   ($classlist,undef,$fullname) = &getclasslist('all','0');
  for (keys (%$handgrade)) {   for (keys (%$handgrade)) {
     my $ncol = &Apache::lonnet::EXT('resource.'.$_.      my $ncol = &Apache::lonnet::EXT('resource.'.$_.
Line 1344  KEYWORDS Line 1781  KEYWORDS
     $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';      $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';
  }   }
                 $result.='<br />'."\n";                  $result.='<br />'."\n";
    my ($part)=split(/\./,$_);
  $result.='<input type="hidden" name="collaborator'.$counter.   $result.='<input type="hidden" name="collaborator'.$counter.
     '" value="'.(join ':',@goodcollaborators).'" />'."\n";      '" value="'.$part.':'.(join ':',@goodcollaborators).'" />'.
       "\n";
     }      }
     if (scalar(@badcollaborators) > 0) {      if (scalar(@badcollaborators) > 0) {
  $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';   $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
Line 1370  KEYWORDS Line 1809  KEYWORDS
     #                  (for multi-response type part)      #                  (for multi-response type part)
     #             (3) Last submission plus the parts info      #             (3) Last submission plus the parts info
     #             (4) The whole record for this student      #             (4) The whole record for this student
     if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {      if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
  if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) {   my ($string,$timestamp)= &get_last_submission(\%record);
     my $submitby=''.   my $lastsubonly=''.
  '<b>Collaborative submission by: </b>'.      ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
  '<a href="javascript:viewSubmitter(\''.       $$timestamp)."</td></tr>\n";
  $ENV{'form.'.$uname.':'.$udom.':submitted_by'}.   if ($$timestamp eq '') {
  '\')"; TARGET=_self>'.      $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; 
  $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'</a>';   } else {
     $request->print($submitby);      my %seenparts;
  } else {      for my $part (sort keys(%$handgrade)) {
     my ($string,$timestamp)= &get_last_submission (\%record);   my ($partid,$respid) = split(/_/,$part);
     my $lastsubonly=''.   my $display_part=&get_display_part($partid,$symb);
  ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.   if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
  $$timestamp)."</td></tr>\n";      if (exists($seenparts{$partid})) { next; }
     if ($$timestamp eq '') {      $seenparts{$partid}=1;
  $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0];       my $submitby='<b>Part:</b> '.$display_part.
     } else {   ' <b>Collaborative submission by:</b> '.
  for my $part (sort keys(%$handgrade)) {   '<a href="javascript:viewSubmitter(\''.
     my ($responsetype,$foo) = split(/:/,$$handgrade{$part});   $env{"form.$uname:$udom:$partid:submitted_by"}.
     my ($partid,$respid) = split(/_/,$part);   '\')"; TARGET=_self>'.
     if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) {   $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';
  $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.      $request->print($submitby);
     $partid.'</b> <font color="#999999">( ID '.$respid.      next;
     ' )</font>&nbsp; &nbsp;Nothing submitted<br /><br />';   }
      } else {   my $responsetype = $responseType->{$partid}->{$respid};
  foreach (@$string) {   if (!exists($record{"resource.$partid.$respid.submission"})) {
     my ($partid,$respid) = /^resource\.(\w+)\.(\w+)\.submission/;      $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
     if ($part eq ($partid.'_'.$respid)) {   $display_part.' <font color="#999999">( ID '.$respid.
  my ($ressub,$subval) = split(/:/,$_,2);   ' )</font>&nbsp; &nbsp;'.
                             # Similarity check   '<font color="red">Nothing submitted - no attempts</font><br /><br />';
  my $similar='';      next;
  my ($oname,$odom,$ocrsid,$oessay,$osim)=&most_similar($uname,$udom,$subval);   }
  if ($osim) {   foreach (@$string) {
     $osim=int($osim*100.0);      my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;
     $similar='<hr /><h3><font color="#FF0000">Essay is '.$osim.      if ($part ne ($partid.'_'.$respid)) { next; }
  '% similar to an essay by '.&Apache::loncommon::plainname($oname,$odom).      my ($ressub,$subval) = split(/:/,$_,2);
  '</font></h3><blockquote><i>'.      # Similarity check
  &keywords_highlight($oessay).'</i></blockquote><hr />';      my $similar='';
  }      if($env{'form.checkPlag'}){
  $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.   my ($oname,$odom,$ocrsid,$oessay,$osim)=
     $partid.'</b> <font color="#999999">( ID '.$respid.      &most_similar($uname,$udom,$subval);
     ' )</font>&nbsp; &nbsp;'.   if ($osim) {
     ($record{"resource.$partid.$respid.uploadedurl"}?      $osim=int($osim*100.0);
      '<a href="'.      $similar="<hr /><h3><font color=\"#FF0000\">Essay".
      &Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).   " is $osim% similar to an essay by ".
      '"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> '.   &Apache::loncommon::plainname($oname,$odom).
      '<font color="red" size="1">Like all files provided by users, '.   '</font></h3><blockquote><i>'.
      'this file may contain virusses</font><br />':'').   &keywords_highlight($oessay).
      '<b>Submitted Answer: </b>'.($responsetype =~ /^(essay|option)$/ ?    '</i></blockquote><hr />';
   '<blockquote>' : '').   }
      &cleanRecord(&keywords_highlight($subval),$responsetype).      }
      ($responsetype =~ /^(essay|option)$/ ? '</blockquote><br />' :       my $order=&get_order($partid,$respid,$symb,$uname,$udom);
       '<br /><br />').$similar."\n"      if ($env{'form.lastSub'} eq 'lastonly' || 
      if ($ENV{'form.lastSub'} eq 'lastonly' ||    ($env{'form.lastSub'} eq 'hdgrade' && 
  ($ENV{'form.lastSub'} eq 'hdgrade' &&    $$handgrade{$part} eq 'yes')) {
   $$handgrade{$part} =~ /:yes$/));   my $display_part=&get_display_part($partid,$symb);
    $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
       $display_part.' <font color="#999999">( ID '.$respid.
       ' )</font>&nbsp; &nbsp;';
    my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
    if (@$files) {
       $lastsubonly.='<br /><font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />';
       my $file_counter = 0;
       foreach my $file (@$files) {
           $file_counter ++;
    &Apache::lonnet::allowuploaded('/adm/grades',$file);
    $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>';
     }      }
       $lastsubonly.='<br />';
  }   }
    $lastsubonly.='<b>Submitted Answer: </b>'.
       &cleanRecord($subval,$responsetype,$symb,$partid,
    $respid,\%record,$order);
    if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
     }      }
  }   }
     }      }
     $lastsubonly.='</td></tr><tr bgcolor="#ffffff"><td>'."\n";  
     $request->print($lastsubonly);  
  }   }
     } else {   $lastsubonly.='</td></tr><tr bgcolor="#ffffff"><td>'."\n";
    $request->print($lastsubonly);
       } elsif ($env{'form.lastSub'} eq 'datesub') {
    my (undef,$responseType,undef,$parts) = &showResourceInfo($symb);
    $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
       } elsif ($env{'form.lastSub'} =~ /^(last|all)$/) {
  $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,   $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
  $ENV{'request.course.id'},   $env{'request.course.id'},
  $last,'.submission',   $last,'.submission',
  'Apache::grades::keywords_highlight'));   'Apache::grades::keywords_highlight'));
     }      }
Line 1445  KEYWORDS Line 1903  KEYWORDS
  .$udom.'" />'."\n");   .$udom.'" />'."\n");
           
     # return if view submission with no grading option      # return if view submission with no grading option
     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></form>'."\n";   $toGrade.='</td></tr></table></td></tr></table>'."\n";
  $toGrade.=&show_grading_menu_form($symb,$url)    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));      $toGrade.='</form>'.&show_grading_menu_form($symb); 
  $request = print($toGrade);   }
    $request->print($toGrade);
  return;   return;
       } else {
    $request->print('</td></tr></table></td></tr></table>'."\n");
     }      }
   
     # essay grading message center      # essay grading message center
     if ($ENV{'form.handgrade'} eq 'yes') {      if ($env{'form.handgrade'} eq 'yes') {
  my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'});   my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
  my $msgfor = $givenn.' '.$lastname;   my $msgfor = $givenn.' '.$lastname;
  if (scalar(@col_fullnames) > 0) {   if (scalar(@col_fullnames) > 0) {
     my $lastone = pop @col_fullnames;      my $lastone = pop @col_fullnames;
     $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';      $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';
  }   }
  $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript   $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
 # $result.='<tr><td bgcolor="#ffffff">'."\n".  
  $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>'.
     'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> &nbsp;'.      &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> ('.
       &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" />)'.
     '<img src="'.$request->dir_config('lonIconsURL').      '<img src="'.$request->dir_config('lonIconsURL').
     '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".      '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".
     '<br />&nbsp;(Message will be sent when you click on Save & Next below.)'."\n"       '<br />&nbsp;('.
     if ($ENV{'form.handgrade'} eq 'yes');      &mt('Message will be sent when you click on Save & Next below.').")\n";
  $request->print($result);   $request->print($result);
     }      }
       if ($perm{'vgr'}) {
    $request->print('<br />'.
       &Apache::loncommon::track_student_link(&mt('View recent activity'),
      $uname,$udom,'check'));
       }
       if ($perm{'opa'}) {
    $request->print('<br />'.
       &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
    $uname,$udom,$symb,'check'));
       }
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     for (sort keys(%$handgrade)) {      my @gradePartRespid;
  my ($partid,$respid) = split(/_/);      for my $part_resp (sort(keys(%$handgrade))) {
    my ($partid,$respid) = split(/_/, $part_resp);
  next if ($seen{$partid} > 0);   next if ($seen{$partid} > 0);
  $seen{$partid}++;   $seen{$partid}++;
  next if ($$handgrade{$_} =~ /:no$/ && $ENV{'form.lastSub'} =~ /^(hdgrade)$/);   next if ($$handgrade{$part_resp} =~ /:no$/ && $env{'form.lastSub'} =~ /^(hdgrade)$/);
  push @partlist,$partid;   push @partlist,$partid;
    push @gradePartRespid,$partid.'.'.$respid;
  $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));   $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
     }      }
     $result='<input type="hidden" name="partlist'.$counter.      $result='<input type="hidden" name="partlist'.$counter.
  '" value="'.(join ":",@partlist).'" />'."\n";   '" value="'.(join ":",@partlist).'" />'."\n";
       $result.='<input type="hidden" name="gradePartRespid'.
    '" value="'.(join ":",@gradePartRespid).'" />'."\n" if ($counter == 0);
     my $ctr = 0;      my $ctr = 0;
     while ($ctr < scalar(@partlist)) {      while ($ctr < scalar(@partlist)) {
  $result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'.   $result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'.
Line 1510  KEYWORDS Line 1984  KEYWORDS
     '<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="on">$nsel</;
  $endform.=$ntstu.'student(s) &nbsp;&nbsp;';   $endform.=$ntstu.'student(s) &nbsp;&nbsp;';
  $endform.='<input type="button" value="Next" '.   $endform.='<input type="button" value="Previous" '.
     'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;'."\n".      'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;'."\n".
     '<input type="button" value="Previous" '.      '<input type="button" value="Next" '.
     'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;';      'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;';
  $endform.='(Next and Previous do not save the scores.)'."\n" ;   $endform.='(Next and Previous (student) do not save the scores.)'."\n" ;
  $endform.='</td><tr></table></form>';   $endform.='</td><tr></table></form>';
  $endform.=&show_grading_menu_form($symb,$url);   $endform.=&show_grading_menu_form($symb);
  $request->print($endform);   $request->print($endform);
     }      }
     return '';      return '';
Line 1547  sub get_last_submission { Line 2021  sub get_last_submission {
     }      }
  }   }
     }      }
     @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;      @string = $string[0] eq '' ? '<font color="red">Nothing submitted - no attempts.</font>' : @string;
     return \@string,\$timestamp;      return \@string,\$timestamp;
 }  }
   
 #--- High light keywords, with style choosen by user.  #--- High light keywords, with style choosen by user.
 sub keywords_highlight {  sub keywords_highlight {
     my $string    = shift;      my $string    = shift;
     my $size      = $ENV{'form.kwsize'} eq '0' ? '' : 'size='.$ENV{'form.kwsize'};      my $size      = $env{'form.kwsize'} eq '0' ? '' : 'size='.$env{'form.kwsize'};
     my $styleon   = $ENV{'form.kwstyle'} eq ''  ? '' : $ENV{'form.kwstyle'};      my $styleon   = $env{'form.kwstyle'} eq ''  ? '' : $env{'form.kwstyle'};
     (my $styleoff = $styleon) =~ s/\</\<\//;      (my $styleoff = $styleon) =~ s/\</\<\//;
     my @keylist   = split(/[,\s+]/,$ENV{'form.keywords'});      my @keylist   = split(/[,\s+]/,$env{'form.keywords'});
     foreach (@keylist) {      foreach (@keylist) {
  $string =~ s/\b\Q$_\E(\b|\.)/<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff<\/font>/gi;   $string =~ s/\b\Q$_\E(\b|\.)/<font color\=$env{'form.kwclr'} $size\>$styleon$_$styleoff<\/font>/gi;
     }      }
     return $string;      return $string;
 }  }
Line 1567  sub keywords_highlight { Line 2041  sub keywords_highlight {
 #--- Called from submission routine  #--- Called from submission routine
 sub processHandGrade {  sub processHandGrade {
     my ($request) = shift;      my ($request) = shift;
     my $url    = $ENV{'form.url'};      my $symb   = &get_symb($request);
     my $symb   = $ENV{'form.symb'};      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     my $button = $ENV{'form.gradeOpt'};      my $button = $env{'form.gradeOpt'};
     my $ngrade = $ENV{'form.NCT'};      my $ngrade = $env{'form.NCT'};
     my $ntstu  = $ENV{'form.NTSTU'};      my $ntstu  = $env{'form.NTSTU'};
       my $cdom   = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum   = $env{'course.'.$env{'request.course.id'}.'.num'};
   
     if ($button eq 'Save & Next') {      if ($button eq 'Save & Next') {
  my $ctr = 0;   my $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});      my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
     my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);      my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$symb,$uname,$udom,$ctr);
     if ($errorflag eq 'no_score') {      if ($errorflag eq 'no_score') {
  $ctr++;   $ctr++;
  next;   next;
Line 1586  sub processHandGrade { Line 2063  sub processHandGrade {
  $ctr++;   $ctr++;
  next;   next;
     }      }
     my $includemsg = $ENV{'form.includemsg'.$ctr};      my $includemsg = $env{'form.includemsg'.$ctr};
     my ($subject,$message,$msgstatus) = ('','','');      my ($subject,$message,$msgstatus) = ('','','');
     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'); }
  my (@msgnum) = split(/,/,$includemsg);   my (@msgnum) = split(/,/,$includemsg);
  foreach (@msgnum) {   foreach (@msgnum) {
     $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');      $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
  }   }
  $message =&Apache::lonfeedback::clear_out_html($message);   $message =&Apache::lonfeedback::clear_out_html($message);
  $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;   if ($env{'form.withgrades'.$ctr}) {
  $message.=" for <a href=\"".      $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
       $message.=" for <a href=\"".
     &Apache::lonnet::clutter($url).      &Apache::lonnet::clutter($url).
     "?symb=$symb\">$ENV{'form.probTitle'}</a>";      "?symb=$symb\">$env{'form.probTitle'}</a>";
  $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,   }
        $ENV{'form.msgsub'},$message);   $msgstatus = &Apache::lonmsg::user_normal_msg($uname,$udom,
     }        $subject.' ['.
     if ($ENV{'form.collaborator'.$ctr}) {        &Apache::lonnet::declutter($url).']',$message);
  my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});   $request->print('<br />'.&mt('Sending message to [_1]@[_2]',$uname,$udom).': '.
  foreach (@collaborators) {   $msgstatus);
     my ($errorflag,$pts,$wgt) =       }
  &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,$ENV{'form.unamedom'.$ctr});      if ($env{'form.collaborator'.$ctr}) {
     if ($errorflag eq 'not_allowed') {   my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
  $request->print("<font color=\"red\">Not allowed to modify grades for $_:$udom</font>");   foreach my $collabstr (@collabstrs) {
  next;      my ($part,@collaborators) = split(/:/,$collabstr);
     } else {      foreach my $collaborator (@collaborators) {
  if ($message ne '') {   my ($errorflag,$pts,$wgt) = 
     $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,      &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
    $ENV{'form.msgsub'},     $env{'form.unamedom'.$ctr},$part);
    $message);   if ($errorflag eq 'not_allowed') {
       $request->print("<font color=\"red\">Not allowed to modify grades for $collaborator:$udom</font>");
       next;
    } else {
       if ($message ne '') {
    $msgstatus = &Apache::lonmsg::user_normal_msg($collaborator,$udom,$env{'form.msgsub'},$message);
       }
  }   }
     }      }
  }   }
Line 1623  sub processHandGrade { Line 2108  sub processHandGrade {
  }   }
     }      }
   
     if ($ENV{'form.handgrade'} eq 'yes') {      if ($env{'form.handgrade'} eq 'yes') {
  # Keywords sorted in alphabatical order   # Keywords sorted in alphabatical order
  my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};   my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
  my %keyhash = ();   my %keyhash = ();
  $ENV{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;   $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
  $ENV{'form.keywords'}           =~ s/^\s+|\s+$//;   $env{'form.keywords'}           =~ s/^\s+|\s+$//;
  my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));   my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
  $ENV{'form.keywords'} = join(' ',@keywords);   $env{'form.keywords'} = join(' ',@keywords);
  $keyhash{$symb.'_keywords'}     = $ENV{'form.keywords'};   $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};
  $keyhash{$symb.'_subject'}      = $ENV{'form.msgsub'};   $keyhash{$symb.'_subject'}      = $env{'form.msgsub'};
  $keyhash{$loginuser.'_kwclr'}   = $ENV{'form.kwclr'};   $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};
  $keyhash{$loginuser.'_kwsize'}  = $ENV{'form.kwsize'};   $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};
  $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'};   $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
   
  # message center - Order of message gets changed. Blank line is eliminated.   # message center - Order of message gets changed. Blank line is eliminated.
  # New messages are saved in ENV for the next student.   # New messages are saved in env for the next student.
  # All messages are saved in nohist_handgrade.db   # All messages are saved in nohist_handgrade.db
  my ($ctr,$idx) = (1,1);   my ($ctr,$idx) = (1,1);
  while ($ctr <= $ENV{'form.savemsgN'}) {   while ($ctr <= $env{'form.savemsgN'}) {
     if ($ENV{'form.savemsg'.$ctr} ne '') {      if ($env{'form.savemsg'.$ctr} ne '') {
  $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};   $keyhash{$symb.'_savemsg'.$idx} = $env{'form.savemsg'.$ctr};
  $idx++;   $idx++;
     }      }
     $ctr++;      $ctr++;
  }   }
  $ctr = 0;   $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     if ($ENV{'form.newmsg'.$ctr} ne '') {      if ($env{'form.newmsg'.$ctr} ne '') {
  $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};   $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};   $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $idx++;   $idx++;
     }      }
     $ctr++;      $ctr++;
  }   }
  $ENV{'form.savemsgN'} = --$idx;   $env{'form.savemsgN'} = --$idx;
  $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'};   $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
  my $putresult = &Apache::lonnet::put   my $putresult = &Apache::lonnet::put
     ('nohist_handgrade',\%keyhash,      ('nohist_handgrade',\%keyhash,$cdom,$cnum);
      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},  
      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});  
     }      }
     # Called by Save & Refresh from Highlight Attribute Window      # Called by Save & Refresh from Highlight Attribute Window
     my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');      my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
     if ($ENV{'form.refresh'} eq 'on') {      if ($env{'form.refresh'} eq 'on') {
  my ($ctr,$total) = (0,0);   my ($ctr,$total) = (0,0);
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     $total++ if  $ENV{'form.unamedom'.$ctr} ne '';      $total++ if  $env{'form.unamedom'.$ctr} ne '';
     $ctr++;      $ctr++;
  }   }
  $ENV{'form.NTSTU'}=$ngrade;   $env{'form.NTSTU'}=$ngrade;
  $ctr = 0;   $ctr = 0;
  while ($ctr < $total) {   while ($ctr < $total) {
     my $processUser = $ENV{'form.unamedom'.$ctr};      my $processUser = $env{'form.unamedom'.$ctr};
     ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser);      ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
     $ENV{'form.fullname'} = $$fullname{$processUser};      $env{'form.fullname'} = $$fullname{$processUser};
     &submission($request,$ctr,$total-1);      &submission($request,$ctr,$total-1);
     $ctr++;      $ctr++;
  }   }
Line 1686  sub processHandGrade { Line 2169  sub processHandGrade {
   
 # Go directly to grade student - from submission or link from chart page  # Go directly to grade student - from submission or link from chart page
     if ($button eq 'Grade Student') {      if ($button eq 'Grade Student') {
  (undef,undef,$ENV{'form.handgrade'},undef,undef) = &showResourceInfo($url);   (undef,undef,$env{'form.handgrade'},undef,undef) = &showResourceInfo($symb);
  my $processUser = $ENV{'form.unamedom'.$ENV{'form.studentNo'}};   my $processUser = $env{'form.unamedom'.$env{'form.studentNo'}};
  ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser);   ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
  $ENV{'form.fullname'} = $$fullname{$processUser};   $env{'form.fullname'} = $$fullname{$processUser};
  &submission($request,0,0);   &submission($request,0,0);
  return '';   return '';
     }      }
   
     # Get the next/previous one or group of students      # Get the next/previous one or group of students
     my $firststu = $ENV{'form.unamedom0'};      my $firststu = $env{'form.unamedom0'};
     my $laststu = $ENV{'form.unamedom'.($ngrade-1)};      my $laststu = $env{'form.unamedom'.($ngrade-1)};
     my $ctr = 2;      my $ctr = 2;
     while ($laststu eq '') {      while ($laststu eq '') {
  $laststu  = $ENV{'form.unamedom'.($ngrade-$ctr)};   $laststu  = $env{'form.unamedom'.($ngrade-$ctr)};
  $ctr++;   $ctr++;
  $laststu = $firststu if ($ctr > $ngrade);   $laststu = $firststu if ($ctr > $ngrade);
     }      }
   
     my (@parsedlist,@nextlist);      my (@parsedlist,@nextlist);
     my ($nextflg) = 0;      my ($nextflg) = 0;
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {      foreach (sort 
        {
    if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
        return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
    }
    return $a cmp $b;
        } (keys(%$fullname))) {
  if ($nextflg == 1 && $button =~ /Next$/) {   if ($nextflg == 1 && $button =~ /Next$/) {
     push @parsedlist,$_;      push @parsedlist,$_;
  }   }
Line 1717  sub processHandGrade { Line 2206  sub processHandGrade {
  }   }
     }      }
     $ctr = 0;      $ctr = 0;
     my ($partlist,$handgrade) = &response_type($ENV{'form.url'});  
     @parsedlist = reverse @parsedlist if ($button eq 'Previous');      @parsedlist = reverse @parsedlist if ($button eq 'Previous');
       my ($partlist) = &response_type($symb);
     foreach my $student (@parsedlist) {      foreach my $student (@parsedlist) {
    my $submitonly=$env{'form.submitonly'};
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
  if ($ENV{'form.submitonly'} eq 'yes') {  
     my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;   if ($submitonly eq 'queued') {
     my $statusflg = '';      my %queue_status = 
    &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
    $udom,$uname);
       next if (!defined($queue_status{'gradingqueue'}));
    }
   
    if ($submitonly =~ /^(yes|graded|incorrect)$/) {
   #    my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
       my %status=&student_gradeStatus($symb,$udom,$uname,$partlist);
       my $submitted = 0;
       my $ungraded = 0;
       my $incorrect = 0;
     foreach (keys(%status)) {      foreach (keys(%status)) {
  $statusflg = 1 if ($status{$_} ne 'nothing');   $submitted = 1 if ($status{$_} ne 'nothing');
  my ($foo,$partid,$foo1) = split(/\./);   $ungraded = 1 if ($status{$_} =~ /^ungraded/);
  $statusflg = '' if ($status{'resource.'.$partid.'.submitted_by'} ne '');   $incorrect = 1 if ($status{$_} =~ /^incorrect/);
    my ($foo,$partid,$foo1) = split(/\./,$_);
    if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
       $submitted = 0;
    }
     }      }
     next if ($statusflg eq '');      next if (!$submitted && ($submitonly eq 'yes' ||
        $submitonly eq 'incorrect' ||
        $submitonly eq 'graded'));
       next if (!$ungraded && ($submitonly eq 'graded'));
       next if (!$incorrect && $submitonly eq 'incorrect');
  }   }
  push @nextlist,$student if ($ctr < $ntstu);   push @nextlist,$student if ($ctr < $ntstu);
    last if ($ctr == $ntstu);
  $ctr++;   $ctr++;
     }      }
   
Line 1740  sub processHandGrade { Line 2250  sub processHandGrade {
   
     foreach (sort @nextlist) {      foreach (sort @nextlist) {
  my ($uname,$udom,$submitter) = split(/:/);   my ($uname,$udom,$submitter) = split(/:/);
  $ENV{'form.student'}  = $uname;   $env{'form.student'}  = $uname;
  $ENV{'form.userdom'}  = $udom;   $env{'form.userdom'}  = $udom;
  $ENV{'form.fullname'} = $$fullname{$_};   $env{'form.fullname'} = $$fullname{$_};
  &submission($request,$ctr,$total);   &submission($request,$ctr,$total);
  $ctr++;   $ctr++;
     }      }
Line 1750  sub processHandGrade { Line 2260  sub processHandGrade {
  my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";   my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";
  $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";   $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";
  $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";   $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";
  $the_end.=&show_grading_menu_form ($symb,$url);   $the_end.=&show_grading_menu_form($symb);
  $request->print($the_end);   $request->print($the_end);
     }      }
     return '';      return '';
Line 1758  sub processHandGrade { Line 2268  sub processHandGrade {
   
 #---- Save the score and award for each student, if changed  #---- Save the score and award for each student, if changed
 sub saveHandGrade {  sub saveHandGrade {
     my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;      my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
       my @version_parts;
     my $usec = &Apache::lonnet::getsection($domain,$stuname,      my $usec = &Apache::lonnet::getsection($domain,$stuname,
    $ENV{'request.course.id'});     $env{'request.course.id'});
     if (!&canmodify($usec)) { return('not_allowed'); }      if (!&canmodify($usec)) { return('not_allowed'); }
     my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
       my @parts_graded;
     my %newrecord  = ();      my %newrecord  = ();
     my ($pts,$wgt) = ('','');      my ($pts,$wgt) = ('','');
     foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {      my %aggregate = ();
  if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {      my $aggregateflag = 0;
     if ($record{'resource.'.$_.'.solved'} ne 'excused') {      my @parts = split(/:/,$env{'form.partlist'.$newflg});
  $newrecord{'resource.'.$_.'.solved'} = 'excused';      foreach my $new_part (@parts) {
  if (exists($record{'resource.'.$_.'.awarded'})) {   #collaborator ($submi may vary for different parts
     $newrecord{'resource.'.$_.'.awarded'} = '';   if ($submitter && $new_part ne $part) { next; }
    my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
    if ($dropMenu eq 'excused') {
       if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {
    $newrecord{'resource.'.$new_part.'.solved'} = 'excused';
    if (exists($record{'resource.'.$new_part.'.awarded'})) {
       $newrecord{'resource.'.$new_part.'.awarded'} = '';
  }   }
       $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
     }      }
  } else {   } elsif ($dropMenu eq 'reset status'
     $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ?    && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
     $ENV{'form.GD_BOX'.$newflg.'_'.$_} :       foreach my $key (keys (%record)) {
     $ENV{'form.RADVAL'.$newflg.'_'.$_});   if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; }
     return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '');      }
     $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 :       $newrecord{'resource.'.$new_part.'.regrader'}=
  $ENV{'form.WGT'.$newflg.'_'.$_};   "$env{'user.name'}:$env{'user.domain'}";
               my $totaltries = $record{'resource.'.$part.'.tries'};
   
               my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
          [$new_part]);
               my $aggtries =$totaltries;
               if ($last_resets{$new_part}) {
                   $aggtries = &get_num_tries(\%record,$last_resets{$new_part},
      $new_part);
               }
   
               my $solvedstatus = $record{'resource.'.$new_part.'.solved'};
               if ($aggtries > 0) {
                   &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                   $aggregateflag = 1;
               }
    } elsif ($dropMenu eq '') {
       $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ? 
       $env{'form.GD_BOX'.$newflg.'_'.$new_part} : 
       $env{'form.RADVAL'.$newflg.'_'.$new_part});
       if ($pts eq '' && $env{'form.GD_SEL'.$newflg.'_'.$new_part} eq '') {
    next;
       }
       $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 : 
    $env{'form.WGT'.$newflg.'_'.$new_part};
     my $partial= $pts/$wgt;      my $partial= $pts/$wgt;
     next if ($partial eq $record{'resource.'.$_.'.awarded'}); #do not update score for part if not changed.      if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
     $newrecord{'resource.'.$_.'.awarded'}  = $partial    #do not update score for part if not changed.
  if ($record{'resource.'.$_.'.awarded'} ne $partial);   next;
     my $reckey = 'resource.'.$_.'.solved';  
     if ($partial == 0) {  
  $newrecord{$reckey} = 'incorrect_by_override'   
     if ($record{$reckey} ne 'incorrect_by_override');  
     } else {      } else {
  $newrecord{$reckey} = 'correct_by_override'           push @parts_graded, $new_part;
     if ($record{$reckey} ne 'correct_by_override');      }
       if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
    $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
     }      }
     $newrecord{'resource.'.$_.'.submitted_by'} = $submitter       my $reckey = 'resource.'.$new_part.'.solved';
  if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));      if ($partial == 0) {
     $newrecord{'resource.'.$_.'regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";   if ($record{$reckey} ne 'incorrect_by_override') {
       $newrecord{$reckey} = 'incorrect_by_override';
    }
       } else {
    if ($record{$reckey} ne 'correct_by_override') {
       $newrecord{$reckey} = 'correct_by_override';
    }
       }    
       if ($submitter && 
    ($record{'resource.'.$new_part.'.submitted_by'} ne $submitter)) {
    $newrecord{'resource.'.$new_part.'.submitted_by'} = $submitter;
       }
       $newrecord{'resource.'.$new_part.'.regrader'}=
    "$env{'user.name'}:$env{'user.domain'}";
               &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
    }
    # unless problem has been graded, set flag to version the submitted files
    unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/  || 
           $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override' ||
           $dropMenu eq 'reset status')
      {
       push (@version_parts,$new_part);
  }   }
     }      }
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
   
     if (scalar(keys(%newrecord)) > 0) {      if (scalar(keys(%newrecord)) > 0) {
           if (scalar(@version_parts)) {
               &version_portfiles(\%record, \%newrecord, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@version_parts);
           }
  &Apache::lonnet::cstore(\%newrecord,$symb,   &Apache::lonnet::cstore(\%newrecord,$symb,
  $ENV{'request.course.id'},$domain,$stuname);   $env{'request.course.id'},$domain,$stuname);
    my @ungraded_parts;
    foreach my $part (@parts) {
       if ( !defined($record{'resource.'.$part.'.awarded'})
    && !defined($newrecord{'resource.'.$part.'.awarded'}) ) {
    push(@ungraded_parts, $part);
       }
    }
    if ( !@ungraded_parts ) {
       &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,
      $cnum,$domain,$stuname);
    }
       }
       if ($aggregateflag) {
           &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
         $cdom,$cnum);
       }
       return ('',$pts,$wgt);
   }
   
   sub handback_files {
       my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;
       my $portfolio_root = &Apache::loncommon::propath($domain,
    $stuname).
    '/userfiles/portfolio';
    my ($partlist,$handgrade,$responseType) = &response_type($symb);
           foreach my $part_resp (sort(keys(%$handgrade))) {
               my ($part_id, $resp_id) = split(/_/,$part_resp);
               if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) {
                   # if multiple files are uploaded names will be 'returndoc2','returndoc3'
                   my $file_counter = 1;
                   while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {
                       my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'};
                       my ($directory,$answer_file) = 
                           ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/);
                       my ($answer_name,$answer_ver,$answer_ext) =
           &file_name_version_ext($answer_file);
       $directory =~ /^.+$stuname\/portfolio(.*)/;
       my $portfolio_path = $1;
       my @dir_list = &Apache::lonnet::dirlist($portfolio_path,$domain,$stuname,$portfolio_root);
       my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
       my $new_answer = &version_selected_portfile($domain, $stuname, $portfolio_path, $answer_file, $version);
                       $$newrecord{"resource.$new_part.$resp_id.handback"} = $new_answer;
                       # set the filename to match the submitted file name
                       $env{'form.'.$newflg.'_'.$part_resp.'_returndoc1.filename'} = $env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter};
                       my $result=&Apache::lonnet::userfileupload($newflg.'_'.$part_resp.'_returndoc'.$file_counter,'',
                  'portfolio',undef,undef,undef,$stuname,$domain);
                       if ($result !~ m|^/uploaded/|) {
                           $request->print('<font color="red"> An errror occured ('.$result.
                           ') while trying to upload '.&display_file().'</font><br />');
                       # $request->print(&done('Back'));
                       }
                       $request->print("<br />".$fname." will be the uploaded file name");
                       $request->print("<font color=\"red\">Will upload document </font>".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter});
                       $file_counter++;
                   }
               }
           }
       return;
   }
   
   sub get_submitted_files {
       my ($udom,$uname,$partid,$respid,$record) = @_;
       my @files;
       if ($$record{"resource.$partid.$respid.portfiles"}) {
           my $file_url = '/uploaded/'.$udom.'/'.$uname.'/portfolio';
           foreach my $file (split(',',$$record{"resource.$partid.$respid.portfiles"})) {
          push(@files,$file_url.$file);
           }
     }      }
     return '',$pts,$wgt;      if ($$record{"resource.$partid.$respid.uploadedurl"}) {
           push(@files,$$record{"resource.$partid.$respid.uploadedurl"});
       }
       return (\@files);
   }
   
   # ----------- Provides number of tries since last reset.
   sub get_num_tries {
       my ($record,$last_reset,$part) = @_;
       my $timestamp = '';
       my $num_tries = 0;
       if ($$record{'version'}) {
           for (my $version=$$record{'version'};$version>=1;$version--) {
               if (exists($$record{$version.':resource.'.$part.'.solved'})) {
                   $timestamp = $$record{$version.':timestamp'};
                   if ($timestamp > $last_reset) {
                       $num_tries ++;
                   } else {
                       last;
                   }
               }
           }
       }
       return $num_tries;
   }
   
   # ----------- Determine decrements required in aggregate totals 
   sub decrement_aggs {
       my ($symb,$part,$aggregate,$aggtries,$totaltries,$solvedstatus) = @_;
       my %decrement = (
                           attempts => 0,
                           users => 0,
                           correct => 0
                       );
       $decrement{'attempts'} = $aggtries;
       if ($solvedstatus =~ /^correct/) {
           $decrement{'correct'} = 1;
       }
       if ($aggtries == $totaltries) {
           $decrement{'users'} = 1;
       }
       foreach my $type (keys (%decrement)) {
           $$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type};
       }
       return;
   }
   
   # ----------- Determine timestamps for last reset of aggregate totals for parts  
   sub get_last_resets {
       my ($symb,$courseid,$partids) =@_;
       my %last_resets;
       my $cdom = $env{'course.'.$courseid.'.domain'};
       my $cname = $env{'course.'.$courseid.'.num'};
       my @keys;
       foreach my $part (@{$partids}) {
    push(@keys,"$symb\0$part\0resettime");
       }
       my %results=&Apache::lonnet::get('nohist_resourcetracker',\@keys,
        $cdom,$cname);
       foreach my $part (@{$partids}) {
    $last_resets{$part}=$results{"$symb\0$part\0resettime"};
       }
       return %last_resets;
   }
   
   # ----------- Handles creating versions for portfolio files as answers
   sub version_portfiles {
       my ($record, $newrecord, $parts_graded, $courseid, $symb, $domain, $stu_name, $v_flag) = @_;
       my $version_parts = join('|',@$v_flag);
       my $parts = join('|', @$parts_graded);
       my $portfolio_root = &Apache::loncommon::propath($domain,
    $stu_name).
    '/userfiles/portfolio';
       foreach my $key (keys(%$record)) {
           my $new_portfiles;
           if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
               my @versioned_portfiles;
               my @portfiles = split(/,/,$$record{$key});
               foreach my $file (@portfiles) {
                   &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file);
                   my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/);
    my ($answer_name,$answer_ver,$answer_ext) =
       &file_name_version_ext($answer_file);
                   my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stu_name,$portfolio_root);
                   my $version = &get_next_version($answer_name, $answer_ext, \@dir_list);
                   my $new_answer = &version_selected_portfile($domain, $stu_name, $directory, $answer_file, $version);
                   if ($new_answer ne 'problem getting file') {
                       push(@versioned_portfiles, $directory.$new_answer);
                       &Apache::lonnet::mark_as_readonly($domain,$stu_name,
                           ['/portfolio'.$directory.$new_answer],
                           [$symb,$env{'request.course.id'},'graded']);
                   }
               }
               $$newrecord{$key} = join(',',@versioned_portfiles);
           }
       } 
       return 'ok';   
   }
   
   sub get_next_version {
       my ($answer_name, $answer_ext, $dir_list) = @_;
       my $version;
       foreach my $row (@$dir_list) {
           my ($file) = split(/\&/,$row,2);
           my ($file_name,$file_version,$file_ext) =
       &file_name_version_ext($file);
           if (($file_name eq $answer_name) && 
       ($file_ext eq $answer_ext)) {
                   # gets here if filename and extension match, regardless of version
                   if ($file_version ne '') {
                   # a versioned file is found  so save it for later
                   if ($file_version > $version) {
       $version = $file_version;
           }
               }
           }
       } 
       $version ++;
       return($version);
   }
   
   sub version_selected_portfile {
       my ($domain,$stu_name,$directory,$file_name,$version) = @_;
       my ($answer_name,$answer_ver,$answer_ext) =
           &file_name_version_ext($file_name);
       my $new_answer;
       $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stu_name/portfolio$directory$file_name");
       if($env{'form.copy'} eq '-1') {
           &Apache::lonnet::logthis('problem getting file '.$file_name);
           $new_answer = 'problem getting file';
       } else {
           $new_answer = $answer_name.'.'.$version.'.'.$answer_ext;
           my $copy_result = &Apache::lonnet::finishuserfileupload(
                               $stu_name,$domain,'copy',
           '/portfolio'.$directory.$new_answer);
       }    
       return ($new_answer);
   }
   
   sub file_name_version_ext {
       my ($file)=@_;
       my @file_parts = split(/\./, $file);
       my ($name,$version,$ext);
       if (@file_parts > 1) {
    $ext=pop(@file_parts);
    if (@file_parts > 1 && $file_parts[-1] =~ /^\d+$/) {
       $version=pop(@file_parts);
    }
    $name=join('.',@file_parts);
       } else {
    $name=join('.',@file_parts);
       }
       return($name,$version,$ext);
 }  }
   
 #--------------------------------------------------------------------------------------  #--------------------------------------------------------------------------------------
Line 1816  sub viewgrades_js { Line 2603  sub viewgrades_js {
     $request->print(<<VIEWJAVASCRIPT);      $request->print(<<VIEWJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
    function writePoint(partid,weight,point) {     function writePoint(partid,weight,point) {
  var radioButton = eval("document.classgrade.RADVAL_"+partid);   var radioButton = document.classgrade["RADVAL_"+partid];
  var textbox = eval("document.classgrade.TEXTVAL_"+partid);   var textbox = document.classgrade["TEXTVAL_"+partid];
  if (point == "textval") {   if (point == "textval") {
     var point = eval("document.classgrade.TEXTVAL_"+partid+".value");      point = document.classgrade["TEXTVAL_"+partid].value;
     if (isNaN(point) || parseFloat(point) < 0) {      if (isNaN(point) || parseFloat(point) < 0) {
  alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));   alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
  var resetbox = false;   var resetbox = false;
Line 1850  sub viewgrades_js { Line 2637  sub viewgrades_js {
     }      }
   
  } else {   } else {
     textbox.value = point;      textbox.value = parseFloat(point);
  }   }
  for (i=0;i<document.classgrade.total.value;i++) {   for (i=0;i<document.classgrade.total.value;i++) {
     var user = eval("document.classgrade.ctr"+i+".value");      var user = document.classgrade["ctr"+i].value;
     var scorename = eval("document.classgrade.GD_"+user+      user = user.replace(new RegExp(':', 'g'),"_");
  "_"+partid+"_awarded");      var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
     var saveval   = eval("document.classgrade.GD_"+user+      var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
  "_"+partid+"_solved_s.value");      var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
     var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");  
     if (saveval != "correct") {      if (saveval != "correct") {
  scorename.value = point;   scorename.value = point;
  if (selname[0].selected != true) {   if (selname[0].selected != true) {
Line 1866  sub viewgrades_js { Line 2652  sub viewgrades_js {
  }   }
     }      }
  }   }
  var selval   = eval("document.classgrade.SELVAL_"+partid);   document.classgrade["SELVAL_"+partid][0].selected = true;
  selval[0].selected = true;  
     }      }
   
     function writeRadText(partid,weight) {      function writeRadText(partid,weight) {
  var selval   = eval("document.classgrade.SELVAL_"+partid);   var selval   = document.classgrade["SELVAL_"+partid];
  var radioButton = eval("document.classgrade.RADVAL_"+partid);   var radioButton = document.classgrade["RADVAL_"+partid];
  var textbox = eval("document.classgrade.TEXTVAL_"+partid);          var override = document.classgrade["FORCE_"+partid].checked;
  if (selval[1].selected) {   var textbox = document.classgrade["TEXTVAL_"+partid];
    if (selval[1].selected || selval[2].selected) {
     for (var i=0; i<radioButton.length; i++) {      for (var i=0; i<radioButton.length; i++) {
  radioButton[i].checked=false;   radioButton[i].checked=false;
   
Line 1882  sub viewgrades_js { Line 2668  sub viewgrades_js {
     textbox.value = "";      textbox.value = "";
   
     for (i=0;i<document.classgrade.total.value;i++) {      for (i=0;i<document.classgrade.total.value;i++) {
  var user = eval("document.classgrade.ctr"+i+".value");   var user = document.classgrade["ctr"+i].value;
  var scorename = eval("document.classgrade.GD_"+user+   user = user.replace(new RegExp(':', 'g'),"_");
      "_"+partid+"_awarded");   var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
  var saveval   = eval("document.classgrade.GD_"+user+   var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
      "_"+partid+"_solved_s.value");   var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
  var selname   = eval("document.classgrade.GD_"+user+   if ((saveval != "correct") || override) {
      "_"+partid+"_solved");  
  if (saveval != "correct") {  
     scorename.value = "";      scorename.value = "";
     selname[1].selected = true;      if (selval[1].selected) {
    selname[1].selected = true;
       } else {
    selname[2].selected = true;
    if (Number(document.classgrade["GD_"+user+"_"+partid+"_tries"].value)) 
    {document.classgrade["GD_"+user+"_"+partid+"_tries"].value = '0';}
       }
  }   }
     }      }
  } else {   } else {
     for (i=0;i<document.classgrade.total.value;i++) {      for (i=0;i<document.classgrade.total.value;i++) {
  var user = eval("document.classgrade.ctr"+i+".value");   var user = document.classgrade["ctr"+i].value;
  var scorename = eval("document.classgrade.GD_"+user+   user = user.replace(new RegExp(':', 'g'),"_");
      "_"+partid+"_awarded");   var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
  var saveval   = eval("document.classgrade.GD_"+user+   var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
      "_"+partid+"_solved_s.value");   var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
  var selname   = eval("document.classgrade.GD_"+user+   if ((saveval != "correct") || override) {
      "_"+partid+"_solved");      scorename.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
  if (saveval != "correct") {  
     scorename.value = eval("document.classgrade.GD_"+user+  
      "_"+partid+"_awarded_s.value");;  
     selname[0].selected = true;      selname[0].selected = true;
  }   }
     }      }
Line 1913  sub viewgrades_js { Line 2700  sub viewgrades_js {
     }      }
   
     function changeSelect(partid,user) {      function changeSelect(partid,user) {
  var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");   var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"];
  var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");   var textbox = document.classgrade["GD_"+user+'_'+partid+"_awarded"];
  var point  = textbox.value;   var point  = textbox.value;
  var weight = eval("document.classgrade.weight_"+partid+".value");   var weight = document.classgrade["weight_"+partid].value;
   
  if (isNaN(point) || parseFloat(point) < 0) {   if (isNaN(point) || parseFloat(point) < 0) {
     alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));      alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
Line 1935  sub viewgrades_js { Line 2722  sub viewgrades_js {
     }      }
   
     function changeOneScore(partid,user) {      function changeOneScore(partid,user) {
  var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");   var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"];
  if (selval[1].selected) {   if (selval[1].selected || selval[2].selected) {
     var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");      document.classgrade["GD_"+user+'_'+partid+"_awarded"].value = "";
     boxval.value = "";      if (selval[2].selected) {
  }   document.classgrade["GD_"+user+'_'+partid+"_tries"].value = "0";
       }
           }
     }      }
   
     function resetEntry(numpart) {      function resetEntry(numpart) {
  for (ctpart=0;ctpart<numpart;ctpart++) {   for (ctpart=0;ctpart<numpart;ctpart++) {
     var partid = eval("document.classgrade.partid_"+ctpart+".value");      var partid = document.classgrade["partid_"+ctpart].value;
     var radioButton = eval("document.classgrade.RADVAL_"+partid);      var radioButton = document.classgrade["RADVAL_"+partid];
     var textbox = eval("document.classgrade.TEXTVAL_"+partid);      var textbox = document.classgrade["TEXTVAL_"+partid];
     var selval  = eval("document.classgrade.SELVAL_"+partid);      var selval  = document.classgrade["SELVAL_"+partid];
     for (var i=0; i<radioButton.length; i++) {      for (var i=0; i<radioButton.length; i++) {
  radioButton[i].checked=false;   radioButton[i].checked=false;
   
Line 1956  sub viewgrades_js { Line 2745  sub viewgrades_js {
     selval[0].selected = true;      selval[0].selected = true;
   
     for (i=0;i<document.classgrade.total.value;i++) {      for (i=0;i<document.classgrade.total.value;i++) {
  var user = eval("document.classgrade.ctr"+i+".value");   var user = document.classgrade["ctr"+i].value;
  var resetscore = eval("document.classgrade.GD_"+user+   user = user.replace(new RegExp(':', 'g'),"_");
       "_"+partid+"_awarded");   var resetscore = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
  resetscore.value = eval("document.classgrade.GD_"+user+   resetscore.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
  "_"+partid+"_awarded_s.value");   var resettries = document.classgrade["GD_"+user+"_"+partid+"_tries"];
    resettries.value = document.classgrade["GD_"+user+"_"+partid+"_tries_s"].value;
  var saveselval   = eval("document.classgrade.GD_"+user+   var saveselval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
      "_"+partid+"_solved_s.value");   var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
   
  var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");  
  if (saveselval == "excused") {   if (saveselval == "excused") {
     if (selname[1].selected == false) { selname[1].selected = true;}      if (selname[1].selected == false) { selname[1].selected = true;}
  } else {   } else {
Line 1984  sub viewgrades { Line 2771  sub viewgrades {
     my ($request) = shift;      my ($request) = shift;
     &viewgrades_js($request);      &viewgrades_js($request);
   
     my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'});       my ($symb) = &get_symb($request);
     my $result='<h3><font color="#339933">Manual Grading</font></h3>';      #need to make sure we have the correct data for later EXT calls, 
       #thus invalidate the cache
       &Apache::lonnet::devalidatecourseresdata(
                    $env{'course.'.$env{'request.course.id'}.'.num'},
                    $env{'course.'.$env{'request.course.id'}.'.domain'});
       &Apache::lonnet::clear_EXT_cache_status();
   
     $result.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n";      my $result='<h3><font color="#339933">'.&mt('Manual Grading').'</font></h3>';
       $result.='<font size=+1><b>Current Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n";
   
     #view individual student submission form - called using Javascript viewOneStudent      #view individual student submission form - called using Javascript viewOneStudent
     $result.=&jscriptNform($url,$symb);      $result.=&jscriptNform($symb);
   
     #beginning of class grading form      #beginning of class grading form
     $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".      $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
  '<input type="hidden" name="url"     value="'.$url.'" />'."\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".   '<input type="hidden" name="section" value="'.$env{'form.section'}.'" />'."\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="Status" value="'.$env{'form.Status'}.'" />'."\n".
    '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
     $result.='<h3>Assign Common Grade To ';  
     if ($ENV{'form.section'} eq 'all') {      my $sectionClass;
  $result.='Class </h3>';      if ($env{'form.section'} eq 'all') {
     } elsif ($ENV{'form.section'} eq 'no') {   $sectionClass='Class </h3>';
  $result.='Students in no Section </h3>';      } elsif ($env{'form.section'} eq 'none') {
    $sectionClass='Students in no Section </h3>';
     } else {      } else {
  $result.='Students in Section '.$ENV{'form.section'}.'</h3>';   $sectionClass='Students in Section '.$env{'form.section'}.'</h3>';
     }      }
       $result.='<h3>Assign Common Grade To '.$sectionClass;
     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".      $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
  '<table border=0><tr bgcolor="#ffffdd"><td>';   '<table border=0><tr bgcolor="#ffffdd"><td>';
     #radio buttons/text box for assigning points for a section or class.      #radio buttons/text box for assigning points for a section or class.
     #handles different parts of a problem      #handles different parts of a problem
     my ($partlist,$handgrade) = &response_type($ENV{'form.url'});      my ($partlist,$handgrade) = &response_type($symb);
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     $result.='<table border="0">';      $result.='<table border="0">';
Line 2022  sub viewgrades { Line 2816  sub viewgrades {
  my ($partid,$respid) = split (/_/,$_,2);   my ($partid,$respid) = split (/_/,$_,2);
  next if $seen{$partid};   next if $seen{$partid};
  $seen{$partid}++;   $seen{$partid}++;
  my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});   my $handgrade=$$handgrade{$_};
  my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);   my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
  $weight{$partid} = $wgt eq '' ? '1' : $wgt;   $weight{$partid} = $wgt eq '' ? '1' : $wgt;
   
Line 2030  sub viewgrades { Line 2824  sub viewgrades {
     $ctsparts.'" value="'.$partid.'" />'."\n";      $ctsparts.'" value="'.$partid.'" />'."\n";
  $result.='<input type="hidden" name="weight_'.   $result.='<input type="hidden" name="weight_'.
     $partid.'" value="'.$weight{$partid}.'" />'."\n";      $partid.'" value="'.$weight{$partid}.'" />'."\n";
  $result.='<tr><td><b>Part  '.$partid.'&nbsp; &nbsp;Point:</b> </td><td>';   my $display_part=&get_display_part($partid,$symb);
    $result.='<tr><td><b>Part:</b> '.$display_part.'&nbsp; &nbsp;<b>Point:</b> </td><td>';
  $result.='<table border="0"><tr>';     $result.='<table border="0"><tr>';  
  my $ctr = 0;   my $ctr = 0;
  while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across   while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
     $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '.      $result.= '<td><label><input type="radio" name="RADVAL_'.$partid.'" '.
  'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.   'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.
  ','.$ctr.')" />'.$ctr."</td>\n";   ','.$ctr.')" />'.$ctr."</label></td>\n";
     $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');      $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
     $ctr++;      $ctr++;
  }   }
Line 2049  sub viewgrades { Line 2844  sub viewgrades {
     'onChange="javascript:writeRadText(\''.$partid.'\','.      'onChange="javascript:writeRadText(\''.$partid.'\','.
  $weight{$partid}.')"> '.   $weight{$partid}.')"> '.
     '<option selected="on"> </option>'.      '<option selected="on"> </option>'.
     '<option>excused</option></select></td></tr>'."\n";      '<option>excused</option>'.
       '<option>reset status</option></select></td>'.
               '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";
  $ctsparts++;   $ctsparts++;
     }      }
     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".      $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
Line 2059  sub viewgrades { Line 2856  sub viewgrades {
   
     #table listing all the students in a section/class      #table listing all the students in a section/class
     #header of table      #header of table
     $result.= '<h3>Assign Grade to Specific Students in ';      $result.= '<h3>Assign Grade to Specific Students in '.$sectionClass;
     if ($ENV{'form.section'} eq 'all') {  
  $result.='the Class </h3>';  
     } elsif ($ENV{'form.section'} eq 'no') {  
  $result.='no Section </h3>';  
     } else {  
  $result.='Section '.$ENV{'form.section'}.'</h3>';  
     }  
     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".      $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
  '<table border=0><tr bgcolor="#deffff">'.   '<table border=0><tr bgcolor="#deffff"><td>&nbsp;<b>No.</b>&nbsp;</td>'.
  '<td><b>Fullname</b> <font color="#999999">(Username)</font></td>'."\n";   '<td>'.&nameUserString('header')."</td>\n";
     my (@parts) = sort(&getpartlist($url));      my (@parts) = sort(&getpartlist($symb));
       my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
       my @partids = ();
     foreach my $part (@parts) {      foreach my $part (@parts) {
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display');
  next if ($display =~ /Number of Attempts/);   $display =~ s|^Number of Attempts|Tries<br />|; # makes the column narrower
  if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }   if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
    my ($partid) = &split_part_type($part);
           push(@partids, $partid);
    my $display_part=&get_display_part($partid,$symb);
  if ($display =~ /^Partial Credit Factor/) {   if ($display =~ /^Partial Credit Factor/) {
     my ($partid) = &split_part_type($part);      $result.='<td><b>Score Part:</b> '.$display_part.
     $result.='<td><b>Score Part '.$partid.'<br />(weight = '.   ' <br /><b>(weight = '.$weight{$partid}.')</b></td>'."\n";
  $weight{$partid}.')</b></td>'."\n";  
     next;      next;
    } else {
       $display =~s/\[Part: \Q$partid\E\]/Part:<\/b> $display_part/;
  }   }
  $display =~ s|Problem Status|Grade Status<br />|;   $display =~ s|Problem Status|Grade Status<br />|;
  $result.='<td><b>'.$display.'</b></td>'."\n";   $result.='<td><b>'.$display.'</td>'."\n";
     }      }
     $result.='</tr>';      $result.='</tr>';
   
       my %last_resets = 
    &get_last_resets($symb,$env{'request.course.id'},\@partids);
   
     #get info for each student      #get info for each student
     #list all the students - with points and grade status      #list all the students - with points and grade status
     my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');      my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
     my $ctr = 0;      my $ctr = 0;
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {      foreach (sort 
  my $uname = $_;       {
  $uname=~s/:/_/;   if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
  $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n";       return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
  $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},   }
    $_,$$fullname{$_},\@parts,\%weight);   return $a cmp $b;
        } (keys(%$fullname))) {
  $ctr++;   $ctr++;
    $result.=&viewstudentgrade($symb,$env{'request.course.id'},
      $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets);
     }      }
     $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="Submit Changes" '.      $result.='<input type="button" value="Save" '.
  'onClick="javascript:submit();" TARGET=_self /></form>'."\n";   'onClick="javascript:submit();" TARGET=_self /></form>'."\n";
     if (scalar(%$fullname) eq 0) {      if (scalar(%$fullname) eq 0) {
  my $colspan=3+scalar(@parts);   my $colspan=3+scalar(@parts);
  $result='<font color="red">There are no students in section "'.$ENV{'form.section'}.   $result='<font color="red">There are no students in section "'.$env{'form.section'}.
     '" with enrollment status "'.$ENV{'form.Status'}.'" to modify or grade.</font>';      '" with enrollment status "'.$env{'form.Status'}.'" to modify or grade.</font>';
     }      }
     $result.=&show_grading_menu_form($symb,$url);      $result.=&show_grading_menu_form($symb);
     return $result;      return $result;
 }  }
   
 #--- call by previous routine to display each student  #--- call by previous routine to display each student
 sub viewstudentgrade {  sub viewstudentgrade {
     my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;      my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;
     my ($uname,$udom) = split(/:/,$student);      my ($uname,$udom) = split(/:/,$student);
     $student=~s/:/_/;  
     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);      my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
     my $result='<tr bgcolor="#ffffdd"><td>'.      my %aggregates = (); 
       my $result='<tr bgcolor="#ffffdd"><td align="right">'.
    '<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.
    "\n".$ctr.'&nbsp;</td><td>&nbsp;'.
  '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.   '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
  '\')"; TARGET=_self>'.$fullname.'</a> '.   '\')"; TARGET=_self>'.$fullname.'</a> '.
  '<font color="#999999">('.$uname.($ENV{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n";   '<font color="#999999">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n";
       $student=~s/:/_/; # colon doen't work in javascript for names
     foreach my $apart (@$parts) {      foreach my $apart (@$parts) {
  my ($part,$type) = &split_part_type($apart);   my ($part,$type) = &split_part_type($apart);
  my $score=$record{"resource.$part.$type"};   my $score=$record{"resource.$part.$type"};
           $result.='<td align="center">';
           my ($aggtries,$totaltries);
           unless (exists($aggregates{$part})) {
       $totaltries = $record{'resource.'.$part.'.tries'};
   
       $aggtries = $totaltries;
               if ($$last_resets{$part}) {  
                   $aggtries = &get_num_tries(\%record,$$last_resets{$part},
      $part);
               }
               $result.='<input type="hidden" name="'.
                   'GD_'.$student.'_'.$part.'_aggtries" value="'.$aggtries.'" />'."\n";
               $result.='<input type="hidden" name="'.
                   'GD_'.$student.'_'.$part.'_totaltries" value="'.$totaltries.'" />'."\n";
               $aggregates{$part} = 1;
           }
  if ($type eq 'awarded') {   if ($type eq 'awarded') {
     my $pts = $score eq '' ? '' : $score*$$weight{$part};      my $pts = $score eq '' ? '' : &compute_points($score,$$weight{$part});
     $result.='<input type="hidden" name="'.      $result.='<input type="hidden" name="'.
  'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";   'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
     $result.='<td align="middle"><input type="text" name="'.      $result.='<input type="text" name="'.
  'GD_'.$student.'_'.$part.'_awarded" '.   'GD_'.$student.'_'.$part.'_awarded" '.
  'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.   'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.
  '\')" value="'.$pts.'" size="4" /></td>'."\n";   '\')" value="'.$pts.'" size="4" /></td>'."\n";
Line 2137  sub viewstudentgrade { Line 2958  sub viewstudentgrade {
     $status = 'nothing' if ($status eq '');      $status = 'nothing' if ($status eq '');
     $result.='<input type="hidden" name="'.'GD_'.$student.'_'.      $result.='<input type="hidden" name="'.'GD_'.$student.'_'.
  $part.'_solved_s" value="'.$status.'" />'."\n";   $part.'_solved_s" value="'.$status.'" />'."\n";
     $result.='<td align="middle"><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";
     my $optsel = '<option selected="on"> </option><option>excused</option>'."\n";      $result.= (($status eq 'excused') ? '<option> </option><option selected="on">excused</option>' 
     $optsel = '<option> </option><option selected="on">excused</option>'."\n"   : '<option selected="on"> </option><option>excused</option>')."\n";
  if ($status eq 'excused');      $result.='<option>reset status</option>';
     $result.=$optsel;      $result.="</select>&nbsp;</td>\n";
     $result.="</select></td>\n";   } else {
 # } else {      $result.='<input type="hidden" name="'.
 #    $result.='<input type="hidden" name="'.   'GD_'.$student.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'.
 # 'GD_'.$student.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'.      "\n";
 #    "\n";      $result.='<input type="text" name="'.
 #    $result.='<td align="middle"><input type="text" name="'.   'GD_'.$student.'_'.$part.'_'.$type.'" '.
 # 'GD_'.$student.'_'.$part.'_'.$type.'" '.   'value="'.$score.'" size="4" /></td>'."\n";
 # 'value="'.$score.'" size="4" /></td>'."\n";  
  }   }
     }      }
     $result.='</tr>';      $result.='</tr>';
Line 2163  sub viewstudentgrade { Line 2983  sub viewstudentgrade {
 sub editgrades {  sub editgrades {
     my ($request) = @_;      my ($request) = @_;
   
     my $symb=$ENV{'form.symb'};      my $symb=&get_symb($request);
     my $url =$ENV{'form.url'};  
     my $title='<h3><font color="#339933">Current Grade Status</font></h3>';      my $title='<h3><font color="#339933">Current Grade Status</font></h3>';
     $title.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font><br />'."\n";      $title.='<font size=+1><b>Current Resource: </b>'.$env{'form.probTitle'}.'</font><br />'."\n";
     $title.='<font size=+1><b>Section: </b>'.$ENV{'form.section'}.'</font>'."\n";      $title.='<font size=+1><b>Section: </b>'.$env{'form.section'}.'</font>'."\n";
   
     my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";      my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";
     $result.= '<table border="0"><tr bgcolor="#deffff">'.      $result.= '<table border="0"><tr bgcolor="#deffff">'.
  '<td rowspan=2><b>Username</b></td><td rowspan=2><b>Domain</b></td><td rowspan=2><b>Fullname</b></td>'."\n";   '<td rowspan=2 valign="center">&nbsp;<b>No.</b>&nbsp;</td>'.
    '<td rowspan=2 valign="center">'.&nameUserString('header')."</td>\n";
   
     my %scoreptr = (      my %scoreptr = (
     'correct'  =>'correct_by_override',      'correct'  =>'correct_by_override',
Line 2179  sub editgrades { Line 3000  sub editgrades {
     'ungraded' =>'ungraded_attempted',      'ungraded' =>'ungraded_attempted',
     'nothing'  => '',      'nothing'  => '',
     );      );
     my ($classlist,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');      my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
   
     my (@partid);      my (@partid);
     my %weight = ();      my %weight = ();
     my %columns = ();      my %columns = ();
     my ($i,$ctr,$count,$rec_update) = (0,0,0,0);      my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
   
     my (@parts) = sort(&getpartlist($url));      my (@parts) = sort(&getpartlist($symb));
     my $header;      my $header;
     while ($ctr < $ENV{'form.totalparts'}) {      while ($ctr < $env{'form.totalparts'}) {
  my $partid = $ENV{'form.partid_'.$ctr};   my $partid = $env{'form.partid_'.$ctr};
  push @partid,$partid;   push @partid,$partid;
  $weight{$partid} = $ENV{'form.weight_'.$partid};   $weight{$partid} = $env{'form.weight_'.$partid};
  $ctr++;   $ctr++;
     }      }
       my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  $header .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.   $header .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.
     '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';      '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';
Line 2204  sub editgrades { Line 3026  sub editgrades {
     if ($type eq 'awarded' || $type eq 'solved') { next; }      if ($type eq 'awarded' || $type eq 'solved') { next; }
     my $display=&Apache::lonnet::metadata($url,$stores.'.display');      my $display=&Apache::lonnet::metadata($url,$stores.'.display');
     $display =~ s/\[Part: (\w)+\]//;      $display =~ s/\[Part: (\w)+\]//;
     $header .= '<td align="center">&nbsp;<b>Old</b> '.$display.'&nbsp;</td>'.      $display =~ s/Number of Attempts/Tries/;
  '<td align="center">&nbsp;<b>New</b> '.$display.'&nbsp;</td>';      $header .= '<td align="center">&nbsp;<b>Old '.$display.'</b>&nbsp;</td>'.
    '<td align="center">&nbsp;<b>New '.$display.'</b>&nbsp;</td>';
     $columns{$partid}+=2;      $columns{$partid}+=2;
  }   }
     }      }
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
    my $display_part=&get_display_part($partid,$symb);
  $result .= '<td colspan="'.$columns{$partid}.   $result .= '<td colspan="'.$columns{$partid}.
     '" align="center"><b>Part '.$partid.      '" align="center"><b>Part:</b> '.$display_part.
     '</b> (Weight = '.$weight{$partid}.')</td>';      ' (Weight = '.$weight{$partid}.')</td>';
   
     }      }
     $result .= '</tr><tr bgcolor="#deffff">';      $result .= '</tr><tr bgcolor="#deffff">';
     $result .= $header;      $result .= $header;
     $result .= '</tr>'."\n";      $result .= '</tr>'."\n";
     my $noupdate;      my $noupdate;
     for ($i=0; $i<$ENV{'form.total'}; $i++) {      my ($updateCtr,$noupdateCtr) = (1,1);
       for ($i=0; $i<$env{'form.total'}; $i++) {
  my $line;   my $line;
  my $user = $ENV{'form.ctr'.$i};   my $user = $env{'form.ctr'.$i};
  my $usercolon = $user;   my ($uname,$udom)=split(/:/,$user);
  $usercolon =~s/_/:/;  
  my ($uname,$udom)=split(/_/,$user);  
  my %newrecord;   my %newrecord;
  my $updateflag = 0;   my $updateflag = 0;
  $line .= '<tr bgcolor="#ffffde"><td>'.$uname.'&nbsp;</td><td>'.   $line .= '<td>'.&nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
     $udom.'&nbsp;</td><td>'.  
  $$fullname{$usercolon}.'&nbsp;</td>';  
  my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
  if (!&canmodify($usec)) {   if (!&canmodify($usec)) {
     my $numcols=scalar(@partid)*(scalar(@parts)-1)*2;      my $numcols=scalar(@partid)*4+2;
     $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";      $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";
     next;      next;
  }   }
           my %aggregate = ();
           my $aggregateflag = 0;
    $user=~s/:/_/; # colon doen't work in javascript for names
  foreach (@partid) {   foreach (@partid) {
     my $old_aw    = $ENV{'form.GD_'.$user.'_'.$_.'_awarded_s'};      my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};
     my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);      my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
     my $old_part  = $old_aw eq '' ? '' : $old_part_pcr;      my $old_part  = $old_aw eq '' ? '' : $old_part_pcr;
     my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};      my $old_score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
       my $awarded   = $env{'form.GD_'.$user.'_'.$_.'_awarded'};
     my $awarded   = $ENV{'form.GD_'.$user.'_'.$_.'_awarded'};  
     my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);      my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
     my $partial   = $awarded eq '' ? '' : $pcr;      my $partial   = $awarded eq '' ? '' : $pcr;
     my $score;      my $score;
     if ($partial eq '') {      if ($partial eq '') {
  $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};   $score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
     } elsif ($partial > 0) {      } elsif ($partial > 0) {
  $score = 'correct_by_override';   $score = 'correct_by_override';
     } elsif ($partial == 0) {      } elsif ($partial == 0) {
  $score = 'incorrect_by_override';   $score = 'incorrect_by_override';
     }      }
     $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_solved'} eq 'excused') &&      my $dropMenu = $env{'form.GD_'.$user.'_'.$_.'_solved'};
    ($score ne 'excused'));      $score = 'excused' if (($dropMenu eq 'excused') && ($score ne 'excused'));
     $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.  
  '<td align="center">'.$awarded.  
  ($score eq 'excused' ? $score : '').'&nbsp;</td>';  
   
     if (!($old_part eq $partial && $old_score eq $score)) {      $newrecord{'resource.'.$_.'.regrader'}=
    "$env{'user.name'}:$env{'user.domain'}";
       if ($dropMenu eq 'reset status' &&
    $old_score ne '') { # ignore if no previous attempts => nothing to reset
    $newrecord{'resource.'.$_.'.tries'} = '';
    $newrecord{'resource.'.$_.'.solved'} = '';
    $newrecord{'resource.'.$_.'.award'} = '';
    $newrecord{'resource.'.$_.'.awarded'} = '';
    $updateflag = 1;
                   if ($env{'form.GD_'.$user.'_'.$_.'_aggtries'} > 0) {
                       my $aggtries = $env{'form.GD_'.$user.'_'.$_.'_aggtries'};
                       my $totaltries = $env{'form.GD_'.$user.'_'.$_.'_totaltries'};
                       my $solvedstatus = $env{'form.GD_'.$user.'_'.$_.'_solved_s'};
                       &decrement_aggs($symb,$_,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                       $aggregateflag = 1;
                   }
       } elsif (!($old_part eq $partial && $old_score eq $score)) {
  $updateflag = 1;   $updateflag = 1;
  $newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';   $newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';
  $newrecord{'resource.'.$_.'.solved'}   = $score;   $newrecord{'resource.'.$_.'.solved'}   = $score;
  $rec_update++;   $rec_update++;
     }      }
   
       $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
    '<td align="center">'.$awarded.
    ($score eq 'excused' ? $score : '').'&nbsp;</td>';
   
   
     my $partid=$_;      my $partid=$_;
     foreach my $stores (@parts) {      foreach my $stores (@parts) {
  my ($part,$type) = &split_part_type($stores);   my ($part,$type) = &split_part_type($stores);
  if ($part !~ m/^\Q$partid\E/) { next;}   if ($part !~ m/^\Q$partid\E/) { next;}
  if ($type eq 'awarded' || $type eq 'solved') { next; }   if ($type eq 'awarded' || $type eq 'solved') { next; }
  my $old_aw    = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'};   my $old_aw    = $env{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'};
  my $awarded   = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type};   my $awarded   = $env{'form.GD_'.$user.'_'.$part.'_'.$type};
  if ($awarded ne '' && $awarded ne $old_aw) {   if ($awarded ne '' && $awarded ne $old_aw) {
     $newrecord{'resource.'.$part.'.'.$type}= $awarded;      $newrecord{'resource.'.$part.'.'.$type}= $awarded;
     $newrecord{'resource.'.$part.'regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";      $newrecord{'resource.'.$part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
     $updateflag=1;      $updateflag=1;
  }   }
  $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.   $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
Line 2283  sub editgrades { Line 3124  sub editgrades {
     }      }
  }   }
  $line.='</tr>'."\n";   $line.='</tr>'."\n";
   
    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
   
  if ($updateflag) {   if ($updateflag) {
     $count++;      $count++;
     &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'},      &Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},
     $udom,$uname);      $udom,$uname);
     $result.=$line;  
       if (&Apache::bridgetask::in_queue('gradingqueue',$symb,$cdom,
         $cnum,$udom,$uname)) {
    # need to figure out if should be in queue.
    my %record =  
       &Apache::lonnet::restore($symb,$env{'request.course.id'},
        $udom,$uname);
    my $all_graded = 1;
    my $none_graded = 1;
    foreach my $part (@parts) {
       if ( $record{'resource.'.$part.'.awarded'} eq '' ) {
    $all_graded = 0;
       } else {
    $none_graded = 0;
       }
    }
   
    if ($all_graded || $none_graded) {
       &Apache::bridgetask::remove_from_queue('gradingqueue',
      $symb,$cdom,$cnum,
      $udom,$uname);
    }
       }
   
       $result.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line;
       $updateCtr++;
  } else {   } else {
     $noupdate.=$line;      $noupdate.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line;
       $noupdateCtr++;
  }   }
           if ($aggregateflag) {
               &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
     $cdom,$cnum);
           }
     }      }
     if ($noupdate) {      if ($noupdate) {
  my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;  # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
  $result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occured For the Students Below</td></tr>'.$noupdate;   my $numcols=scalar(@partid)*4+2;
    $result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occurred For the Students Below</td></tr><tr bgcolor="#ffffde">'.$noupdate;
     }      }
     $result .= '</table></td></tr></table>'."\n".      $result .= '</table></td></tr></table>'."\n".
  &show_grading_menu_form ($symb,$url);   &show_grading_menu_form ($symb);
     my $msg = '<b>Number of records updated = '.$rec_update.      my $msg = '<br /><b>Number of records updated = '.$rec_update.
  ' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.   ' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.
  '<b>Total number of students = '.$ENV{'form.total'}.'</b><br />';   '<b>Total number of students = '.$env{'form.total'}.'</b><br />';
     return $title.$msg.$result;      return $title.$msg.$result;
 }  }
   
Line 2323  sub split_part_type { Line 3199  sub split_part_type {
 #  #
 #--- Javascript to handle csv upload  #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {  sub csvupload_javascript_reverse_associate {
       my $error1=&mt('You need to specify the username or ID');
       my $error2=&mt('You need to specify at least one grading field');
   return(<<ENDPICK);    return(<<ENDPICK);
   function verify(vf) {    function verify(vf) {
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var founddomain=0;      var foundID=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (i==0 && tw!=0) { founduname=1; }        if (i==0 && tw!=0) { foundID=1; }
       if (i==1 && tw!=0) { founddomain=1; }        if (i==1 && tw!=0) { founduname=1; }
       if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }        if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }
     }      }
     if (founduname==0 || founddomain==0) {      if (founduname==0 && foundID==0) {
       alert('You need to specify at both the username and domain');   alert('$error1');
       return;   return;
     }      }
     if (foundsomething==0) {      if (foundsomething==0) {
       alert('You need to specify at least one grading field');   alert('$error2');
       return;   return;
     }      }
     vf.submit();      vf.submit();
   }    }
Line 2361  ENDPICK Line 3239  ENDPICK
 }  }
   
 sub csvupload_javascript_forward_associate {  sub csvupload_javascript_forward_associate {
       my $error1=&mt('You need to specify the username or ID');
       my $error2=&mt('You need to specify at least one grading field');
   return(<<ENDPICK);    return(<<ENDPICK);
   function verify(vf) {    function verify(vf) {
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var founddomain=0;      var foundID=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (tw==1) { founduname=1; }        if (tw==1) { foundID=1; }
       if (tw==2) { founddomain=1; }        if (tw==2) { founduname=1; }
       if (tw>2) { foundsomething=1; }        if (tw>3) { foundsomething=1; }
     }      }
     if (founduname==0 || founddomain==0) {      if (founduname==0 && foundID==0) {
       alert('You need to specify at both the username and domain');   alert('$error1');
       return;   return;
     }      }
     if (foundsomething==0) {      if (foundsomething==0) {
       alert('You need to specify at least one grading field');   alert('$error2');
       return;   return;
     }      }
     vf.submit();      vf.submit();
   }    }
Line 2396  ENDPICK Line 3276  ENDPICK
 }  }
   
 sub csvuploadmap_header {  sub csvuploadmap_header {
     my ($request,$symb,$url,$datatoken,$distotal)= @_;      my ($request,$symb,$datatoken,$distotal)= @_;
     my $javascript;      my $javascript;
     if ($ENV{'form.upfile_associate'} eq 'reverse') {      if ($env{'form.upfile_associate'} eq 'reverse') {
  $javascript=&csvupload_javascript_reverse_associate();   $javascript=&csvupload_javascript_reverse_associate();
     } else {      } else {
  $javascript=&csvupload_javascript_forward_associate();   $javascript=&csvupload_javascript_forward_associate();
     }      }
   
     my ($result,$resptype,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'});      my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
       my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
       my $ignore=&mt('Ignore First Line');
     $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 <h3><font color="#339933">Uploading Class Grades</font></h3>  <h3><font color="#339933">Uploading Class Grades</font></h3>
 $result  $result
 <hr>  <hr />
 <h3>Identify fields</h3>  <h3>Identify fields</h3>
 Total number of records found in file: $distotal <hr />  Total number of records found in file: $distotal <hr />
 Enter as many fields as you can. The system will inform you and bring you back  Enter as many fields as you can. The system will inform you and bring you back
 to this page if the data selected is insufficient to run your class.<hr />  to this page if the data selected is insufficient to run your class.<hr />
 <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />  <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
   <label><input type="checkbox" name="noFirstLine" $checked />$ignore</label>
 <input type="hidden" name="associate"  value="" />  <input type="hidden" name="associate"  value="" />
 <input type="hidden" name="phase"      value="three" />  <input type="hidden" name="phase"      value="three" />
 <input type="hidden" name="datatoken"  value="$datatoken" />  <input type="hidden" name="datatoken"  value="$datatoken" />
 <input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" />  <input type="hidden" name="fileupload" value="$env{'form.fileupload'}" />
 <input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" />  <input type="hidden" name="upfiletype" value="$env{'form.upfiletype'}" />
 <input type="hidden" name="upfile_associate"   <input type="hidden" name="upfile_associate" 
                                        value="$ENV{'form.upfile_associate'}" />                                         value="$env{'form.upfile_associate'}" />
 <input type="hidden" name="symb"       value="$symb" />  <input type="hidden" name="symb"       value="$symb" />
 <input type="hidden" name="url"        value="$url" />  <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 <input type="hidden" name="saveState"  value="$ENV{'form.saveState'}" />  <input type="hidden" name="probTitle"  value="$env{'form.probTitle'}" />
 <input type="hidden" name="probTitle"  value="$ENV{'form.probTitle'}" />  <input type="hidden" name="command"    value="csvuploadoptions" />
 <input type="hidden" name="command"    value="csvuploadassign" />  
 <hr />  <hr />
 <script type="text/javascript" language="Javascript">  <script type="text/javascript" language="Javascript">
 $javascript  $javascript
 </script>  </script>
 ENDPICK  ENDPICK
     $request->print(&show_grading_menu_form($symb,$url));  
     return '';      return '';
   
 }  }
   
 sub csvupload_fields {  sub csvupload_fields {
     my ($url) = @_;      my ($symb) = @_;
     my (@parts) = &getpartlist($url);      my (@parts) = &getpartlist($symb);
     my @fields=(['username','Student Username'],['domain','Student Domain']);      my @fields=(['ID','Student ID'],
    ['username','Student Username'],
    ['domain','Student Domain']);
       my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $part (sort(@parts)) {      foreach my $part (sort(@parts)) {
  my @datum;   my @datum;
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display');
  my $name=$part;   my $name=$part;
  if  (!$display) { $display = $name; }   if  (!$display) { $display = $name; }
  @datum=($name,$display);   @datum=($name,$display);
    if ($name=~/^stores_(.*)_awarded/) {
       push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
    }
  push(@fields,\@datum);   push(@fields,\@datum);
     }      }
     return (@fields);      return (@fields);
Line 2464  sub csvuploadmap_footer { Line 3350  sub csvuploadmap_footer {
 ENDPICK  ENDPICK
 }  }
   
 sub upcsvScores_form {  sub checkforfile_js {
     my ($request) = shift;  
     my ($symb,$url)=&get_symb_and_url($request);  
     if (!$symb) {return '';}  
     my $result =<<CSVFORMJS;      my $result =<<CSVFORMJS;
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkUpload(formname) {      function checkUpload(formname) {
Line 2479  sub upcsvScores_form { Line 3362  sub upcsvScores_form {
     }      }
     </script>      </script>
 CSVFORMJS  CSVFORMJS
     $ENV{'form.probTitle'} = &Apache::lonnet::gettitle($symb);      return $result;
     my ($table) = &showResourceInfo($url,$ENV{'form.probTitle'});  }
   
   sub upcsvScores_form {
       my ($request) = shift;
       my ($symb)=&get_symb($request);
       if (!$symb) {return '';}
       my $result=&checkforfile_js();
       $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
       my ($table) = &showResourceInfo($symb,$env{'form.probTitle'});
     $result.=$table;      $result.=$table;
     $result.='<br /><table width=100% border=0><tr><td bgcolor="#777777">'."\n";      $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
     $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";      $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
     $result.='&nbsp;<b>Specify a file containing the class scores for current resource'.      $result.='&nbsp;<b>Specify a file containing the class scores for current resource'.
  '.</b></td></tr>'."\n";   '.</b></td></tr>'."\n";
     $result.='<tr bgcolor=#ffffe6><td>'."\n";      $result.='<tr bgcolor=#ffffe6><td>'."\n";
     my $upfile_select=&Apache::loncommon::upfile_select_html();      my $upfile_select=&Apache::loncommon::upfile_select_html();
       my $ignore=&mt('Ignore First Line');
     $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" />
 <input type="hidden" name="url" value="$url" />  
 <input type="hidden" name="command" value="csvuploadmap" />  <input type="hidden" name="command" value="csvuploadmap" />
 <input type="hidden" name="probTitle" value="$ENV{'form.probTitle'}" />  <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
 <input type="hidden" name="saveState"  value="$ENV{'form.saveState'}" />  <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 $upfile_select  $upfile_select
 <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />  <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />
   <label><input type="checkbox" name="noFirstLine" />$ignore</label>
 </form>  </form>
 ENDUPFORM  ENDUPFORM
     $result.='</td></tr></table>'."\n";      $result.='</td></tr></table>'."\n";
     $result.='</td></tr></table><br /><br />'."\n";      $result.='</td></tr></table><br /><br />'."\n";
     $result.=&show_grading_menu_form($symb,$url);      $result.=&show_grading_menu_form($symb);
     return $result;      return $result;
 }  }
   
   
 sub csvuploadmap {  sub csvuploadmap {
     my ($request)= @_;      my ($request)= @_;
     my ($symb,$url)=&get_symb_and_url($request);      my ($symb)=&get_symb($request);
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my $datatoken;      my $datatoken;
     if (!$ENV{'form.datatoken'}) {      if (!$env{'form.datatoken'}) {
  $datatoken=&Apache::loncommon::upfile_store($request);   $datatoken=&Apache::loncommon::upfile_store($request);
     } else {      } else {
  $datatoken=$ENV{'form.datatoken'};   $datatoken=$env{'form.datatoken'};
  &Apache::loncommon::load_tmp_file($request);   &Apache::loncommon::load_tmp_file($request);
     }      }
     my @records=&Apache::loncommon::upfile_record_sep();      my @records=&Apache::loncommon::upfile_record_sep();
     &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);      if ($env{'form.noFirstLine'}) { shift(@records); }
       &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
     my ($i,$keyfields);      my ($i,$keyfields);
     if (@records) {      if (@records) {
  my @fields=&csvupload_fields($url);   my @fields=&csvupload_fields($symb);
   
  if ($ENV{'form.upfile_associate'} eq 'reverse') {   if ($env{'form.upfile_associate'} eq 'reverse') {
     &Apache::loncommon::csv_print_samples($request,\@records);      &Apache::loncommon::csv_print_samples($request,\@records);
     $i=&Apache::loncommon::csv_print_select_table($request,\@records,      $i=&Apache::loncommon::csv_print_select_table($request,\@records,
   \@fields);    \@fields);
Line 2535  sub csvuploadmap { Line 3427  sub csvuploadmap {
     unshift(@fields,['none','']);      unshift(@fields,['none','']);
     $i=&Apache::loncommon::csv_samples_select_table($request,\@records,      $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
     \@fields);      \@fields);
     my %sone=&Apache::loncommon::record_sep($records[0]);              foreach my $rec (@records) {
     $keyfields=join(',',sort(keys(%sone)));                  my %temp = &Apache::loncommon::record_sep($rec);
                   if (%temp) {
                       $keyfields=join(',',sort(keys(%temp)));
                       last;
                   }
               }
  }   }
     }      }
     &csvuploadmap_footer($request,$i,$keyfields);      &csvuploadmap_footer($request,$i,$keyfields);
     $request->print(&show_grading_menu_form($symb,$url));      $request->print(&show_grading_menu_form($symb));
   
     return '';      return '';
 }  }
   
 sub csvuploadassign {  sub csvuploadoptions {
     my ($request)= @_;      my ($request)= @_;
     my ($symb,$url)=&get_symb_and_url($request);      my ($symb)=&get_symb($request);
     if (!$symb) {return '';}      my $checked=(($env{'form.noFirstLine'})?'1':'0');
     &Apache::loncommon::load_tmp_file($request);      my $ignore=&mt('Ignore First Line');
     my @gradedata = &Apache::loncommon::upfile_record_sep();      $request->print(<<ENDPICK);
     my @keyfields = split(/\,/,$ENV{'form.keyfields'});  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
     my %fields=();  <h3><font color="#339933">Uploading Class Grade Options</font></h3>
     for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {  <input type="hidden" name="command"    value="csvuploadassign" />
  if ($ENV{'form.upfile_associate'} eq 'reverse') {  <!--
     if ($ENV{'form.f'.$i} ne 'none') {  <p>
  $fields{$keyfields[$i]}=$ENV{'form.f'.$i};  <label>
      <input type="checkbox" name="show_full_results" />
      Show a table of all changes
   </label>
   </p>
   -->
   <p>
   <label>
      <input type="checkbox" name="overwite_scores" checked="checked" />
      Overwrite any existing score
   </label>
   </p>
   ENDPICK
       my %fields=&get_fields();
       if (!defined($fields{'domain'})) {
    my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');
    $request->print("\n<p> Users are in domain: ".$domform."</p>\n");
       }
       foreach my $key (sort(keys(%env))) {
    if ($key !~ /^form\.(.*)$/) { next; }
    my $cleankey=$1;
    if ($cleankey eq 'command') { next; }
    $request->print('<input type="hidden" name="'.$cleankey.
    '"  value="'.$env{$key}.'" />'."\n");
       }
       # FIXME do a check for any duplicated user ids...
       # FIXME do a check for any invalid user ids?...
       $request->print('<input type="submit" value="Assign Grades" /><br />
   <hr /></form>'."\n");
       $request->print(&show_grading_menu_form($symb));
       return '';
   }
   
   sub get_fields {
       my %fields;
       my @keyfields = split(/\,/,$env{'form.keyfields'});
       for (my $i=0; $i<=$env{'form.nfields'}; $i++) {
    if ($env{'form.upfile_associate'} eq 'reverse') {
       if ($env{'form.f'.$i} ne 'none') {
    $fields{$keyfields[$i]}=$env{'form.f'.$i};
     }      }
  } else {   } else {
     if ($ENV{'form.f'.$i} ne 'none') {      if ($env{'form.f'.$i} ne 'none') {
  $fields{$ENV{'form.f'.$i}}=$keyfields[$i];   $fields{$env{'form.f'.$i}}=$keyfields[$i];
     }      }
  }   }
     }      }
       return %fields;
   }
   
   sub csvuploadassign {
       my ($request)= @_;
       my ($symb)=&get_symb($request);
       if (!$symb) {return '';}
       &Apache::loncommon::load_tmp_file($request);
       my @gradedata = &Apache::loncommon::upfile_record_sep();
       if ($env{'form.noFirstLine'}) { shift(@gradedata); }
       my %fields=&get_fields();
     $request->print('<h3>Assigning Grades</h3>');      $request->print('<h3>Assigning Grades</h3>');
     my $courseid=$ENV{'request.course.id'};      my $courseid=$env{'request.course.id'};
     my ($classlist) = &getclasslist('all',0);      my ($classlist) = &getclasslist('all',0);
     my @notallowed;      my @notallowed;
     my @skipped;      my @skipped;
     my $countdone=0;      my $countdone=0;
     foreach my $grade (@gradedata) {      foreach my $grade (@gradedata) {
  my %entries=&Apache::loncommon::record_sep($grade);   my %entries=&Apache::loncommon::record_sep($grade);
    my $domain;
    if ($entries{$fields{'domain'}}) {
       $domain=$entries{$fields{'domain'}};
    } else {
       $domain=$env{'form.default_domain'};
    }
    $domain=~s/\s//g;
  my $username=$entries{$fields{'username'}};   my $username=$entries{$fields{'username'}};
  my $domain=$entries{$fields{'domain'}};   $username=~s/\s//g;
    if (!$username) {
       my $id=$entries{$fields{'ID'}};
       $id=~s/\s//g;
       my %ids=&Apache::lonnet::idget($domain,$id);
       $username=$ids{$id};
    }
  if (!exists($$classlist{"$username:$domain"})) {   if (!exists($$classlist{"$username:$domain"})) {
     push(@skipped,"$username:$domain");      my $id=$entries{$fields{'ID'}};
       $id=~s/\s//g;
       if ($id) {
    push(@skipped,"$id:$domain");
       } else {
    push(@skipped,"$username:$domain");
       }
     next;      next;
  }   }
  my $usec=$classlist->{"$username:$domain"}[5];   my $usec=$classlist->{"$username:$domain"}[5];
Line 2583  sub csvuploadassign { Line 3549  sub csvuploadassign {
     push(@notallowed,"$username:$domain");      push(@notallowed,"$username:$domain");
     next;      next;
  }   }
    my %points;
  my %grades;   my %grades;
  foreach my $dest (keys(%fields)) {   foreach my $dest (keys(%fields)) {
     if ($dest eq 'username' || $dest eq 'domain') { next; }      if ($dest eq 'ID' || $dest eq 'username' ||
     if ($entries{$fields{$dest}} eq '') { next; }   $dest eq 'domain') { next; }
     my $store_key=$dest;      if ($entries{$fields{$dest}} =~ /^\s*$/) { next; }
     $store_key=~s/^stores/resource/;      if ($dest=~/stores_(.*)_points/) {
     $store_key=~s/_/\./g;   my $part=$1;
     $grades{$store_key}=$entries{$fields{$dest}};   my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
  }        $symb,$domain,$username);
  $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";   $entries{$fields{$dest}}=~s/\s//g;
  &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},   my $pcr=$entries{$fields{$dest}} / $wgt;
  $domain,$username);   my $award='correct_by_override';
  $request->print('.');   $grades{"resource.$part.awarded"}=$pcr;
    $grades{"resource.$part.solved"}=$award;
    $points{$part}=1;
       } else {
    if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
    if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
    my $store_key=$dest;
    $store_key=~s/^stores/resource/;
    $store_key=~s/_/\./g;
    $grades{$store_key}=$entries{$fields{$dest}};
       }
    }
    if (! %grades) { push(@skipped,"$username:$domain no data to store"); }
    $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
   # &Apache::lonnet::logthis(" storing ".(join('-',%grades)));
    my $result=&Apache::lonnet::cstore(\%grades,$symb,
      $env{'request.course.id'},
      $domain,$username);
    if ($result eq 'ok') {
       $request->print('.');
    } else {
       $request->print("<p>
                                 <font color='red'>
                                    Failed to store student $username\@$domain.
                                    Message when trying to store was ($result)
                                 </font>
                                </p>" );
    }
  $request->rflush();   $request->rflush();
  $countdone++;   $countdone++;
     }      }
     $request->print("<br />Stored $countdone students\n");      $request->print("<br />Stored $countdone students\n");
     if (@skipped) {      if (@skipped) {
  $request->print('<p<font size="+1"><b>Skipped Students</b></font></p>');   $request->print('<p><font size="+1"><b>Skipped Students</b></font></p>');
  foreach my $student (@skipped) { $request->print("$student<br />\n"); }   foreach my $student (@skipped) { $request->print("$student<br />\n"); }
     }      }
     if (@notallowed) {      if (@notallowed) {
Line 2609  sub csvuploadassign { Line 3603  sub csvuploadassign {
  foreach my $student (@notallowed) { $request->print("$student<br />\n"); }   foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
     }      }
     $request->print("<br />\n");      $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb,$url));      $request->print(&show_grading_menu_form($symb));
     return '';      return '';
 }  }
 #------------- end of section for handling csv file upload ---------  #------------- end of section for handling csv file upload ---------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
 #  #
 #-------------- Next few routines handles grading by page/sequence  #-------------- Next few routines handle grading by page/sequence
 #  #
 #--- Select a page/sequence and a student to grade  #--- Select a page/sequence and a student to grade
 sub pickStudentPage {  sub pickStudentPage {
Line 2630  function checkPickOne(formname) { Line 3624  function checkPickOne(formname) {
  alert("Please select the student you wish to grade.");   alert("Please select the student you wish to grade.");
  return;   return;
     }      }
     var ptr = pullDownSelection(formname.selectpage);      ptr = pullDownSelection(formname.selectpage);
     formname.page.value = eval("formname.page"+ptr+".value");      formname.page.value = formname["page"+ptr].value;
     formname.title.value = eval("formname.title"+ptr+".value");      formname.title.value = formname["title"+ptr].value;
     formname.submit();      formname.submit();
 }  }
   
 </script>  </script>
 LISTJAVASCRIPT  LISTJAVASCRIPT
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my ($symb,$url) = &get_symb_and_url($request);      my ($symb) = &get_symb($request);
     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"};
     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
   
     my $result='<h3><font color="#339933">&nbsp;'.      my $result='<h3><font color="#339933">&nbsp;'.
  'Manual Grading by Page or Sequence</font></h3>';   'Manual Grading by Page or Sequence</font></h3>';
Line 2650  LISTJAVASCRIPT Line 3644  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($request);
     my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/);       my ($curpage) =&Apache::lonnet::decode_symb($symb); 
   #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
   #    my $type=($curpage =~ /\.(page|sequence)/);
     my $ctr=0;      my $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
Line 2659  LISTJAVASCRIPT Line 3655  LISTJAVASCRIPT
     '>'.$showtitle.'</option>'."\n";      '>'.$showtitle.'</option>'."\n";
  $ctr++;   $ctr++;
     }      }
     $result.= '</select>'."<br>\n";      $result.= '</select>'."<br />\n";
     $ctr=0;      $ctr=0;
     foreach (@$titles) {      foreach (@$titles) {
  my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);   my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
Line 2670  LISTJAVASCRIPT Line 3666  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><input type="radio" name="vProb" value="no" checked /> no '."\n".      $result.='&nbsp;<b>View Problems Text: </b><label><input type="radio" name="vProb" value="no" checked="on" /> no </label>'."\n".
  '<input type="radio" name="vProb" value="yes" /> yes '."<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>'.
  '<input type="radio" name="lastSub" value="none" /> none'."\n".   '<label><input type="radio" name="lastSub" value="none" /> none</label>'."\n".
  '<input type="radio" name="lastSub" value="datesub" checked /> dates and submissions'."\n".   '<label><input type="radio" name="lastSub" value="datesub" checked /> by dates and submissions</label>'."\n".
  '<input type="radio" name="lastSub" value="all" /> all details'."\n";   '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n";
   
     $result.='<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".      $result.='<input type="hidden" name="section"     value="'.$getsec.'" />'."\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="command" value="displayPage" />'."\n".   '<input type="hidden" name="command" value="displayPage" />'."\n".
  '<input type="hidden" name="url"     value="'.$url.'" />'."\n".  
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.$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;<input type="button" '.      $result.='&nbsp;<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);"value="Submit" /><br />'."\n";   'onClick="javascript:checkPickOne(this.form);"value="Next->" /><br />'."\n";
   
     $request->print($result);      $request->print($result);
   
     my $studentTable.='&nbsp;<b>Select a student you wish to grade</b><br>'.      my $studentTable.='&nbsp;<b>Select a student you wish to grade and then click on the Next button.</b><br />'.
  '<table border="0"><tr><td bgcolor="#777777">'.   '<table border="0"><tr><td bgcolor="#777777">'.
  '<table border="0"><tr bgcolor="#e6ffff">'.   '<table border="0"><tr bgcolor="#e6ffff">'.
  '<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.   '<td align="right">&nbsp;<b>No.</b></td>'.
  '<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.   '<td>'.&nameUserString('header').'</td>'.
  '<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.   '<td align="right">&nbsp;<b>No.</b></td>'.
  '<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td></tr>';   '<td>'.&nameUserString('header').'</td></tr>';
     
     my (undef,undef,$fullname) = &getclasslist($getsec,'1');      my (undef,undef,$fullname) = &getclasslist($getsec,'1');
     my $ptr = 1;      my $ptr = 1;
     foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {      foreach my $student (sort 
    {
        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
    return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
        }
        return $a cmp $b;
    } (keys(%$fullname))) {
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
  $studentTable.=($ptr%4 == 1 ? '<tr bgcolor="#ffffe6"><td>' : '</td><td>');   $studentTable.=($ptr%2 == 1 ? '<tr bgcolor="#ffffe6">' : '</td>');
  $studentTable.='<input type="radio" name="student" value="'.$student.'" /> '.$$fullname{$student}.   $studentTable.='<td align="right">'.$ptr.'&nbsp;</td>';
     '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font>'."\n";   $studentTable.='<td>&nbsp;<label><input type="radio" name="student" value="'.$student.'" /> '
  $studentTable.=($ptr%4 == 0 ? '</td></tr>' : '');      .&nameUserString(undef,$$fullname{$student},$uname,$udom)."</label>\n";
    $studentTable.=($ptr%2 == 0 ? '</td></tr>' : '');
  $ptr++;   $ptr++;
     }      }
     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%4 == 2);      $studentTable.='</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%2 == 0);
     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%4 == 3);  
     $studentTable.='</td><td>&nbsp;' if ($ptr%4 == 0);  
     $studentTable.='</td></tr></table></td></tr></table>'."\n";      $studentTable.='</td></tr></table></td></tr></table>'."\n";
     $studentTable.='<br />&nbsp;<input type="button" '.      $studentTable.='<input type="button" '.
  'onClick="javascript:checkPickOne(this.form);"value="Submit" /></form>'."\n";   'onClick="javascript:checkPickOne(this.form);"value="Next->" /></form>'."\n";
   
     $studentTable.=&show_grading_menu_form($symb,$url);      $studentTable.=&show_grading_menu_form($symb);
     $request->print($studentTable);      $request->print($studentTable);
   
     return '';      return '';
Line 2723  LISTJAVASCRIPT Line 3723  LISTJAVASCRIPT
   
 sub getSymbMap {  sub getSymbMap {
     my ($request) = @_;      my ($request) = @_;
     my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',      my $navmap = Apache::lonnavmaps::navmap->new();
   $ENV{'request.course.fn'}.'_parms.db');  
     $navmap->init();  
   
     my %symbx = ();      my %symbx = ();
     my @titles = ();      my @titles = ();
     my $minder = 0;      my $minder = 0;
   
     # Gather every sequence that has problems.      # Gather every sequence that has problems.
     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, 1);      my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
          1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {      for my $sequence ($navmap->getById('0.0'), @sequences) {
  if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {   if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
     my $title = $minder.'.'.$sequence->compTitle();      my $title = $minder.'.'.$sequence->compTitle();
Line 2741  sub getSymbMap { Line 3740  sub getSymbMap {
     $minder++;      $minder++;
  }   }
     }      }
   
     $navmap->untieHashes();  
     return \@titles,\%symbx;      return \@titles,\%symbx;
 }  }
   
Line 2751  sub getSymbMap { Line 3748  sub getSymbMap {
 sub displayPage {  sub displayPage {
     my ($request) = shift;      my ($request) = shift;
   
     my ($symb,$url) = &get_symb_and_url($request);      my ($symb) = &get_symb($request);
     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"};
     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
     my $pageTitle = $ENV{'form.page'};      my $pageTitle = $env{'form.page'};
     my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');      my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');
     my ($uname,$udom) = split(/:/,$ENV{'form.student'});      my ($uname,$udom) = split(/:/,$env{'form.student'});
     my $usec=$classlist->{$ENV{'form.student'}}[5];      my $usec=$classlist->{$env{'form.student'}}[5];
   
       #need to make sure we have the correct data for later EXT calls, 
       #thus invalidate the cache
       &Apache::lonnet::devalidatecourseresdata(
                    $env{'course.'.$env{'request.course.id'}.'.num'},
                    $env{'course.'.$env{'request.course.id'}.'.domain'});
       &Apache::lonnet::clear_EXT_cache_status();
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
  $request->print('<font color="red">Unable to view requested student.('.$ENV{'form.student'}.')</font>');   $request->print('<font color="red">Unable to view requested student.('.$env{'form.student'}.')</font>');
  $request->print(&show_grading_menu_form($symb,$url));   $request->print(&show_grading_menu_form($symb));
  return;   return;
     }      }
     my $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';      my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';
     $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.      $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom).
  '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";   '</h3>'."\n";
   
     &sub_page_js($request);      &sub_page_js($request);
     $request->print($result);      $request->print($result);
   
     my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',      my $navmap = Apache::lonnavmaps::navmap->new();
   $ENV{'request.course.fn'}.'_parms.db',1, 1);      my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});
     my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});  
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
       if (!$map) {
    $request->print('<font color="red">Unable to view requested sequence. ('.$resUrl.')</font>');
    $request->print(&show_grading_menu_form($symb));
    return; 
       }
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
  $map->map_finish());   $map->map_finish());
   
     my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n".      my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n".
  '<input type="hidden" name="command" value="gradeByPage" />'."\n".   '<input type="hidden" name="command" value="gradeByPage" />'."\n".
  '<input type="hidden" name="student" value="'.$ENV{'form.student'}.'" />'."\n".   '<input type="hidden" name="fullname" value="'.$$fullname{$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="url"     value="'.$url.'" />'."\n".  
  '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
  '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n";   '<input type="hidden" name="overRideScore" value="no" />'."\n".
    '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n";
   
     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').      my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
  '/check.gif" height="16" border="0" />';   '/check.gif" height="16" border="0" />';
Line 2796  sub displayPage { Line 3804  sub displayPage {
  '<table border="0"><tr><td bgcolor="#777777">'.   '<table border="0"><tr><td bgcolor="#777777">'.
  '<table border="0"><tr bgcolor="#e6ffff">'.   '<table border="0"><tr bgcolor="#e6ffff">'.
  '<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.   '<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
  '<td><b>&nbsp;'.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';   '<td><b>&nbsp;'.($env{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';
   
     my ($depth,$question) = (1,1);      &Apache::lonxml::clear_problem_counter();
       my ($depth,$question,$prob) = (1,1,1);
     $iterator->next(); # skip the first BEGIN_MAP      $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"      my $curRes = $iterator->next(); # for "current resource"
     while ($depth > 0) {      while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
   
 #        if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {          if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
         if (ref($curRes) && $curRes->is_problem()) {  
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
     $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.      $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
  (scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';   (scalar(@{$parts}) == 1 ? '' : '<br />('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
     $studentTable.='<td valign="top">';      $studentTable.='<td valign="top">';
     if ($ENV{'form.vProb'} eq 'yes') {      if ($env{'form.vProb'} eq 'yes' ) {
  $studentTable.=&show_problem($request,$symbx,$uname,$udom,1);   $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
        undef,'both');
     } else {      } else {
  my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$ENV{'request.course.id'});   my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'});
  $companswer =~ s|<form(.*?)>||g;   $companswer =~ s|<form(.*?)>||g;
  $companswer =~ s|</form>||g;   $companswer =~ s|</form>||g;
 # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>  # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
 #    $companswer =~ s/$1/ /ms;  #    $companswer =~ s/$1/ /ms;
 #    $request->print('match='.$1."<br>\n");  #    $request->print('match='.$1."<br />\n");
 # }  # }
 # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;  # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
  $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br>&nbsp;<b>Correct answer:</b><br>'.$companswer;   $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>Correct answer:</b><br />'.$companswer;
     }      }
   
     my %record = &Apache::lonnet::restore($symbx,$ENV{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
     if ($ENV{'form.lastSub'} eq 'datesub') {  
       if ($env{'form.lastSub'} eq 'datesub') {
  if ($record{'version'} eq '') {   if ($record{'version'} eq '') {
     $studentTable.='<br />&nbsp;<font color="red">No recorded submission for this problem</font><br />';      $studentTable.='<br />&nbsp;<font color="red">No recorded submission for this problem</font><br />';
  } else {   } else {
     my %responseType = ();      my %responseType = ();
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
  $responseType{$partid} = $curRes->responseType($partid);   my @responseIds =$curRes->responseIds($partid);
    my @responseType =$curRes->responseType($partid);
    my %responseIds;
    for (my $i=0;$i<=$#responseIds;$i++) {
       $responseIds{$responseIds[$i]}=$responseType[$i];
    }
    $responseType{$partid} = \%responseIds;
     }      }
     $studentTable.= &displaySubByDates(\%record,$parts,\%responseType,$checkIcon);      $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
   
  }   }
     } elsif ($ENV{'form.lastSub'} eq 'all') {      } elsif ($env{'form.lastSub'} eq 'all') {
  my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');   my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
  $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,   $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
  $ENV{'request.course.id'},   $env{'request.course.id'},
  '','.submission');   '','.submission');
     
     }      }
Line 2851  sub displayPage { Line 3868  sub displayPage {
     $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";      $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
     $question++;      $question++;
  }   }
    $prob++;
     }      }
     $studentTable.='</td></tr>';      $studentTable.='</td></tr>';
   
Line 2858  sub displayPage { Line 3876  sub displayPage {
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
   
     $navmap->untieHashes();  
   
     $studentTable.='</td></tr></table></td></tr></table>'."\n".      $studentTable.='</td></tr></table></td></tr></table>'."\n".
  '&nbsp;&nbsp;<input type="button" value="Save" '.   '<input type="button" value="Save" '.
  'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.   'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.
  '</form>'."\n";   '</form>'."\n";
     $studentTable.=&show_grading_menu_form($symb,$url);      $studentTable.=&show_grading_menu_form($symb);
     $request->print($studentTable);      $request->print($studentTable);
   
     return '';      return '';
 }  }
   
 sub displaySubByDates {  sub displaySubByDates {
     my ($record,$parts,$responseType,$checkIcon) = @_;      my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
       my $isCODE=0;
       my $isTask = ($symb =~/\.task$/);
       if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.      my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
  '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.   '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
  '<td><b>Date/Time</b></td>'.   '<td><b>Date/Time</b></td>'.
    ($isCODE?'<td><b>CODE</b></td>':'').
  '<td><b>Submission</b></td>'.   '<td><b>Submission</b></td>'.
  '<td><b>Status&nbsp;</b></td></tr>';   '<td><b>Status&nbsp;</b></td></tr>';
     my ($version);      my ($version);
     my %mark;      my %mark;
       my %orders;
     $mark{'correct_by_student'} = $checkIcon;      $mark{'correct_by_student'} = $checkIcon;
       if (!exists($$record{'1:timestamp'})) {
    return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />';
       }
   
       my $interaction;
     for ($version=1;$version<=$$record{'version'};$version++) {      for ($version=1;$version<=$$record{'version'};$version++) {
  my $timestamp = scalar(localtime($$record{$version.':timestamp'}));   my $timestamp = scalar(localtime($$record{$version.':timestamp'}));
    if (exists($$record{$version.':resource.0.version'})) {
       $interaction = $$record{$version.':resource.0.version'};
    }
   
    my $where = ($isTask ? "$version:resource.$interaction"
                : "$version:resource");
    #&Apache::lonnet::logthis(" got $where");
  $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';   $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';
    if ($isCODE) {
       $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>';
    }
  my @versionKeys = split(/\:/,$$record{$version.':keys'});   my @versionKeys = split(/\:/,$$record{$version.':keys'});
  my @displaySub = ();   my @displaySub = ();
  foreach my $partid (@{$parts}) {   foreach my $partid (@{$parts}) {
     my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys;      my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)
     next if ($$record{"$version:resource.$partid.solved"} eq '');              : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
     $displaySub[0].=(exists $$record{$version.':'.$matchKey[0]}) ?       
  '<b>Part&nbsp;'.$partid.'&nbsp;'.  
  ($$record{"$version:resource.$partid.tries"} eq '' ? 'Trial&nbsp;not&nbsp;counted' :  #    next if ($$record{"$version:resource.$partid.solved"} eq '');
  'Trial&nbsp;'.$$record{"$version:resource.$partid.tries"}).'</b>&nbsp; '.      my $display_part=&get_display_part($partid,$symb);
  &cleanRecord($$record{$version.':'.$matchKey[0]},$$responseType{$partid}).'<br />' : '';      foreach my $matchKey (@matchKey) {
     $displaySub[1].=(exists $$record{"$version:resource.$partid.award"}) ?   if (exists($$record{$version.':'.$matchKey}) &&
  '<b>Part&nbsp;'.$partid.'</b> &nbsp;'.      $$record{$version.':'.$matchKey} ne '') {
  lc($$record{"$version:resource.$partid.award"}).' '.  
  $mark{$$record{"$version:resource.$partid.solved"}}.'<br />' : '';      my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
 #    $$record{"$version:resource.$partid.solved"}.'<br />' : '';                 : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
     $displaySub[2].=(exists $$record{"$version:resource.$partid.regrader"}) ?      #&Apache::lonnet::logthis("match $matchKey $responseId (".$$record{$version.':'.$matchKey});
  $$record{"$version:resource.$partid.regrader"}.' (<b>Part:</b> '.$partid.')' : '';      $displaySub[0].='<b>Part:</b>&nbsp;'.$display_part.'&nbsp;';
  }      $displaySub[0].='<font color="#999999">(ID&nbsp;'.
  $displaySub[2].=(exists $$record{"$version:resource.regrader"}) ?   $responseId.')</font>&nbsp;<b>';
     $$record{"$version:resource.regrader"} : '';      if ($$record{"$where.$partid.tries"} eq '') {
  $studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1].   $displaySub[0].='Trial&nbsp;not&nbsp;counted';
     ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).'&nbsp;</td></tr>';      } else {
    $displaySub[0].='Trial&nbsp;'.
       $$record{"$where.$partid.tries"};
       }
       my $responseType=($isTask ? 'Task'
                                                 : $responseType->{$partid}->{$responseId});
       if (!exists($orders{$partid})) { $orders{$partid}={}; }
       if (!exists($orders{$partid}->{$responseId})) {
    $orders{$partid}->{$responseId}=
       &get_order($partid,$responseId,$symb,$uname,$udom);
       }
       $displaySub[0].='</b>&nbsp; '.
    &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />';
    }
       }
       if (exists($$record{"$where.$partid.checkedin"})) {
    $displaySub[1].='Checked in by '.
       $$record{"$where.$partid.checkedin"}.' into slot '.
       $$record{"$where.$partid.checkedin.slot"}.
       '<br />';
       }
       if (exists $$record{"$where.$partid.award"}) {
    $displaySub[1].='<b>Part:</b>&nbsp;'.$display_part.' &nbsp;'.
       lc($$record{"$where.$partid.award"}).' '.
       $mark{$$record{"$where.$partid.solved"}}.
       '<br />';
       }
       if (exists $$record{"$where.$partid.regrader"}) {
    $displaySub[2].=$$record{"$where.$partid.regrader"}.
       ' (<b>'.&mt('Part').':</b> '.$display_part.')';
       } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {
    $displaySub[2].=
       $$record{"$version:resource.$partid.regrader"}.
       ' (<b>'.&mt('Part').':</b> '.$display_part.')';
       }
    }
    # needed because old essay regrader has not parts info
    if (exists $$record{"$version:resource.regrader"}) {
       $displaySub[2].=$$record{"$version:resource.regrader"};
    }
    $studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1];
    if ($displaySub[2]) {
       $studentTable.='Manually graded by '.$displaySub[2];
    }
    $studentTable.='&nbsp;</td></tr>';
       
     }      }
     $studentTable.='</table></td></tr></table>';      $studentTable.='</table></td></tr></table>';
     return $studentTable;      return $studentTable;
Line 2913  sub displaySubByDates { Line 3994  sub displaySubByDates {
 sub updateGradeByPage {  sub updateGradeByPage {
     my ($request) = shift;      my ($request) = shift;
   
     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"};
     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
     my $pageTitle = $ENV{'form.page'};      my $pageTitle = $env{'form.page'};
     my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');      my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');
     my ($uname,$udom) = split(/:/,$ENV{'form.student'});      my ($uname,$udom) = split(/:/,$env{'form.student'});
     my $usec=$classlist->{$ENV{'form.student'}}[5];      my $usec=$classlist->{$env{'form.student'}}[5];
     if (!&canmodify($usec)) {      if (!&canmodify($usec)) {
  $request->print('<font color="red">Unable to modify requested student.('.$ENV{'form.student'}.'</font>');   $request->print('<font color="red">Unable to modify requested student.('.$env{'form.student'}.'</font>');
  $request->print(&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'}));   $request->print(&show_grading_menu_form($env{'form.symb'}));
  return;   return;
     }      }
     my $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';      my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';
     $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.      $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).
  '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";   '</h3>'."\n";
   
     $request->print($result);      $request->print($result);
   
     my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',      my $navmap = Apache::lonnavmaps::navmap->new();
   $ENV{'request.course.fn'}.'_parms.db',1, 1);      my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});
     my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});  
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps      my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
       if (!$map) {
    $request->print('<font color="red">Unable to grade requested sequence. ('.$resUrl.')</font>');
    my ($symb)=&get_symb($request);
    $request->print(&show_grading_menu_form($symb));
    return; 
       }
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
  $map->map_finish());   $map->map_finish());
   
     my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.      my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.
  '<table border="0"><tr bgcolor="#e6ffff">'.   '<table border="0"><tr bgcolor="#e6ffff">'.
  '<td align="center"><b>&nbsp;No&nbsp;</b></td>'.   '<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
  '<td><b>&nbsp;Title&nbsp;</b></td>'.   '<td><b>&nbsp;Title&nbsp;</b></td>'.
  '<td><b>&nbsp;Previous Score&nbsp;</b></td>'.   '<td><b>&nbsp;Previous Score&nbsp;</b></td>'.
  '<td><b>&nbsp;New Score&nbsp;</b></td></tr>';   '<td><b>&nbsp;New Score&nbsp;</b></td></tr>';
   
     $iterator->next(); # skip the first BEGIN_MAP      $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"      my $curRes = $iterator->next(); # for "current resource"
     my ($depth,$question,$changeflag)= (1,1,0);      my ($depth,$question,$prob,$changeflag)= (1,1,1,0);
     while ($depth > 0) {      while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
Line 2957  sub updateGradeByPage { Line 4042  sub updateGradeByPage {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
     $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.      $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
  (scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';   (scalar(@{$parts}) == 1 ? '' : '<br />('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
     $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';      $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
   
     my %newrecord=();      my %newrecord=();
     my @displayPts=();      my @displayPts=();
               my %aggregate = ();
               my $aggregateflag = 0;
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
  my $newpts = $ENV{'form.GD_BOX'.$question.'_'.$partid};   my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
  my $oldpts = $ENV{'form.oldpts'.$question.'_'.$partid};   my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
   
  my $wgt = $ENV{'form.WGT'.$question.'_'.$partid} != 0 ?    my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ? 
     $ENV{'form.WGT'.$question.'_'.$partid} : 1;      $env{'form.WGT'.$question.'_'.$partid} : 1;
  my $partial = $newpts/$wgt;   my $partial = $newpts/$wgt;
  my $score;   my $score;
  if ($partial > 0) {   if ($partial > 0) {
     $score = 'correct_by_override';      $score = 'correct_by_override';
  } elsif ($partial == 0) {   } elsif ($newpts ne '') { #empty is taken as 0
     $score = 'incorrect_by_override';      $score = 'incorrect_by_override';
  }   }
  if ($ENV{'form.GD_SEL'.$question.'_'.$partid} eq 'excused') {   my $dropMenu = $env{'form.GD_SEL'.$question.'_'.$partid};
    if ($dropMenu eq 'excused') {
     $partial = '';      $partial = '';
     $score = 'excused';      $score = 'excused';
    } elsif ($dropMenu eq 'reset status'
    && $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists
       $newrecord{'resource.'.$partid.'.tries'} = 0;
       $newrecord{'resource.'.$partid.'.solved'} = '';
       $newrecord{'resource.'.$partid.'.award'} = '';
       $newrecord{'resource.'.$partid.'.awarded'} = 0;
       $newrecord{'resource.'.$partid.'.regrader'} = "$env{'user.name'}:$env{'user.domain'}";
       $changeflag++;
       $newpts = '';
                       
                       my $aggtries =  $env{'form.aggtries'.$question.'_'.$partid};
                       my $totaltries = $env{'form.totaltries'.$question.'_'.$partid};
                       my $solvedstatus = $env{'form.solved'.$question.'_'.$partid};
                       if ($aggtries > 0) {
                           &decrement_aggs($symbx,$partid,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                           $aggregateflag = 1;
                       }
  }   }
  my $oldstatus = $ENV{'form.solved'.$question.'_'.$partid};   my $display_part=&get_display_part($partid,$curRes->symb());
  $displayPts[0].='&nbsp;<b>Part</b> '.$partid.' = '.   my $oldstatus = $env{'form.solved'.$question.'_'.$partid};
    $displayPts[0].='&nbsp;<b>Part:</b> '.$display_part.' = '.
     (($oldstatus eq 'excused') ? 'excused' : $oldpts).      (($oldstatus eq 'excused') ? 'excused' : $oldpts).
     '&nbsp;<br>';      '&nbsp;<br />';
  $displayPts[1].='&nbsp;<b>Part</b> '.$partid.' = '.   $displayPts[1].='&nbsp;<b>Part:</b> '.$display_part.' = '.
     ($oldstatus eq 'correct_by_student' ? $oldpts :       (($score eq 'excused') ? 'excused' : $newpts).
      (($score eq 'excused') ? 'excused' : $newpts)).      '&nbsp;<br />';
     '&nbsp;<br>';  
   
  $question++;   $question++;
  if (($oldstatus eq 'correct_by_student') ||   next if ($dropMenu eq 'reset status' || ($newpts == $oldpts && $score ne 'excused'));
     ($newpts eq $oldpts && $score eq $oldstatus))  
  {  
     next;  
  }  
  $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';   $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
  $newrecord{'resource.'.$partid.'.solved'}   = $score;   $newrecord{'resource.'.$partid.'.solved'}   = $score if $score ne '';
  $newrecord{'resource.'.$partid.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";   $newrecord{'resource.'.$partid.'.regrader'} = "$env{'user.name'}:$env{'user.domain'}"
       if (scalar(keys(%newrecord)) > 0);
   
  $changeflag++;   $changeflag++;
     }      }
     if (scalar(keys(%newrecord)) > 0) {      if (scalar(keys(%newrecord)) > 0) {
  &Apache::lonnet::cstore(\%newrecord,$symbx,$ENV{'request.course.id'},   &Apache::lonnet::cstore(\%newrecord,$symbx,$env{'request.course.id'},
  $udom,$uname);   $udom,$uname);
     }      }
               if ($aggregateflag) {
                   &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                         $env{'course.'.$env{'request.course.id'}.'.domain'},
                         $env{'course.'.$env{'request.course.id'}.'.num'});
               }
   
     $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.      $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
  '<td valign="top">'.$displayPts[1].'</td>'.   '<td valign="top">'.$displayPts[1].'</td>'.
  '</tr>';   '</tr>';
   
       $prob++;
  }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
   
     $navmap->untieHashes();  
   
     $studentTable.='</td></tr></table></td></tr></table>';      $studentTable.='</td></tr></table></td></tr></table>';
     $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'});      $studentTable.=&show_grading_menu_form($env{'form.symb'});
     my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :      my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :
   'The scores were changed for '.    'The scores were changed for '.
   $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));    $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
Line 3034  sub updateGradeByPage { Line 4142  sub updateGradeByPage {
 #------ start of section for handling grading by page/sequence ---------  #------ start of section for handling grading by page/sequence ---------
   
 sub defaultFormData {  sub defaultFormData {
     my ($symb,$url)=@_;      my ($symb)=@_;
     return '      return '
       <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".        <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
      '<input type="hidden" name="url"     value="'.$url.'" />'."\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";  
 }  }
   
 sub getSequenceDropDown {  sub getSequenceDropDown {
     my ($request,$symb)=@_;      my ($request,$symb)=@_;
     my $result='<select name="selectpage">'."\n";      my $result='<select name="selectpage">'."\n";
     my ($titles,$symbx) = &getSymbMap($request);      my ($titles,$symbx) = &getSymbMap($request);
     my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/);       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+)\.(.*)/);
Line 3059  sub getSequenceDropDown { Line 4166  sub getSequenceDropDown {
     return $result;      return $result;
 }  }
   
   sub scantron_filenames {
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
       &Apache::loncommon::propath($cdom,$cname));
       my @possiblenames;
       foreach my $filename (sort(@files)) {
    ($filename)=split(/&/,$filename);
    if ($filename!~/^scantron_orig_/) { next ; }
    $filename=~s/^scantron_orig_//;
    push(@possiblenames,$filename);
       }
       return @possiblenames;
   }
   
 sub scantron_uploads {  sub scantron_uploads {
     if (!-e $Apache::lonnet::perlvar{'lonScansDir'}) { return ''};      my ($file2grade) = @_;
     my $result= '<select name="scantron_selectfile">';      my $result= '<select name="scantron_selectfile">';
     opendir(DIR,$Apache::lonnet::perlvar{'lonScansDir'});      $result.="<option></option>";
     my @files=sort(readdir(DIR));      foreach my $filename (sort(&scantron_filenames())) {
     foreach my $filename (@files) {   $result.="<option".($filename eq $file2grade ? ' selected="on"':'').">$filename</option>\n";
  if ($filename eq '.' or $filename eq '..') { next; }  
  $result.="<option>$filename</option>\n";  
     }      }
     closedir(DIR);  
     $result.="</select>";      $result.="</select>";
     return $result;      return $result;
 }  }
Line 3076  sub scantron_uploads { Line 4195  sub scantron_uploads {
 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";
       $result.='<option></option>'."\n";
     foreach my $line (<$fh>) {      foreach my $line (<$fh>) {
  my ($name,$descrip)=split(/:/,$line);   my ($name,$descrip)=split(/:/,$line);
  if ($name =~ /^\#/) { next; }   if ($name =~ /^\#/) { next; }
Line 3086  sub scantron_scantab { Line 4206  sub scantron_scantab {
     return $result;      return $result;
 }  }
   
   sub scantron_CODElist {
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my @names=&Apache::lonnet::getkeys('CODEs',$cdom,$cnum);
       my $namechoice='<option></option>';
       foreach my $name (sort {uc($a) cmp uc($b)} @names) {
    if ($name =~ /^error: 2 /) { next; }
    if ($name =~ /^type\0/) { next; }
    $namechoice.='<option value="'.$name.'">'.$name.'</option>';
       }
       $namechoice='<select name="scantron_CODElist">'.$namechoice.'</select>';
       return $namechoice;
   }
   
   sub scantron_CODEunique {
       my $result='<nobr>
                    <label><input type="radio" name="scantron_CODEunique"
                           value="yes" checked="checked" /> Yes </label>
                   </nobr>
                   <nobr>
                    <label><input type="radio" name="scantron_CODEunique"
                           value="no" /> No </label>
                   </nobr>';
       return $result;
   }
   
 sub scantron_selectphase {  sub scantron_selectphase {
     my ($r) = @_;      my ($r,$file2grade) = @_;
     my ($symb,$url)=&get_symb_and_url($r);      my ($symb)=&get_symb($r);
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $sequence_selector=&getSequenceDropDown($r,$symb);      my $sequence_selector=&getSequenceDropDown($r,$symb);
     my $default_form_data=&defaultFormData($symb,$url);      my $default_form_data=&defaultFormData($symb);
     my $grading_menu_button=&show_grading_menu_form($symb,$url);      my $grading_menu_button=&show_grading_menu_form($symb);
     my $file_selector=&scantron_uploads();      my $file_selector=&scantron_uploads($file2grade);
     my $format_selector=&scantron_scantab();      my $format_selector=&scantron_scantab();
       my $CODE_selector=&scantron_CODElist();
       my $CODE_unique=&scantron_CODEunique();
     my $result;      my $result;
       #FIXME allow instructor to be able to download the scantron file
       # and to upload it,
     $result.= <<SCANTRONFORM;      $result.= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantro_process">      <table width="100%" border="0">
   <input type="hidden" name="command" value="scantron_process" />  
   $default_form_data  
   <table width="100%" border="0">  
     <tr>      <tr>
        <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process">
       <td bgcolor="#777777">        <td bgcolor="#777777">
          <input type="hidden" name="command" value="scantron_warning" />
           $default_form_data
         <table width="100%" border="0">          <table width="100%" border="0">
           <tr bgcolor="#e6ffff">            <tr bgcolor="#e6ffff">
             <td>              <td colspan="2">
               &nbsp;<b>Specify file location and which Folder/Sequence to grade</b>                &nbsp;<b>Specify file and which Folder/Sequence to grade</b>
             </td>              </td>
           </tr>            </tr>
           <tr bgcolor="#ffffe6">            <tr bgcolor="#ffffe6">
               <td> Sequence to grade: </td><td> $sequence_selector </td>
             </tr>
             <tr bgcolor="#ffffe6">
               <td> Filename of scoring office file: </td><td> $file_selector </td>
             </tr>
             <tr bgcolor="#ffffe6">
               <td> Format of data file: </td><td> $format_selector </td>
             </tr>
             <tr bgcolor="#ffffe6">
               <td> Saved CODEs to validate against: </td><td> $CODE_selector</td>
             </tr>
             <tr bgcolor="#ffffe6">
               <td> Each CODE is only to be used once:</td><td> $CODE_unique </td>
             </tr>
             <tr bgcolor="#ffffe6">
       <td> Options: </td>
             <td>              <td>
                Sequence to grade: $sequence_selector         <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_hidden" value="ignore_hidden"/> Skip hidden resources when grading</label>
     </td>      </td>
           </tr>            </tr>
           <tr bgcolor="#ffffe6">            <tr bgcolor="#ffffe6">
               <td colspan="2">
                 <input type="submit" value="Grading: Validate Scantron Records" />
               </td>
             </tr>
           </table>
          </td>
        </form>
       </tr>
   SCANTRONFORM
      
       $r->print($result);
   
       if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
           &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
   
           $r->print(<<SCANTRONFORM);
       <tr>
         <td bgcolor="#777777">
           <table width="100%" border="0">
             <tr bgcolor="#e6ffff">
             <td>              <td>
  Filename of scoring office file: $file_selector                &nbsp;<b>Specify a Scantron data file to upload.</b>
     </td>              </td>
           </tr>            </tr>
           <tr bgcolor="#ffffe6">            <tr bgcolor="#ffffe6">
             <td>              <td>
               Format of data file: $format_selector  SCANTRONFORM
     </td>      my $default_form_data=&defaultFormData(&get_symb($r,1));
       my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
       $r->print(<<UPLOAD);
                 <script type="text/javascript" language="javascript">
       function checkUpload(formname) {
    if (formname.upfile.value == "") {
       alert("Please use the browse button to select a file from your local directory.");
       return false;
    }
    formname.submit();
       }
                 </script>
   
                 <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'>
                   $default_form_data
                   <input name='courseid' type='hidden' value='$cnum' />
                   <input name='domainid' type='hidden' value='$cdom' />
                   <input name='command' value='scantronupload_save' type='hidden' />
                   File to upload:<input type="file" name="upfile" size="50" />
                   <br />
                   <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" />
                 </form>
   UPLOAD
   
           $r->print(<<SCANTRONFORM);
               </td>
           </tr>            </tr>
         </table>          </table>
       </td>        </td>
     </tr>      </tr>
   SCANTRONFORM
       }
       $r->print(<<SCANTRONFORM);
       <tr>
         <form action='/adm/grades' name='scantron_download'>
           <td bgcolor="#777777">
             <input type="hidden" name="command" value="scantron_download" />
             <table width="100%" border="0">
               <tr bgcolor="#e6ffff">
                 <td colspan="2">
                   &nbsp;<b>Download a scoring office file</b>
                 </td>
               </tr>
               <tr bgcolor="#ffffe6">
                 <td> Filename of scoring office file: </td><td> $file_selector </td>
               </tr>
               <tr bgcolor="#ffffe6">
                 <td colspan="2">
                   <input type="submit" value="Download: Show List of Associated Files" />
                 </td>
               </tr>
             </table>
           </td>
         </form>
       </tr>
   SCANTRONFORM
   
       $r->print(<<SCANTRONFORM);
   </table>    </table>
   <input type="submit" value="Submit" />  
 </form>  
 $grading_menu_button  $grading_menu_button
 SCANTRONFORM  SCANTRONFORM
   
     return $result;      return
 }  }
   
 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');
     my %config;      my %config;
       #FIXME probably should move to XML it has already gotten a bit much now
     foreach my $line (<$fh>) {      foreach my $line (<$fh>) {
  my ($name,$descrip)=split(/:/,$line);   my ($name,$descrip)=split(/:/,$line);
  if ($name ne $which ) { next; }   if ($name ne $which ) { next; }
Line 3156  sub get_scantron_config { Line 4397  sub get_scantron_config {
  $config{'Qlength'}=$config[8];   $config{'Qlength'}=$config[8];
  $config{'Qoff'}=$config[9];   $config{'Qoff'}=$config[9];
  $config{'Qon'}=$config[10];   $config{'Qon'}=$config[10];
    $config{'PaperID'}=$config[11];
    $config{'PaperIDlength'}=$config[12];
    $config{'FirstName'}=$config[13];
    $config{'FirstNamelength'}=$config[14];
    $config{'LastName'}=$config[15];
    $config{'LastNamelength'}=$config[16];
  last;   last;
     }      }
     return %config;      return %config;
Line 3171  sub username_to_idmap { Line 4418  sub username_to_idmap {
     return %idmap;      return %idmap;
 }  }
   
   sub scantron_fixup_scanline {
       my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_;
       if ($field eq 'ID') {
    if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) {
       return ($line,1,'New value too large');
    }
    if (length($args->{'newid'}) < $$scantron_config{'IDlength'}) {
       $args->{'newid'}=sprintf('%-'.$$scantron_config{'IDlength'}.'s',
        $args->{'newid'});
    }
    substr($line,$$scantron_config{'IDstart'}-1,
          $$scantron_config{'IDlength'})=$args->{'newid'};
    if ($args->{'newid'}=~/^\s*$/) {
       &scan_data($scan_data,"$whichline.user",
          $args->{'username'}.':'.$args->{'domain'});
    }
       } elsif ($field eq 'CODE') {
    if ($args->{'CODE_ignore_dup'}) {
       &scan_data($scan_data,"$whichline.CODE_ignore_dup",'1');
    }
    &scan_data($scan_data,"$whichline.useCODE",'1');
    if ($args->{'CODE'} ne 'use_unfound') {
       if (length($args->{'CODE'}) > $$scantron_config{'CODElength'}) {
    return ($line,1,'New CODE value too large');
       }
       if (length($args->{'CODE'}) < $$scantron_config{'CODElength'}) {
    $args->{'CODE'}=sprintf('%-'.$$scantron_config{'CODElength'}.'s',$args->{'CODE'});
       }
       substr($line,$$scantron_config{'CODEstart'}-1,
      $$scantron_config{'CODElength'})=$args->{'CODE'};
    }
       } elsif ($field eq 'answer') {
    my $length=$scantron_config->{'Qlength'};
    my $off=$scantron_config->{'Qoff'};
    my $on=$scantron_config->{'Qon'};
    my $answer=${off}x$length;
    if ($args->{'response'} eq 'none') {
       &scan_data($scan_data,
          "$whichline.no_bubble.".$args->{'question'},'1');
    } else {
       if ($on eq 'letter') {
    my @alphabet=('A'..'Z');
    $answer=$alphabet[$args->{'response'}];
       } elsif ($on eq 'number') {
    $answer=$args->{'response'}+1;
       } else {
    substr($answer,$args->{'response'},1)=$on;
       }
       &scan_data($scan_data,
          "$whichline.no_bubble.".$args->{'question'},undef,'1');
    }
    my $where=$length*($args->{'question'}-1)+$scantron_config->{'Qstart'};
    substr($line,$where-1,$length)=$answer;
       }
       return $line;
   }
   
   sub scan_data {
       my ($scan_data,$key,$value,$delete)=@_;
       my $filename=$env{'form.scantron_selectfile'};
       if (defined($value)) {
    $scan_data->{$filename.'_'.$key} = $value;
       }
       if ($delete) { delete($scan_data->{$filename.'_'.$key}); }
       return $scan_data->{$filename.'_'.$key};
   }
   
 sub scantron_parse_scanline {  sub scantron_parse_scanline {
     my ($line,$scantron_config)=@_;      my ($line,$whichline,$scantron_config,$scan_data,$justHeader)=@_;
     my %record;      my %record;
     my $questions=substr($line,$$scantron_config{'Qstart'}-1);      my $questions=substr($line,$$scantron_config{'Qstart'}-1);
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);      my $data=substr($line,0,$$scantron_config{'Qstart'}-1);
     if ($$scantron_config{'CODElocation'} ne 0) {      if (!($$scantron_config{'CODElocation'} eq 0 ||
  if ($$scantron_config{'CODElocation'} < 0) {    $$scantron_config{'CODElocation'} eq 'none')) {
     $record{'scantron.CODE'}=substr($data,$$scantron_config{'CODEstart'}-1,   if ($$scantron_config{'CODElocation'} < 0 ||
       $$scantron_config{'CODElocation'} eq 'letter' ||
       $$scantron_config{'CODElocation'} eq 'number') {
       $record{'scantron.CODE'}=substr($data,
       $$scantron_config{'CODEstart'}-1,
     $$scantron_config{'CODElength'});      $$scantron_config{'CODElength'});
       if (&scan_data($scan_data,"$whichline.useCODE")) {
    $record{'scantron.useCODE'}=1;
       }
       if (&scan_data($scan_data,"$whichline.CODE_ignore_dup")) {
    $record{'scantron.CODE_ignore_dup'}=1;
       }
  } else {   } else {
     #FIXME interpret first N questions      #FIXME interpret first N questions
  }   }
     }      }
     $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,      $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,
   $$scantron_config{'IDlength'});    $$scantron_config{'IDlength'});
       $record{'scantron.PaperID'}=
    substr($data,$$scantron_config{'PaperID'}-1,
          $$scantron_config{'PaperIDlength'});
       $record{'scantron.FirstName'}=
    substr($data,$$scantron_config{'FirstName'}-1,
          $$scantron_config{'FirstNamelength'});
       $record{'scantron.LastName'}=
    substr($data,$$scantron_config{'LastName'}-1,
          $$scantron_config{'LastNamelength'});
       if ($justHeader) { return \%record; }
   
     my @alphabet=('A'..'Z');      my @alphabet=('A'..'Z');
     my $questnum=0;      my $questnum=0;
     while ($questions) {      while ($questions) {
Line 3193  sub scantron_parse_scanline { Line 4528  sub scantron_parse_scanline {
  my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});   my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});
  substr($questions,0,$$scantron_config{'Qlength'})='';   substr($questions,0,$$scantron_config{'Qlength'})='';
  if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }   if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
  my (@array)=split(/$$scantron_config{'Qon'}/,$currentquest);   if ($$scantron_config{'Qon'} eq 'letter') {
  if (scalar(@array) gt 2) {      if ($currentquest eq '?') {
     #FIXME do something intelligent with double bubbles   push(@{$record{'scantron.doubleerror'}},$questnum);
     Apache->request->print("<br ><b>Wha!!!</b> <pre>".scalar(@array).   $record{"scantron.$questnum.answer"}='';
    '-'.$currentquest.'-'.$questnum.'</pre><br />');      } elsif (!$currentquest 
  }       || $currentquest eq $$scantron_config{'Qoff'}
  if (length($array[0]) eq $$scantron_config{'Qlength'}) {       || $currentquest !~ /^[A-Z]$/) {
     $record{"scantron.$questnum.answer"}='';   $record{"scantron.$questnum.answer"}='';
    if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
       push(@{$record{"scantron.missingerror"}},$questnum);
    }
       } else {
    $record{"scantron.$questnum.answer"}=$currentquest;
       }
    } elsif ($$scantron_config{'Qon'} eq 'number') {
       if ($currentquest eq '?') {
    push(@{$record{'scantron.doubleerror'}},$questnum);
    $record{"scantron.$questnum.answer"}='';
    } elsif (!$currentquest 
    || $currentquest eq $$scantron_config{'Qoff'} 
    || $currentquest !~ /^\d$/) {
    $record{"scantron.$questnum.answer"}='';
    if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
       push(@{$record{"scantron.missingerror"}},$questnum);
    }
       } else {
    $record{"scantron.$questnum.answer"}=
       $alphabet[$currentquest-1];
       }
  } else {   } else {
     $record{"scantron.$questnum.answer"}=$alphabet[length($array[0])];      my @array=split($$scantron_config{'Qon'},$currentquest,-1);
       if (length($array[0]) eq $$scantron_config{'Qlength'}) {
    $record{"scantron.$questnum.answer"}='';
    if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
       push(@{$record{"scantron.missingerror"}},$questnum);
    }
       } else {
    $record{"scantron.$questnum.answer"}=
       $alphabet[length($array[0])];
       }
       if (scalar(@array) gt 2) {
    push(@{$record{'scantron.doubleerror'}},$questnum);
    my @ans=@array;
    my $i=length($ans[0]);shift(@ans);
    while ($#ans) {
       $i+=length($ans[0])+1;
       $record{"scantron.$questnum.answer"}.=$alphabet[$i];
       shift(@ans);
    }
       }
  }   }
     }      }
     $record{'scantron.maxquest'}=$questnum;      $record{'scantron.maxquest'}=$questnum;
Line 3210  sub scantron_parse_scanline { Line 4585  sub scantron_parse_scanline {
 }  }
   
 sub scantron_add_delay {  sub scantron_add_delay {
       my ($delayqueue,$scanline,$errormessage,$errorcode)=@_;
       push(@$delayqueue,
    {'line' => $scanline, 'emsg' => $errormessage,
     'ecode' => $errorcode }
    );
 }  }
   
 sub scantron_find_student {  sub scantron_find_student {
     my ($scantron_record,$idmap)=@_;      my ($scantron_record,$scan_data,$idmap,$line)=@_;
     my $scanID=$$scantron_record{'scantron.ID'};      my $scanID=$$scantron_record{'scantron.ID'};
       if ($scanID =~ /^\s*$/) {
     return &scan_data($scan_data,"$line.user");
       }
     foreach my $id (keys(%$idmap)) {      foreach my $id (keys(%$idmap)) {
  Apache->request->print('<pre>checking studnet -'.$id.'- againt -'.$scanID.'- </pre>');    if (lc($id) eq lc($scanID)) {
  if (lc($id) eq lc($scanID)) { Apache->request->print('success');return $$idmap{$id}; }       return $$idmap{$id};
     }
     }      }
     return undef;      return undef;
 }  }
   
 sub scantron_filter {  sub scantron_filter {
     my ($curres)=@_;      my ($curres)=@_;
     if (ref($curres) && $curres->is_problem() && !$curres->randomout) {  
       if (ref($curres) && $curres->is_problem()) {
    # if the user has asked to not have either hidden
    # or 'randomout' controlled resources to be graded
    # don't include them
    if ($env{'form.scantron_options_hidden'} eq 'ignore_hidden'
       && $curres->randomout) {
       return 0;
    }
    return 1;
       }
       return 0;
   }
   
   sub scantron_process_corrections {
       my ($r) = @_;
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&scantron_getfile();
       my $classlist=&Apache::loncoursedata::get_classlist();
       my $which=$env{'form.scantron_line'};
       my $line=&scantron_get_line($scanlines,$scan_data,$which);
       my ($skip,$err,$errmsg);
       if ($env{'form.scantron_skip_record'}) {
    $skip=1;
       } elsif ($env{'form.scantron_corrections'} =~ /^(duplicate|incorrect)ID$/) {
    my $newstudent=$env{'form.scantron_username'}.':'.
       $env{'form.scantron_domain'};
    my $newid=$classlist->{$newstudent}->[&Apache::loncoursedata::CL_ID];
    ($line,$err,$errmsg)=
       &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
        'ID',{'newid'=>$newid,
       'username'=>$env{'form.scantron_username'},
       'domain'=>$env{'form.scantron_domain'}});
       } elsif ($env{'form.scantron_corrections'} =~ /^(duplicate|incorrect)CODE$/) {
    my $resolution=$env{'form.scantron_CODE_resolution'};
    my $newCODE;
    my %args;
    if      ($resolution eq 'use_unfound') {
       $newCODE='use_unfound';
    } elsif ($resolution eq 'use_found') {
       $newCODE=$env{'form.scantron_CODE_selectedvalue'};
    } elsif ($resolution eq 'use_typed') {
       $newCODE=$env{'form.scantron_CODE_newvalue'};
    } elsif ($resolution =~ /^use_closest_(\d+)/) {
       $newCODE=$env{"form.scantron_CODE_closest_$1"};
    }
    if ($env{'form.scantron_corrections'} eq 'duplicateCODE') {
       $args{'CODE_ignore_dup'}=1;
    }
    $args{'CODE'}=$newCODE;
    ($line,$err,$errmsg)=
       &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
        'CODE',\%args);
       } elsif ($env{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) {
    foreach my $question (split(',',$env{'form.scantron_questions'})) {
       ($line,$err,$errmsg)=
    &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,
    $which,'answer',
    { 'question'=>$question,
          'response'=>$env{"form.scantron_correct_Q_$question"}});
       if ($err) { last; }
    }
       }
       if ($err) {
    $r->print("<font color='red'>Unable to accept last correction, an error occurred :$errmsg:</font>");
       } else {
    &scantron_put_line($scanlines,$scan_data,$which,$line,$skip);
    &scantron_putfile($scanlines,$scan_data);
       }
   }
   
   sub reset_skipping_status {
       my ($scanlines,$scan_data)=&scantron_getfile();
       &scan_data($scan_data,'remember_skipping',undef,1);
       &scantron_putfile(undef,$scan_data);
   }
   
   sub allow_skipping {
       my ($scan_data,$i)=@_;
       my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
       delete($remembered{$i});
       &scan_data($scan_data,'remember_skipping',join(':',%remembered));
   }
   
   sub should_be_skipped {
       my ($scan_data,$i)=@_;
       if ($env{'form.scantron_options_redo'} !~ /^redo_/) {
    # not redoing old skips
    return 0;
       }
       my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
       if (exists($remembered{$i})) { return 0; }
       return 1;
   }
   
   sub remember_current_skipped {
       my ($scanlines,$scan_data)=&scantron_getfile();
       my %to_remember;
       for (my $i=0;$i<=$scanlines->{'count'};$i++) {
    if ($scanlines->{'skipped'}[$i]) {
       $to_remember{$i}=1;
    }
       }
       &scan_data($scan_data,'remember_skipping',join(':',%to_remember));
       &scantron_putfile(undef,$scan_data);
   }
   
   sub check_for_error {
       my ($r,$result)=@_;
       if ($result ne 'ok' && $result ne 'not_found' ) {
    $r->print("An error occured ($result) when trying to Remove the existing corrections.");
       }
   }
   
   sub scantron_warning_screen {
       my ($button_text)=@_;
       my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my $CODElist="a";
       if ($scantron_config{'CODElocation'} &&
    $scantron_config{'CODEstart'} &&
    $scantron_config{'CODElength'}) {
    $CODElist=$env{'form.scantron_CODElist'};
    if ($CODElist eq '') { $CODElist='<font color="red">None</font>'; }
    $CODElist=
       '<tr><td><b>List of CODES to validate against:</b></td><td><tt>'.
       $CODElist.'</tt></td></tr>';
       }
       return (<<STUFF);
   <p>
   <font color="red">Please double check the information
                    below before clicking on '$button_text'</font>
   </p>
   <table>
   <tr><td><b>Sequence to be Graded:</b></td><td>$title</td></tr>
   <tr><td><b>Data File that will be used:</b></td><td><tt>$env{'form.scantron_selectfile'}</tt></td></tr>
   $CODElist
   </table>
   </font>
   <br />
   <p> If this information is correct, please click on '$button_text'.</p>
   <p> If something is incorrect, please click the 'Grading Menu' button to start over.</p>
   
   <br />
   STUFF
   }
   
   sub scantron_do_warning {
       my ($r)=@_;
       my ($symb)=&get_symb($r);
       if (!$symb) {return '';}
       my $default_form_data=&defaultFormData($symb);
       $r->print(&scantron_form_start().$default_form_data);
       if ( $env{'form.selectpage'} eq '' ||
    $env{'form.scantron_selectfile'} eq '' ||
    $env{'form.scantron_format'} eq '' ) {
    $r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>");
    if ( $env{'form.selectpage'} eq '') {
       $r->print('<p><font color="red">You have not selected a Sequence to grade</font></p>');
    } 
    if ( $env{'form.scantron_selectfile'} eq '') {
       $r->print('<p><font color="red">You have not selected a file that contains the student\'s response data.</font></p>');
    } 
    if ( $env{'form.scantron_format'} eq '') {
       $r->print('<p><font color="red">You have not selected a the format of the student\'s response data.</font></p>');
    } 
       } else {
    my $warning=&scantron_warning_screen('Grading: Validate Records');
    $r->print(<<STUFF);
   $warning
   <input type="submit" name="submit" value="Grading: Validate Records" />
   <input type="hidden" name="command" value="scantron_validate" />
   STUFF
       }
       $r->print("</form><br />".&show_grading_menu_form($symb)."</body></html>");
       return '';
   }
   
   sub scantron_form_start {
       my ($max_bubble)=@_;
       my $result= <<SCANTRONFORM;
   <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
     <input type="hidden" name="selectpage" value="$env{'form.selectpage'}" />
     <input type="hidden" name="scantron_format" value="$env{'form.scantron_format'}" />
     <input type="hidden" name="scantron_selectfile" value="$env{'form.scantron_selectfile'}" />
     <input type="hidden" name="scantron_maxbubble" value="$max_bubble" />
     <input type="hidden" name="scantron_CODElist" value="$env{'form.scantron_CODElist'}" />
     <input type="hidden" name="scantron_CODEunique" value="$env{'form.scantron_CODEunique'}" />
     <input type="hidden" name="scantron_options_redo" value="$env{'form.scantron_options_redo'}" />
     <input type="hidden" name="scantron_options_ignore" value="$env{'form.scantron_options_ignore'}" />
     <input type="hidden" name="scantron_options_hidden" value="$env{'form.scantron_options_hidden'}" />
   SCANTRONFORM
       return $result;
   }
   
   sub scantron_validate_file {
       my ($r) = @_;
       my ($symb)=&get_symb($r);
       if (!$symb) {return '';}
       my $default_form_data=&defaultFormData($symb);
       
       # do the detection of only doing skipped records first befroe we delete
       # them  when doing the corrections reset
       if ($env{'form.scantron_options_redo'} ne 'redo_skipped_ready') {
    &reset_skipping_status();
       }
       if ($env{'form.scantron_options_redo'} eq 'redo_skipped') {
    &remember_current_skipped();
    &scantron_remove_file('skipped');
    $env{'form.scantron_options_redo'}='redo_skipped_ready';
       }
   
       if ($env{'form.scantron_options_ignore'} eq 'ignore_corrections') {
    &check_for_error($r,&scantron_remove_file('corrected'));
    &check_for_error($r,&scantron_remove_file('skipped'));
    &check_for_error($r,&scantron_remove_scan_data());
    $env{'form.scantron_options_ignore'}='done';
       }
   
       if ($env{'form.scantron_corrections'}) {
    &scantron_process_corrections($r);
       }
       $r->print("<p>Gathering neccessary info.</p>");$r->rflush();
       #get the student pick code ready
       $r->print(&Apache::loncommon::studentbrowser_javascript());
       my $max_bubble=&scantron_get_maxbubble();
       my $result=&scantron_form_start($max_bubble).$default_form_data;
       $r->print($result);
       
       my @validate_phases=( 'sequence',
     'ID',
     'CODE',
     'doublebubble',
     'missingbubbles');
       if (!$env{'form.validatepass'}) {
    $env{'form.validatepass'} = 0;
       }
       my $currentphase=$env{'form.validatepass'};
   
       my $stop=0;
       while (!$stop && $currentphase < scalar(@validate_phases)) {
    $r->print("<p> Validating ".$validate_phases[$currentphase]."</p>");
    $r->rflush();
    my $which="scantron_validate_".$validate_phases[$currentphase];
    {
       no strict 'refs';
       ($stop,$currentphase)=&$which($r,$currentphase);
    }
       }
       if (!$stop) {
    my $warning=&scantron_warning_screen('Start Grading');
    $r->print(<<STUFF);
   Validation process complete.<br />
   $warning
   <input type="submit" name="submit" value="Start Grading" />
   <input type="hidden" name="command" value="scantron_process" />
   STUFF
   
       } else {
    $r->print('<input type="hidden" name="command" value="scantron_validate" />');
    $r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />");
       }
       if ($stop) {
    if ($validate_phases[$currentphase] eq 'sequence') {
       $r->print('<input type="submit" name="submit" value="Ignore -> " />');
       $r->print(' this error <br />');
   
       $r->print(" <p>Or click the 'Grading Menu' button to start over.</p>");
    } else {
       $r->print('<input type="submit" name="submit" value="Continue ->" />');
       $r->print(' using corrected info <br />');
       $r->print("<input type='submit' value='Skip' name='scantron_skip_record' />");
       $r->print(" this scanline saving it for later.");
    }
       }
       $r->print(" </form><br />".&show_grading_menu_form($symb).
         "</body></html>");
       return '';
   }
   
   sub scantron_remove_file {
       my ($which)=@_;
       my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my $file='scantron_';
       if ($which eq 'corrected' || $which eq 'skipped') {
    $file.=$which.'_';
       } else {
    return 'refused';
       }
       $file.=$env{'form.scantron_selectfile'};
       return &Apache::lonnet::removeuserfile($cname,$cdom,$file);
   }
   
   sub scantron_remove_scan_data {
       my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my @keys=&Apache::lonnet::getkeys('nohist_scantrondata',$cdom,$cname);
       my @todelete;
       my $filename=$env{'form.scantron_selectfile'};
       foreach my $key (@keys) {
    if ($key=~/^\Q$filename\E_/) {
       if ($env{'form.scantron_options_redo'} eq 'redo_skipped_ready' &&
    $key=~/remember_skipping/) {
    next;
       }
       push(@todelete,$key);
    }
       }
       my $result;
       if (@todelete) {
    $result=&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname);
       }
       return $result;
   }
   
   sub scantron_getfile {
       #FIXME really would prefer a scantron directory
       my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my $lines;
       $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
          'scantron_orig_'.$env{'form.scantron_selectfile'});
       my %scanlines;
       $scanlines{'orig'}=[(split("\n",$lines,-1))];
       my $temp=$scanlines{'orig'};
       $scanlines{'count'}=$#$temp;
   
       $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
          'scantron_corrected_'.$env{'form.scantron_selectfile'});
       if ($lines eq '-1') {
    $scanlines{'corrected'}=[];
       } else {
    $scanlines{'corrected'}=[(split("\n",$lines,-1))];
       }
       $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
          'scantron_skipped_'.$env{'form.scantron_selectfile'});
       if ($lines eq '-1') {
    $scanlines{'skipped'}=[];
       } else {
    $scanlines{'skipped'}=[(split("\n",$lines,-1))];
       }
       my @tmp=&Apache::lonnet::dump('nohist_scantrondata',$cdom,$cname);
       if ($tmp[0] =~ /^(error:|no_such_host)/) { @tmp=(); }
       my %scan_data = @tmp;
       return (\%scanlines,\%scan_data);
   }
   
   sub lonnet_putfile {
       my ($contents,$filename)=@_;
       my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       $env{'form.sillywaytopassafilearound'}=$contents;
       &Apache::lonnet::finishuserfileupload($docuname,$docudom,'sillywaytopassafilearound',$filename);
   
   }
   
   sub scantron_putfile {
       my ($scanlines,$scan_data) = @_;
       #FIXME really would prefer a scantron directory
       my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       if ($scanlines) {
    my $prefix='scantron_';
   # no need to update orig, shouldn't change
   #   &lonnet_putfile(join("\n",@{$scanlines->{'orig'}}),$prefix.'orig_'.
   #    $env{'form.scantron_selectfile'});
    &lonnet_putfile(join("\n",@{$scanlines->{'corrected'}}),
    $prefix.'corrected_'.
    $env{'form.scantron_selectfile'});
    &lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}),
    $prefix.'skipped_'.
    $env{'form.scantron_selectfile'});
       }
       &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname);
   }
   
   sub scantron_get_line {
       my ($scanlines,$scan_data,$i)=@_;
       if (&should_be_skipped($scan_data,$i)) { return undef; }
       if ($scanlines->{'skipped'}[$i]) { return undef; }
       if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];}
       return $scanlines->{'orig'}[$i]; 
   }
   
   sub get_todo_count {
       my ($scanlines,$scan_data)=@_;
       my $count=0;
       for (my $i=0;$i<=$scanlines->{'count'};$i++) {
    my $line=&scantron_get_line($scanlines,$scan_data,$i);
    if ($line=~/^[\s\cz]*$/) { next; }
    $count++;
       }
       return $count;
   }
   
   sub scantron_put_line {
       my ($scanlines,$scan_data,$i,$newline,$skip)=@_;
       if ($skip) {
    $scanlines->{'skipped'}[$i]=$newline;
    &allow_skipping($scan_data,$i);
    return;
       }
       $scanlines->{'corrected'}[$i]=$newline;
   }
   
   sub scantron_filter_not_exam {
       my ($curres)=@_;
       
       if (ref($curres) && $curres->is_problem() && !$curres->is_exam()) {
    # if the user has asked to not have either hidden
    # or 'randomout' controlled resources to be graded
    # don't include them
    if ($env{'form.scantron_options_hidden'} eq 'ignore_hidden'
       && $curres->randomout) {
       return 0;
    }
  return 1;   return 1;
     }      }
     return 0;      return 0;
 }  }
   
   sub scantron_validate_sequence {
       my ($r,$currentphase) = @_;
   
       my $navmap=Apache::lonnavmaps::navmap->new();
       my (undef,undef,$sequence)=
    &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
       my $map=$navmap->getResourceByUrl($sequence);
   
       $r->print('<input type="hidden" name="validate_sequence_exam"
                                       value="ignore" />');
       if ($env{'form.validate_sequence_exam'} ne 'ignore') {
    my @resources=
       $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0);
    if (@resources) {
       $r->print("<p>".&mt('Some resource in the sequece currently are not set to exam mode. Grading these resources currently may not work correctly.')."</p>");
       return (1,$currentphase);
    }
       }
   
       return (0,$currentphase+1);
   }
   
   sub scantron_validate_ID {
       my ($r,$currentphase) = @_;
       
       #get student info
       my $classlist=&Apache::loncoursedata::get_classlist();
       my %idmap=&username_to_idmap($classlist);
   
       #get scantron line setup
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&scantron_getfile();
   
       my %found=('ids'=>{},'usernames'=>{});
       for (my $i=0;$i<=$scanlines->{'count'};$i++) {
    my $line=&scantron_get_line($scanlines,$scan_data,$i);
    if ($line=~/^[\s\cz]*$/) { next; }
    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
    $scan_data);
    my $id=$$scan_record{'scantron.ID'};
    my $found;
    foreach my $checkid (keys(%idmap)) {
       if (lc($checkid) eq lc($id)) { $found=$checkid;last; }
    }
    if ($found) {
       my $username=$idmap{$found};
       if ($found{'ids'}{$found}) {
    &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
    $line,'duplicateID',$found);
    return(1,$currentphase);
       } elsif ($found{'usernames'}{$username}) {
    &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
    $line,'duplicateID',$username);
    return(1,$currentphase);
       }
       #FIXME store away line we previously saw the ID on to use above
       $found{'ids'}{$found}++;
       $found{'usernames'}{$username}++;
    } else {
       if ($id =~ /^\s*$/) {
    my $username=&scan_data($scan_data,"$i.user");
    if (defined($username) && $found{'usernames'}{$username}) {
       &scantron_get_correction($r,$i,$scan_record,
        \%scantron_config,
        $line,'duplicateID',$username);
       return(1,$currentphase);
    } elsif (!defined($username)) {
       &scantron_get_correction($r,$i,$scan_record,
        \%scantron_config,
        $line,'incorrectID');
       return(1,$currentphase);
    }
    $found{'usernames'}{$username}++;
       } else {
    &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
    $line,'incorrectID');
    return(1,$currentphase);
       }
    }
       }
   
       return (0,$currentphase+1);
   }
   
   sub scantron_get_correction {
       my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;
   
   #FIXME in the case of a duplicated ID the previous line, probaly need
   #to show both the current line and the previous one and allow skipping
   #the previous one or the current one
   
       $r->print("<p><b>An error was detected ($error)</b>");
       if ( $$scan_record{'scantron.PaperID'} =~ /\S/) {
    $r->print(" for PaperID <tt>".
     $$scan_record{'scantron.PaperID'}."</tt> \n");
       } else {
    $r->print(" in scanline $i <pre>".
     $line."</pre> \n");
       }
       my $message="<p>The ID on the form is  <tt>".
    $$scan_record{'scantron.ID'}."</tt><br />\n".
    "The name on the paper is ".
    $$scan_record{'scantron.LastName'}.",".
    $$scan_record{'scantron.FirstName'}."</p>";
   
       $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n");
       $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n");
       if ($error =~ /ID$/) {
    if ($error eq 'incorrectID') {
       $r->print("The encoded ID is not in the classlist</p>\n");
    } elsif ($error eq 'duplicateID') {
       $r->print("The encoded ID has also been used by a previous paper $arg</p>\n");
    }
    $r->print($message);
    $r->print("<p>How should I handle this? <br /> \n");
    $r->print("\n<ul><li> ");
    #FIXME it would be nice if this sent back the user ID and
    #could do partial userID matches
    $r->print(&Apache::loncommon::selectstudent_link('scantronupload',
          'scantron_username','scantron_domain'));
    $r->print(": <input type='text' name='scantron_username' value='' />");
    $r->print("\n@".
    &Apache::loncommon::select_dom_form($env{'request.role.domain'},'scantron_domain'));
   
    $r->print('</li>');
       } elsif ($error =~ /CODE$/) {
    if ($error eq 'incorrectCODE') {
       $r->print("</p><p>The encoded CODE is not in the list of possible CODEs</p>\n");
    } elsif ($error eq 'duplicateCODE') {
       $r->print("</p><p>The encoded CODE has also been used by a previous paper ".join(', ',@{$arg}).", and CODEs are supposed to be unique</p>\n");
    }
    $r->print("<p>The CODE on the form is  <tt>'".
     $$scan_record{'scantron.CODE'}."'</tt><br />\n");
    $r->print($message);
    $r->print("<p>How should I handle this? <br /> \n");
    $r->print("\n<br /> ");
    my $i=0;
    if ($error eq 'incorrectCODE' 
       && $$scan_record{'scantron.CODE'}=~/\S/ ) {
       my ($max,$closest)=&scantron_get_closely_matching_CODEs($arg,$$scan_record{'scantron.CODE'});
       if ($closest > 0) {
    foreach my $testcode (@{$closest}) {
       my $checked='';
       if (!$i) { $checked=' checked="on" '; }
       $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 />");
       $i++;
    }
       }
    }
    if ($$scan_record{'scantron.CODE'}=~/\S/ ) {
       my $checked; if (!$i) { $checked=' checked="on" '; }
       $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(<<ENDSCRIPT);
   <script type="text/javascript">
   function change_radio(field) {
       var slct=document.scantronupload.scantron_CODE_resolution;
       var i;
       for (i=0;i<slct.length;i++) {
           if (slct[i].value==field) { slct[i].checked=true; }
       }
   }
   </script>
   ENDSCRIPT
    my $href="/adm/pickcode?".
      "form=".&Apache::lonnet::escape("scantronupload").
      "&scantron_format=".&Apache::lonnet::escape($env{'form.scantron_format'}).
      "&scantron_CODElist=".&Apache::lonnet::escape($env{'form.scantron_CODElist'}).
      "&curCODE=".&Apache::lonnet::escape($$scan_record{'scantron.CODE'}).
      "&scantron_selectfile=".&Apache::lonnet::escape($env{'form.scantron_selectfile'});
    if ($env{'form.scantron_CODElist'} =~ /\S/) { 
       $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it.</label> Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />");
       $r->print("\n<br />");
    }
    $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_typed' /> Use </label><input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" /> as the CODE.");
    $r->print("\n<br /><br />");
       } elsif ($error eq 'doublebubble') {
    $r->print("<p>There have been multiple bubbles scanned for a some question(s)</p>\n");
    $r->print('<input type="hidden" name="scantron_questions" value="'.
     join(',',@{$arg}).'" />');
    $r->print($message);
    $r->print("<p>Please indicate which bubble should be used for grading</p>");
    foreach my $question (@{$arg}) {
       my $selected=$$scan_record{"scantron.$question.answer"};
       &scantron_bubble_selector($r,$scan_config,$question,split('',$selected));
    }
       } elsif ($error eq 'missingbubble') {
    $r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n");
    $r->print($message);
    $r->print("<p>Please indicate which bubble should be used for grading</p>");
    $r->print("Some questions have no scanned bubbles\n");
    $r->print('<input type="hidden" name="scantron_questions" value="'.
     join(',',@{$arg}).'" />');
    foreach my $question (@{$arg}) {
       my $selected=$$scan_record{"scantron.$question.answer"};
       &scantron_bubble_selector($r,$scan_config,$question);
    }
       } else {
    $r->print("\n<ul>");
       }
       $r->print("\n</li></ul>");
   
   }
   
   sub scantron_bubble_selector {
       my ($r,$scan_config,$quest,@selected)=@_;
       my $max=$$scan_config{'Qlength'};
   
       my $scmode=$$scan_config{'Qon'};
       if ($scmode eq 'number' || $scmode eq 'letter') { $max=10; }     
   
       my @alphabet=('A'..'Z');
       $r->print("<table border='1'><tr><td rowspan='2'>$quest</td>");
       for (my $i=0;$i<$max+1;$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>');
       }
       $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_'.
         $quest.'" value="none" /> No bubble </label></td>');
       $r->print('</tr></table>');
   }
   
   sub num_matches {
       my ($orig,$code) = @_;
       my @code=split(//,$code);
       my @orig=split(//,$orig);
       my $same=0;
       for (my $i=0;$i<scalar(@code);$i++) {
    if ($code[$i] eq $orig[$i]) { $same++; }
       }
       return $same;
   }
   
   sub scantron_get_closely_matching_CODEs {
       my ($allcodes,$CODE)=@_;
       my @CODEs;
       foreach my $testcode (sort(keys(%{$allcodes}))) {
    push(@{$CODEs[&num_matches($CODE,$testcode)]},$testcode);
       }
   
       return ($#CODEs,$CODEs[-1]);
   }
   
   sub get_codes {
       my ($old_name, $cdom, $cnum) = @_;
       if (!$old_name) {
    $old_name=$env{'form.scantron_CODElist'};
       }
       if (!$cdom) {
    $cdom =$env{'course.'.$env{'request.course.id'}.'.domain'};
       }
       if (!$cnum) {
    $cnum =$env{'course.'.$env{'request.course.id'}.'.num'};
       }
       my %result=&Apache::lonnet::get('CODEs',[$old_name,"type\0$old_name"],
       $cdom,$cnum);
       my %allcodes;
       if ($result{"type\0$old_name"} eq 'number') {
    %allcodes=map {($_,1)} split(',',$result{$old_name});
       } else {
    %allcodes=map {(&Apache::lonprintout::num_to_letters($_),1)} split(',',$result{$old_name});
       }
       return %allcodes;
   }
   
   sub scantron_validate_CODE {
       my ($r,$currentphase) = @_;
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       if ($scantron_config{'CODElocation'} &&
    $scantron_config{'CODEstart'} &&
    $scantron_config{'CODElength'}) {
    if (!defined($env{'form.scantron_CODElist'})) {
       &FIXME_blow_up()
    }
       } else {
    return (0,$currentphase+1);
       }
       
       my %usedCODEs;
   
       my %allcodes=&get_codes();
   
       my ($scanlines,$scan_data)=&scantron_getfile();
       for (my $i=0;$i<=$scanlines->{'count'};$i++) {
    my $line=&scantron_get_line($scanlines,$scan_data,$i);
    if ($line=~/^[\s\cz]*$/) { next; }
    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
    $scan_data);
    my $CODE=$$scan_record{'scantron.CODE'};
    my $error=0;
    if (!&Apache::lonnet::validCODE($CODE)) {
       &scantron_get_correction($r,$i,$scan_record,
        \%scantron_config,
        $line,'incorrectCODE',\%allcodes);
       return(1,$currentphase);
    }
    if (%allcodes && !exists($allcodes{$CODE}) 
       && !$$scan_record{'scantron.useCODE'}) {
       &scantron_get_correction($r,$i,$scan_record,
        \%scantron_config,
        $line,'incorrectCODE',\%allcodes);
       return(1,$currentphase);
    }
    if (exists($usedCODEs{$CODE}) 
       && $env{'form.scantron_CODEunique'} eq 'yes'
       && !$$scan_record{'scantron.CODE_ignore_dup'}) {
       &scantron_get_correction($r,$i,$scan_record,
        \%scantron_config,
        $line,'duplicateCODE',$usedCODEs{$CODE});
       return(1,$currentphase);
    }
    push (@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'});
       }
       return (0,$currentphase+1);
   }
   
   sub scantron_validate_doublebubble {
       my ($r,$currentphase) = @_;
       #get student info
       my $classlist=&Apache::loncoursedata::get_classlist();
       my %idmap=&username_to_idmap($classlist);
   
       #get scantron line setup
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&scantron_getfile();
       for (my $i=0;$i<=$scanlines->{'count'};$i++) {
    my $line=&scantron_get_line($scanlines,$scan_data,$i);
    if ($line=~/^[\s\cz]*$/) { next; }
    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
    $scan_data);
    if (!defined($$scan_record{'scantron.doubleerror'})) { next; }
    &scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line,
    'doublebubble',
    $$scan_record{'scantron.doubleerror'});
       return (1,$currentphase);
       }
       return (0,$currentphase+1);
   }
   
   sub scantron_get_maxbubble {    
       if (defined($env{'form.scantron_maxbubble'}) &&
    $env{'form.scantron_maxbubble'}) {
    return $env{'form.scantron_maxbubble'};
       }
   
       my $navmap=Apache::lonnavmaps::navmap->new();
       my (undef,undef,$sequence)=
    &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
   
       &Apache::lonxml::clear_problem_counter();
   
       foreach my $resource (@resources) {
    my $result=&Apache::lonnet::ssi($resource->src(),
    ('symb' => $resource->symb()));
       }
       &Apache::lonnet::delenv('scantron\.');
       $env{'form.scantron_maxbubble'} =
    &Apache::lonxml::get_problem_counter()-1;
   
       return $env{'form.scantron_maxbubble'};
   }
   
   sub scantron_validate_missingbubbles {
       my ($r,$currentphase) = @_;
       #get student info
       my $classlist=&Apache::loncoursedata::get_classlist();
       my %idmap=&username_to_idmap($classlist);
   
       #get scantron line setup
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&scantron_getfile();
       my $max_bubble=&scantron_get_maxbubble();
       if (!$max_bubble) { $max_bubble=2**31; }
       for (my $i=0;$i<=$scanlines->{'count'};$i++) {
    my $line=&scantron_get_line($scanlines,$scan_data,$i);
    if ($line=~/^[\s\cz]*$/) { next; }
    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
    $scan_data);
    if (!defined($$scan_record{'scantron.missingerror'})) { next; }
    my @to_correct;
    foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {
       if ($missing > $max_bubble) { next; }
       push(@to_correct,$missing);
    }
    if (@to_correct) {
       &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
        $line,'missingbubble',\@to_correct);
       return (1,$currentphase);
    }
   
       }
       return (0,$currentphase+1);
   }
   
 sub scantron_process_students {  sub scantron_process_students {
     my ($r) = @_;      my ($r) = @_;
     my (undef,undef,$sequence)=split(/___/,$ENV{'form.selectpage'});      my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
     my ($symb,$url)=&get_symb_and_url($r);      my ($symb)=&get_symb($r);
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $default_form_data=&defaultFormData($symb,$url);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});      my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my $scanlines=Apache::File->new($Apache::lonnet::perlvar{'lonScansDir'}."/$ENV{'form.scantron_selectfile'}");      my ($scanlines,$scan_data)=&scantron_getfile();
     my @scanlines=<$scanlines>;  
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new($ENV{'request.course.fn'}.'.db',$ENV{'request.course.fn'}.'_parms.db',1, 1);      my $navmap=Apache::lonnavmaps::navmap->new();
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     $r->print("geto ".scalar(@resources)."<br />");  #    $r->print("geto ".scalar(@resources)."<br />");
     my $result= <<SCANTRONFORM;      my $result= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">  <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
   <input type="hidden" name="command" value="scantron_configphase" />    <input type="hidden" name="command" value="scantron_configphase" />
Line 3254  SCANTRONFORM Line 5462  SCANTRONFORM
     $r->print($result);      $r->print($result);
   
     my @delayqueue;      my @delayqueue;
     my $totalcorrect;      my %completedstudents;
     my $totalincorrect;      
       my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
            'Scantron Status','Scantron Progress',scalar(@scanlines));       'Scantron Progress',$count,
     foreach my $line (@scanlines) {      'inline',undef,'scantronupload');
  my $studentcorrect;      &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
  my $studentincorrect;    'Processing first student');
       my $start=&Time::HiRes::time();
       my $i=-1;
       my ($uname,$udom,$started);
       while ($i<$scanlines->{'count'}) {
     ($uname,$udom)=('','');
     $i++;
     my $line=&scantron_get_line($scanlines,$scan_data,$i);
     if ($line=~/^[\s\cz]*$/) { next; }
    if ($started) {
       &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
        'last student');
    }
    $started=1;
     my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
     $scan_data);
     unless ($uname=&scantron_find_student($scan_record,$scan_data,
          \%idmap,$i)) {
        &scantron_add_delay(\@delayqueue,$line,
     'Unable to find a student that matches',1);
        next;
     }
     if (exists $completedstudents{$uname}) {
        &scantron_add_delay(\@delayqueue,$line,
     'Student '.$uname.' has multiple sheets',2);
        next;
     }
     ($uname,$udom)=split(/:/,$uname);
   
  chomp($line);   &Apache::lonxml::clear_problem_counter();
  my $scan_record=&scantron_parse_scanline($line,\%scantron_config);    &Apache::lonnet::appenv(%$scan_record);
  my ($uname,$udom);  
  if ($uname=&scantron_find_student($scan_record,\%idmap)) {  
     &scantron_add_delay(\@delayqueue,$line,  
  'Unable to find a student that matches');  
  }  
  $r->print('<pre>doing studnet'.$uname.'</pre>');  
  ($uname,$udom)=split(/:/,$uname);  
  &Apache::lonnet::delenv('form.counter');  
  &Apache::lonnet::appenv(%$scan_record);  
 #    &Apache::lonhomework::showhash(%ENV);  
     $Apache::lonxml::debug=1;  
  &Apache::lonxml::debug("line is $line");  
   
     my $i=0;   my $i=0;
  foreach my $resource (@resources) {   foreach my $resource (@resources) {
     $i++;      $i++;
     my $result=&Apache::lonnet::ssi($resource->src(),      my %form=('submitted'     =>'scantron',
  ('submitted'     =>'scantron',        'grade_target'  =>'grade',
   'grade_target'  =>'grade',        'grade_username'=>$uname,
   'grade_username'=>$uname,        'grade_domain'  =>$udom,
   'grade_domain'  =>$udom,        'grade_courseid'=>$env{'request.course.id'},
   'grade_courseid'=>$ENV{'request.course.id'},        'grade_symb'    =>$resource->symb());
   'grade_symb'    =>$resource->symb()));      if (exists($scan_record->{'scantron.CODE'}) &&
     my %score=&Apache::lonnet::restore($resource->symb(),   $scan_record->{'scantron.CODE'}) {
        $ENV{'request.course.id'},   $form{'CODE'}=$scan_record->{'scantron.CODE'};
        $udom,$uname);      } else {
     foreach my $part ($resource->{PARTS}) {   $form{'CODE'}='';
  if ($score{'resource.'.$part.'.solved'} =~ /^correct/) {  
     $studentcorrect++;  
     $totalcorrect++;  
  } else {  
     $studentincorrect++;  
     $totalincorrect++;  
  }  
     }      }
     $r->print('<pre>'.      my $result=&Apache::lonnet::ssi($resource->src(),%form);
       $resource->symb().'-'.      if ($result ne '') {
       $resource->src().'-'.'</pre>result is'.$result);   &Apache::lonnet::logthis("scantron grading error -> $result");
     &Apache::lonhomework::showhash(%score);   &Apache::lonnet::logthis("scantron grading error info name $uname domain $udom course $env{'request.course.id'} url ".$resource->src());
  #    if ($i eq 3) {last;}      }
  }      if (&Apache::loncommon::connection_aborted($r)) { last; }
  &Apache::lonnet::delenv('form.counter');   }
    $completedstudents{$uname}={'line'=>$line};
    if (&Apache::loncommon::connection_aborted($r)) { last; }
       } continue {
    &Apache::lonxml::clear_problem_counter();
  &Apache::lonnet::delenv('scantron\.');   &Apache::lonnet::delenv('scantron\.');
  &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,  
              'last student Who got a '.$studentcorrect.' correct and '.  
      $studentincorrect.' incorrect. The class has gotten '.  
              $totalcorrect.' correct and '.$totalincorrect.' incorrect');  
  last;  
  #FIXME  
  #get iterator for $sequence  
  #foreach question 'submit' the students answer to the server  
  #   through grade target {  
  #   generate data to pass back that includes grade recevied  
  #}  
     }  
     $Apache::lonxml::debug=0;  
     foreach my $delay (@delayqueue) {  
  #FIXME  
  #print out each delayed student with interface to select how  
  #  to repair student provided info  
  #Expected errors include  
  #  1 bad/no stuid/username  
  #  2 invalid bubblings  
   
     }      }
       &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
   #    my $lasttime = &Time::HiRes::time()-$start;
   #    $r->print("<p>took $lasttime</p>");
   
       $r->print("</form>");
       $r->print(&show_grading_menu_form($symb));
       return '';
   }
   
   sub scantron_upload_scantron_data {
       my ($r)=@_;
       $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));
       my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
     'domainid',
     'coursename');
       my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'},
      'domainid');
       my $default_form_data=&defaultFormData(&get_symb($r,1));
       $r->print(<<UPLOAD);
   <script type="text/javascript" language="javascript">
       function checkUpload(formname) {
    if (formname.upfile.value == "") {
       alert("Please use the browse button to select a file from your local directory.");
       return false;
    }
    formname.submit();
       }
   </script>
   
   <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'>
   $default_form_data
   <table>
   <tr><td>$select_link </td></tr>
   <tr><td>Course ID:   </td><td><input name='courseid' type='text' />  </td></tr>
   <tr><td>Course Name: </td><td><input name='coursename' type='text' /></td></tr>
   <tr><td>Domain:      </td><td>$domsel                                </td></tr>
   <tr><td>File to upload:</td><td><input type="file" name="upfile" size="50" /></td></tr>
   </table>
   <input name='command' value='scantronupload_save' type='hidden' />
   <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" />
   </form>
   UPLOAD
       return '';
   }
   
   sub scantron_upload_scantron_data_save {
       my($r)=@_;
       my ($symb)=&get_symb($r,1);
       my $doanotherupload=
    '<br /><form action="/adm/grades" method="post">'."\n".
    '<input type="hidden" name="command" value="scantronupload" />'."\n".
    '<input type="submit" name="submit" value="Do Another Upload" />'."\n".
    '</form>'."\n";
       if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
    !&Apache::lonnet::allowed('usc',
       $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
    $r->print("You are not allowed to upload Scantron data to the requested course.<br />");
    if ($symb) {
       $r->print(&show_grading_menu_form($symb));
    } else {
       $r->print($doanotherupload);
    }
    return '';
       }
       my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
       $r->print("Doing upload to ".$coursedata{'description'}." <br />");
       my $fname=$env{'form.upfile.filename'};
     #FIXME      #FIXME
     # if delay queue exists 2 submits one to process delayed students one      #copied from lonnet::userfileupload()
     #     to ignore delayed students, possibly saving the delay queue for later      #make that function able to target a specified course
           # Replace Windows backslashes by forward slashes
     $navmap->untieHashes();      $fname=~s/\\/\//g;
       # Get rid of everything but the actual filename
       $fname=~s/^.*\/([^\/]+)$/$1/;
       # Replace spaces by underscores
       $fname=~s/\s+/\_/g;
       # Replace all other weird characters by nothing
       $fname=~s/[^\w\.\-]//g;
       # See if there is anything left
       unless ($fname) { return 'error: no uploaded file'; }
       my $uploadedfile=$fname;
       $fname='scantron_orig_'.$fname;
       if (length($env{'form.upfile'}) < 2) {
    $r->print("<font color='red'>Error:</font> The file you attempted to upload, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename.");
       } else {
    my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);
    if ($result =~ m|^/uploaded/|) {
       $r->print("<font color='green'>Success:</font> Successfully uploaded ".(length($env{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>");
    } else {
       $r->print("<font color='red'>Error:</font> An error (".$result.") occurred when attempting to upload the file, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>");
    }
       }
       if ($symb) {
    $r->print(&scantron_selectphase($r,$uploadedfile));
       } else {
    $r->print($doanotherupload);
       }
       return '';
   }
   
   sub valid_file {
       my ($requested_file)=@_;
       foreach my $filename (sort(&scantron_filenames())) {
    if ($requested_file eq $filename) { return 1; }
       }
       return 0;
 }  }
   
   sub scantron_download_scantron_data {
       my ($r)=@_;
       my $default_form_data=&defaultFormData(&get_symb($r,1));
       my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       my $file=$env{'form.scantron_selectfile'};
       if (! &valid_file($file)) {
    $r->print(<<ERROR);
    <p>
       The requested file name was invalid.
           </p>
   ERROR
    $r->print(&show_grading_menu_form(&get_symb($r,1)));
    return;
       }
       my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
       my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
       my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
       &Apache::lonnet::allowuploaded('/adm/grades',$orig);
       &Apache::lonnet::allowuploaded('/adm/grades',$corrected);
       &Apache::lonnet::allowuploaded('/adm/grades',$skipped);
       $r->print(<<DOWNLOAD);
       <p>
    <a href="$orig">Original</a> file as uploaded by the scantron office.
       </p>
       <p>
    <a href="$corrected">Corrections</a>, a file of corrected records that were used in grading.
       </p>
       <p>
    <a href="$skipped">Skipped</a>, a file of records that were skipped.
       </p>
   DOWNLOAD
       $r->print(&show_grading_menu_form(&get_symb($r,1)));
       return '';
   }
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
   
   
 #-------------------------- Menu interface -------------------------  #-------------------------- Menu interface -------------------------
 #  #
 #--- Show a Grading Menu button - Calls the next routine ---  #--- Show a Grading Menu button - Calls the next routine ---
 sub show_grading_menu_form {  sub show_grading_menu_form {
     my ($symb,$url)=@_;      my ($symb)=@_;
     my $result.='<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="'.$symb.'" />'."\n".
  '<input type="hidden" name="url" value="'.$url.'" />'."\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".
  '</form>'."\n";   '</form>'."\n";
Line 3359  sub show_grading_menu_form { Line 5694  sub show_grading_menu_form {
 # -- Retrieve choices for grading form  # -- Retrieve choices for grading form
 sub savedState {  sub savedState {
     my %savedState = ();      my %savedState = ();
     if ($ENV{'form.saveState'}) {      if ($env{'form.saveState'}) {
  foreach (split(/:/,$ENV{'form.saveState'})) {   foreach (split(/:/,$env{'form.saveState'})) {
     my ($key,$value) = split(/=/,$_,2);      my ($key,$value) = split(/=/,$_,2);
     $savedState{$key} = $value;      $savedState{$key} = $value;
  }   }
Line 3371  sub savedState { Line 5706  sub savedState {
 #--- Displays the main menu page -------  #--- Displays the main menu page -------
 sub gradingmenu {  sub gradingmenu {
     my ($request) = @_;      my ($request) = @_;
     my ($symb,$url)=&get_symb_and_url($request);      my ($symb)=&get_symb($request);
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $probTitle = &Apache::lonnet::gettitle($symb);      my $probTitle = &Apache::lonnet::gettitle($symb);
   
Line 3387  sub gradingmenu { Line 5722  sub gradingmenu {
  }   }
  formname.command.value = cmd;   formname.command.value = cmd;
  formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+   formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+
     ":saveSub="+radioSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status);      ":saveSub="+pullDownSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status);
  if (val < 5) formname.submit();   if (val < 5) formname.submit();
  if (val == 5) {   if (val == 5) {
     if (!checkReceiptNo(formname,'notOK')) { return false;}      if (!checkReceiptNo(formname,'notOK')) { return false;}
     formname.submit();      formname.submit();
  }   }
    if (val < 7) formname.submit();
     }      }
   
     function checkReceiptNo(formname,nospace) {      function checkReceiptNo(formname,nospace) {
Line 3412  sub gradingmenu { Line 5748  sub gradingmenu {
 GRADINGMENUJS  GRADINGMENUJS
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my $result='<h3>&nbsp;<font color="#339933">Manual Grading/View Submission</font></h3>';      my $result='<h3>&nbsp;<font color="#339933">Manual Grading/View Submission</font></h3>';
     my ($table,$resptype,$hdgrade) = &showResourceInfo($url,$probTitle);      my ($table,undef,$hdgrade) = &showResourceInfo($symb,$probTitle);
     $result.=$table;      $result.=$table;
     my (undef,$sections) = &getclasslist('all','0');      my (undef,$sections) = &getclasslist('all','0');
     my $savedState = &savedState();      my $savedState = &savedState();
Line 3423  GRADINGMENUJS Line 5759  GRADINGMENUJS
   
     $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="'.$symb.'" />'."\n".
  '<input type="hidden" name="url"         value="'.$url.'" />'."\n".  
  '<input type="hidden" name="response"    value="'.$resptype.'" />'."\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".
  '<input type="hidden" name="saveState"   value="" />'."\n".   '<input type="hidden" name="saveState"   value="" />'."\n".
    '<input type="hidden" name="gradingMenu" value="1" />'."\n".
  '<input type="hidden" name="showgrading" value="yes" />'."\n";   '<input type="hidden" name="showgrading" value="yes" />'."\n";
   
     $result.='<table width="100%" border=0><tr><td bgcolor=#777777>'."\n".      $result.='<table width="100%" border="0"><tr><td bgcolor=#777777>'."\n".
  '<table width=100% border=0><tr bgcolor="#e6ffff"><td colspan="2">'."\n".   '<table width="100%" border="0"><tr bgcolor="#e6ffff"><td colspan="2">'."\n".
  '&nbsp;<b>Select a Grading/Viewing Option</b></td></tr>'."\n".   '&nbsp;<b>Select a Grading/Viewing Option</b></td></tr>'."\n".
  '<tr bgcolor="#ffffe6" valign="top"><td>'."\n";   '<tr bgcolor="#ffffe6" valign="top"><td>'."\n";
   
     $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;Select Section: <select name="section">'."\n";   '&nbsp;'.&mt('Select Section').': <select name="section">'."\n";
     if (ref($sections)) {      if (ref($sections)) {
  foreach (sort (@$sections)) {$result.='<option value="'.$_.'" '.   foreach (sort (@$sections)) {
  ($saveSec eq $_ ? 'selected="on"' : '').'>'.$_.'</option>'."\n";}      $result.='<option value="'.$_.'" '.
    ($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n";
    }
     }      }
     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> &nbsp; ';      $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</option></select> &nbsp; ';
   
     $result.='Student Status:</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);      $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
   
     if (ref($sections)) {  
  $result.='&nbsp;(Section "no" implies the students were not assigned a section.)<br />'   
     if (grep /no/,@$sections);  
     }  
     $result.='</td></tr>';      $result.='</td></tr>';
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.      $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>Current Resource:</b> For one or more students'.   ($saveCmd eq 'submission' ? 'checked' : '').' /> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').
  '<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;-->For students with '.   '</label> <select name="submitonly">'.
  '<input type="radio" name="submitonly" value="yes" '.   '<option value="yes" '.
  ($saveSub eq 'yes' ? 'checked' : '').' /> submissions or '.   ($saveSub eq 'yes' ? 'selected="on"' : '').' />'.&mt('with submissions').'</option>'.
  '<input type="radio" name="submitonly" value="all" '.   '<option value="queued" '.
  ($saveSub eq 'all' ? 'checked' : '').' /> for all</td></tr>'."\n";   ($saveSub eq 'queued' ? 'selected="on"' : '').' />'.&mt('in grading queue').'</option>'.
    '<option value="graded" '.
    ($saveSub eq 'graded' ? 'selected="on"' : '').' />'.&mt('with ungraded submissions').'</option>'.
    '<option value="incorrect" '.
    ($saveSub eq 'incorrect' ? 'selected="on"' : '').' />'.&mt('with incorrect submissions').'</option>'.
    '<option value="all" '.
    ($saveSub eq 'all' ? 'selected="on"' : '').' />'.&mt('with any status').'</option></select></td></tr>'."\n";
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.      $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
  '<input type="radio" name="radioChoice" value="viewgrades" '.   '<label><input type="radio" name="radioChoice" value="viewgrades" '.
  ($saveCmd eq 'viewgrades' ? 'checked' : '').'> '.   ($saveCmd eq 'viewgrades' ? 'checked' : '').' /> '.
  '<b>Current Resource:</b> For all students in selected section or course</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>'.
  '<input type="radio" name="radioChoice" value="pickStudentPage" '.   '<label><input type="radio" name="radioChoice" value="pickStudentPage" '.
  ($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.   ($saveCmd eq 'pickStudentPage' ? 'checked' : '').' /> '.
  'The <b>complete</b> set/page/sequence: For one student</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 />'.
  '<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="View/Grade/Regrade" />'.   '<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="Next->" />'.
  '</td></tr></table>'."\n";   '</td></tr></table>'."\n";
   
     $result.='</td><td valign="top">';      $result.='</td><td valign="top">';
   
     $result.='<table width="100%" border=0>';      $result.='<table width="100%" border="0">';
     $result.='<tr bgcolor="#ffffe6"><td>'.      $result.='<tr bgcolor="#ffffe6"><td>'.
  '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="Upload" />'.   '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.
  ' scores from file </td></tr>'."\n";   ' '.&mt('scores from 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="Grade" /> scantron forms</td></tr>'."\n";   '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";
   
     if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb)) {      if ((&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) && ($symb)) {
  $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.   $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
     '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="Verify" />'.      '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="'.&mt('Verify').'" />'.
     ' submission Receipt no: '.unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).      ' '.&mt('receipt').': '.
     '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'.      &Apache::lonnet::recprefix($env{'request.course.id'}).
       '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')" />'.
     '</td></tr>'."\n";      '</td></tr>'."\n";
     }       } 
       $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
    '<input type="button" onClick="javascript:this.form.action=\'/adm/helper/resettimes.helper\';this.form.submit();'.
    '" value="'.&mt('Manage').'" /> access times.</td></tr>'."\n";
       $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
    '<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";
   
     $result.='</form></td></tr></table>'."\n".      $result.='</form></td></tr></table>'."\n".
  '</td></tr></table>'."\n".   '</td></tr></table>'."\n".
Line 3501  GRADINGMENUJS Line 5847  GRADINGMENUJS
     return $result;      return $result;
 }  }
   
   sub reset_perm {
       undef(%perm);
   }
   
   sub init_perm {
       &reset_perm();
       foreach my $test_perm ('vgr','mgr','opa') {
   
    my $scope = $env{'request.course.id'};
    if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {
   
       $scope .= '/'.$env{'request.course.sec'};
       if ( $perm{$test_perm}=
    &Apache::lonnet::allowed($test_perm,$scope)) {
    $perm{$test_perm.'_section'}=$env{'request.course.sec'};
       } else {
    delete($perm{$test_perm});
       }
    }
       }
   }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
   
     undef(%perm);      &reset_perm();
     if ($ENV{'browser.mathml'}) {      if ($env{'browser.mathml'}) {
  $request->content_type('text/xml');   &Apache::loncommon::content_type($request,'text/xml');
     } else {      } else {
  $request->content_type('text/html');   &Apache::loncommon::content_type($request,'text/html');
     }      }
     $request->send_http_header;      $request->send_http_header;
     return '' if $request->header_only;      return '' if $request->header_only;
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
     my $url=$ENV{'form.url'};      my $symb=&get_symb($request,1);
     my $symb=$ENV{'form.symb'};      my @commands=&Apache::loncommon::get_env_multiple('form.command');
     my $command=$ENV{'form.command'};      my $command=$commands[0];
     if (!$url) {      if ($#commands > 0) {
  my ($temp1,$temp2);   &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
  ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);  
  $url = $ENV{'form.url'};  
     }      }
     &send_header($request);      &send_header($request);
     if ($url eq '' && $symb eq '') {      if ($symb eq '' && $command eq '') {
  if ($ENV{'user.adv'}) {   if ($env{'user.adv'}) {
     if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&      if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&
  ($ENV{'form.codethree'})) {   ($env{'form.codethree'})) {
  my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.   my $token=$env{'form.codeone'}.'*'.$env{'form.codetwo'}.'*'.
     $ENV{'form.codethree'};      $env{'form.codethree'};
  my ($tsymb,$tuname,$tudom,$tcrsid)=   my ($tsymb,$tuname,$tudom,$tcrsid)=
     &Apache::lonnet::checkin($token);      &Apache::lonnet::checkin($token);
  if ($tsymb) {   if ($tsymb) {
     my ($map,$id,$url)=split(/\_\_\_/,$tsymb);      my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb);
     if (&Apache::lonnet::allowed('mgr',$tcrsid)) {      if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
  $request->print(&Apache::lonnet::ssi_body('/res/'.$url,   $request->print(&Apache::lonnet::ssi_body('/res/'.$url,
   ('grade_username' => $tuname,    ('grade_username' => $tuname,
Line 3549  sub handler { Line 5915  sub handler {
     }      }
  }   }
     } else {      } else {
  if (!($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}))) {   &init_perm();
     if ($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {  
  $perm{'vgr_section'}=$ENV{'request.course.sec'};  
     } else {  
  delete($perm{'vgr'});  
     }  
  }  
  if (!($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}))) {  
     if ($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {  
  $perm{'mgr_section'}=$ENV{'request.course.sec'};  
     } else {  
  delete($perm{'mgr'});  
     }  
  }  
   
  if ($command eq 'submission' && $perm{'vgr'}) {   if ($command eq 'submission' && $perm{'vgr'}) {
     ($ENV{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));      ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
  } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {   } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
     &pickStudentPage($request);      &pickStudentPage($request);
  } elsif ($command eq 'displayPage' && $perm{'vgr'}) {   } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
Line 3590  sub handler { Line 5942  sub handler {
     $request->print(&csvupload($request));      $request->print(&csvupload($request));
  } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {   } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
     $request->print(&csvuploadmap($request));      $request->print(&csvuploadmap($request));
  } elsif ($command eq 'csvuploadassign' && $perm{'mgr'}) {   } elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {
     if ($ENV{'form.associate'} ne 'Reverse Association') {      if ($env{'form.associate'} ne 'Reverse Association') {
  $request->print(&csvuploadassign($request));   $request->print(&csvuploadoptions($request));
     } else {      } else {
  if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {   if ( $env{'form.upfile_associate'} ne 'reverse' ) {
     $ENV{'form.upfile_associate'} = 'reverse';      $env{'form.upfile_associate'} = 'reverse';
  } else {   } else {
     $ENV{'form.upfile_associate'} = 'forward';      $env{'form.upfile_associate'} = 'forward';
  }   }
  $request->print(&csvuploadmap($request));   $request->print(&csvuploadmap($request));
     }      }
    } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
       $request->print(&csvuploadassign($request));
  } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
     $request->print(&scantron_selectphase($request));      $request->print(&scantron_selectphase($request));
     } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
        $request->print(&scantron_do_warning($request));
    } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
       $request->print(&scantron_validate_file($request));
  } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
     $request->print(&scantron_process_students($request));      $request->print(&scantron_process_students($request));
     } elsif ($command eq 'scantronupload' && 
     (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
     &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
        $request->print(&scantron_upload_scantron_data($request)); 
     } elsif ($command eq 'scantronupload_save' &&
     (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
     &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
        $request->print(&scantron_upload_scantron_data_save($request));
     } elsif ($command eq 'scantron_download' &&
    &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
        $request->print(&scantron_download_scantron_data($request));
  } elsif ($command) {   } elsif ($command) {
     $request->print("Access Denied");      $request->print("Access Denied ($command)");
  }   }
     }      }
     &send_footer($request);      &send_footer($request);
Line 3615  sub handler { Line 5984  sub handler {
   
 sub send_header {  sub send_header {
     my ($request)= @_;      my ($request)= @_;
     $request->print(&Apache::lontexconvert::header());      &Apache::lontexconvert::init_tth();
 #  $request->print("      $request->print(&Apache::loncommon::start_page('Grading'));
 #<script>      $request->rflush();
 #remotewindow=open('','homeworkremote');  
 #remotewindow.close();  
 #</script>");   
     $request->print(&Apache::loncommon::bodytag('Grading'));  
 }  }
   
 sub send_footer {  sub send_footer {
     my ($request)= @_;      my ($request)= @_;
     $request->print('</body>');      $request->print(&Apache::loncommon::end_page());
     $request->print(&Apache::lontexconvert::footer());  
 }  }
   
 1;  1;

Removed from v.1.121  
changed lines
  Added in v.1.342


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