Diff for /loncom/homework/grades.pm between versions 1.98 and 1.322

version 1.98, 2003/06/18 17:28:35 version 1.322, 2006/02/27 21:23:52
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 41  use Apache::style; Line 32  use Apache::style;
 use Apache::lonxml;  use Apache::lonxml;
 use Apache::lonnet;  use Apache::lonnet;
 use Apache::loncommon;  use Apache::loncommon;
   use Apache::lonhtmlcommon;
 use Apache::lonnavmaps;  use Apache::lonnavmaps;
 use Apache::lonhomework;  use Apache::lonhomework;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use Apache::lonmsg qw(:user_normal_msg);  use Apache::lonmsg 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=();
   
 # ----- 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 ($url,$symb) = @_;
     my @parts =();      my $partorder = &Apache::lonnet::metadata($url, 'partorder');
     my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));      my @parts;
     foreach my $key (@metakeys) {      if ($partorder) {
  if ( $key =~ m/stores_(\w+)_.*/) {   for my $part (split (/,/,$partorder)) {
     push(@parts,$key);      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_and_url {
     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 '') { 
    if (!$silent) {
       $request->print("Unable to handle ambiguous references:$url:.");
       return ();
    }
       }
     return ($symb,$url);      return ($symb,$url);
 }  }
   
 # --- Retrieve the fullname for a user. Return lastname, first middle ---  #--- Format fullname, username:domain if different for display
 # --- Generation is attached next to the lastname if it exists. ---  #--- Use anywhere where the student names are listed
 sub get_fullname {  sub nameUserString {
     my ($uname,$udom) = @_;      my ($type,$fullname,$uname,$udom) = @_;
     my %name=&Apache::lonnet::get('environment', ['lastname','generation',      if ($type eq 'header') {
   'firstname','middlename'],   return '<b>&nbsp;Fullname&nbsp;</b><font color="#999999">(Username)</font>';
                                   $udom,$uname);  
     my $fullname;  
     my ($tmp) = keys(%name);  
     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {  
         $fullname = &Apache::loncoursedata::ProcessFullName  
             (@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 ($url,$symb) = shift;
       $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq '');
     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_.*/) {
     my ($responsetype,$part) = split(/_/,$_,2);      my ($responsetype,$part) = split(/_/,$_,2);
     my ($partid,$respid) = split(/_/,$part);      my ($partid,$respid) = split(/_/,$part);
     $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');      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!!
       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,$url,$symb)=@_;
       if (!defined($symb) || $symb eq '') {
    $symb=$env{'form.symb'};
    if ($symb eq '') { $symb=&Apache::lonnet::symbread($url) }
       }
       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
   #--- and parts and response type
   sub showResourceInfo {
       my ($url,$probTitle,$checkboxes) = @_;
       my $col=3;
       if ($checkboxes) { $col=4; }
       my $result ='<table border="0">'.
    '<tr><td colspan="'.$col.'"><font size="+1"><b>'.&mt('Current Resource').': </b>'.
    $probTitle.'</font></td></tr>'."\n";
       my ($partlist,$handgrade,$responseType) = &response_type($url);
       my %resptype = ();
       my $hdgrade='no';
       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');
    $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,$url);
    $result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'.
       $resID.'</font></td>'.
       '<td><b>Type: </b>'.$responsetype.'</td></tr>';
   #    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';
       }
       $result.='</table>'."\n";
       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
   #--- Currently filters option/rank/radiobutton/match/essay response types only.
   sub cleanRecord {
       my ($answer,$response,$symb,$partid,$respid,$record,$order,$version) = @_;
       my $grayFont = '<font color="#999999">';
       if ($response =~ /^(option|rank)$/) {
    my %answer=&Apache::lonnet::str2hash($answer);
    my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
    my ($toprow,$bottomrow);
    foreach my $foil (@$order) {
       if ($grading{$foil} == 1) {
    $toprow.='<td><b>'.$answer{$foil}.'&nbsp;</b></td>';
       } else {
    $toprow.='<td><i>'.$answer{$foil}.'&nbsp;</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.'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;
       }
       return $answer;
   }
   
   #-- A couple of common js functions
   sub commonJSfunctions {
       my $request = shift;
       $request->print(<<COMMONJSFUNCTIONS);
   <script type="text/javascript" language="javascript">
       function radioSelection(radioButton) {
    var selection=null;
    if (radioButton.length > 1) {
       for (var i=0; i<radioButton.length; i++) {
    if (radioButton[i].checked) {
       return radioButton[i].value;
    }
       }
    } else {
       if (radioButton.checked) return radioButton.value;
    }
    return selection;
       }
   
       function pullDownSelection(selectOne) {
    var selection="";
    if (selectOne.length > 1) {
       for (var i=0; i<selectOne.length; i++) {
    if (selectOne[i].selected) {
       return selectOne[i].value;
    }
       }
    } else {
               // only one value it must be the selected one
       return selectOne.value;
    }
       }
   </script>
   COMMONJSFUNCTIONS
 }  }
   
 #--- Dumps the class list with usernames,list of sections,  #--- Dumps the class list with usernames,list of sections,
 #--- section, ids and fullnames for each user.  #--- section, ids and fullnames for each user.
 sub getclasslist {  sub getclasslist {
     my ($getsec,$filterlist) = @_;      my ($getsec,$filterlist) = @_;
       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 ($getsec eq 'all' || $getsec eq $section) {   if (&canview($section)) {
             $sections{$section}++;      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
             $fullnames{$_}=$fullname;   $sections{$section}++;
         } else {   $fullnames{$student}=$fullname;
             delete($classlist->{$_});      } else {
         }   delete($classlist->{$student});
       }
    } else {
       delete($classlist->{$student});
    }
     }      }
     my %seen = ();      my %seen = ();
     my @sections = sort(keys(%sections));      my @sections = sort(keys(%sections));
     return ($classlist,\@sections,\%fullnames);      return ($classlist,\@sections,\%fullnames);
 }  }
   
 #find user domain  sub canmodify {
 sub finduser {      my ($sec)=@_;
     my ($name) = @_;      if ($perm{'mgr'}) {
     my $domain = '';   if (!defined($perm{'mgr_section'})) {
     if ( $Apache::grades::viewgrades eq 'F' ) {      # can modify whole class
  my %classlist=&Apache::lonnet::dump('classlist',      return 1;
     $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},   } else {
     $ENV{'course.'.$ENV{'request.course.id'}.'.num'});      if ($sec eq $perm{'mgr_section'}) {
  my (@fields) = grep /^$name:/, keys %classlist;   #can modify the requested section
  ($name, $domain) = split(/:/,$fields[0]);   return 1;
  return ($name,$domain);      } else {
     } else {   # can't modify the request section
  return ($ENV{'user.name'},$ENV{'user.domain'});   return 0;
       }
    }
     }      }
       #can't modify
       return 0;
 }  }
   
 #--- Prompts a user to enter a username.  sub canview {
 sub moreinfo {      my ($sec)=@_;
     my ($request,$reason) = @_;      if ($perm{'vgr'}) {
     $request->print("Unable to process request: $reason");   if (!defined($perm{'vgr_section'})) {
     if ( $Apache::grades::viewgrades eq 'F' ) {      # can modify whole class
  $request->print('<form action="/adm/grades" method="post">'."\n");      return 1;
  if ($ENV{'form.url'}) {   } else {
     $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");      if ($sec eq $perm{'vgr_section'}) {
  }   #can modify the requested section
  if ($ENV{'form.symb'}) {   return 1;
     $request->print('<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n");      } else {
  }   # can't modify the request section
  $request->print('<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />'."\n");   return 0;
  $request->print("Student:".'<input type="text" name="student" value="'.$ENV{'form.student'}.'" />'."<br />\n");      }
  $request->print("Domain:".'<input type="text" name="domain" value="'.$ENV{'user.domain'}.'" />'."<br />\n");   }
  $request->print('<input type="submit" name="submit" value="ReSubmit" />'."<br />\n");  
  $request->print('</form>');  
     }      }
     return '';      #can't modify
       return 0;
 }  }
   
 #--- 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 ($url,$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 214  sub jscriptNform { Line 458  sub jscriptNform {
     $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="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 223  sub jscriptNform { Line 468  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 236  sub most_similar { Line 508  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 276  sub most_similar { Line 552  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 $url      = $env{'form.url'};
     my $symb     = $ENV{'form.symb'};      my $symb     = $env{'form.symb'};
     unless ($symb) {      unless ($symb) {
  $symb    = &Apache::lonnet::symbread($url);   $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>Problem: </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($url,$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) {
Line 316  sub verifyreceipt { Line 607  sub verifyreceipt {
     '<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,$url);
Line 331  sub listStudents { Line 625  sub listStudents {
     my ($request) = shift;      my ($request) = shift;
   
     my ($symb,$url) = &get_symb_and_url($request);      my ($symb,$url) = &get_symb_and_url($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';
       $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
    &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
   
     my $result;      my $result='<h3><font color="#339933">&nbsp;'.$viewgrade.
     my ($partlist,$handgrade) = &response_type($url);   ' Submissions for a Student or a Group of Students</font></h3>';
     for (sort keys(%$handgrade)) {  
  my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});  
  $ENV{'form.handgrade'} = 'yes' if ($handgrade eq 'yes');  
  $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.  
     '<td><b>Type: </b>'.$responsetype.'</td>'.  
     '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';  
     }  
     $result.='</table>'."\n";  
   
     my $viewgrade = $ENV{'form.handgrade'} eq 'yes' ? 'View/Grade' : 'View';      my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
     $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ?   
  &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};  
   
     $result='<h3><font color="#339933">&nbsp;'.  
  $viewgrade.  
     ' Submissions for a Student or a Group of Students</font></h3>'.  
     '<table border="0"><tr><td colspan=3><font size=+1>'.  
     '<b>Problem: </b>'.$ENV{'form.probTitle'}.'</font></td></tr>'.$result;  
   
     $request->print(<<LISTJAVASCRIPT);      $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
   function checkSelect(checkBox) {      function checkSelect(checkBox) {
     var ctr=0;   var ctr=0;
     var sense="";   var sense="";
     if (checkBox.length > 1) {   if (checkBox.length > 1) {
        for (var i=0; i<checkBox.length; i++) {      for (var i=0; i<checkBox.length; i++) {
   if (checkBox[i].checked) {   if (checkBox[i].checked) {
      ctr++;      ctr++;
   }   }
        }      }
        sense = "a student or group of students";      sense = "a student or group of students";
     } else {   } else {
        if (checkBox.checked) {      if (checkBox.checked) {
    ctr = 1;   ctr = 1;
        }      }
        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();
       }
   
       function reLoadList(formname) {
    if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}
    formname.command.value = 'submission';
    formname.submit();
     }      }
     document.gradesub.submit();  
   }  
 </script>  </script>
 LISTJAVASCRIPT  LISTJAVASCRIPT
   
       &commonJSfunctions($request);
     $request->print($result);      $request->print($result);
   
     my $checkhdgrade = $ENV{'form.handgrade'} eq 'yes' ? 'checked' : '';      my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : '';
     my $checklastsub = $ENV{'form.handgrade'} eq 'yes' ? '' : 'checked';      my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';
       my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".   "\n".$table.
  '&nbsp;<b>View Problem: </b><input type="radio" name="vProb" value="no" checked /> no '."\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="yes" /> one student '."\n".   '<label><input type="radio" name="vProb" value="yes" /> one student </label>'."\n".
  '<input type="radio" name="vProb" value="all" /> all students <br />'."\n".   '<label><input type="radio" name="vProb" value="all" /> all students </label><br />'."\n".
    '&nbsp;<b>View Answer: </b><label><input type="radio" name="vAns" value="no"  /> no </label>'."\n".
    '<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') {      if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
  $gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> handgrade only'."\n";   $gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";
     }      }
     $gradeTable.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last sub only'."\n".  
  '<input type="radio" name="lastSub" value="last" /> last sub & parts info'."\n".      my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'};
  '<input type="radio" name="lastSub" value="all" /> all details'."\n".      $env{'form.Status'} = $saveStatus;
   
       $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".
    '<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".
    '<label><input type="radio" name="lastSub" value="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="url"  value="'.$url.'" />'."\n".
  '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
  'To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n".   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
  'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n".  
  ' on the check boxes for the group of students.<br />'."\n".      if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
  '<input type="hidden" name="command" value="processGroup" />'."\n".   $gradeTable.='<input type="hidden" name="Status"   value="'.$env{'form.Status'}.'" />'."\n";
  '<input type="button" '."\n".      } else {
    $gradeTable.='<b>Student Status:</b> '.
       &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />';
       }
   
       $gradeTable.='To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '.
    'next to the student\'s name(s). Then click on the Next button.<br />'."\n".
    '<input type="hidden" name="command" value="processGroup" />'."\n";
   
   # checkall buttons
       $gradeTable.=&check_script('gradesub', 'stuinfo');
       $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,$ENV{'form.showgrading'} eq 'yes' ? '1' : '0');      $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">';
  '<td><b>&nbsp;Select&nbsp;</b></td><td><b>&nbsp;Fullname&nbsp;</b></td>'.      my $loop = 0;
  '<td><b>&nbsp;Username&nbsp;</b></td><td><b>&nbsp;Domain&nbsp;</b></td>';      while ($loop < 2) {
     foreach (sort(@$partlist)) {   $gradeTable.='<td><b>&nbsp;No.</b>&nbsp;</td><td><b>&nbsp;Select&nbsp;</b></td>'.
  $gradeTable.='<td><b>&nbsp;Part '.(split(/_/))[0].' Status&nbsp;</b></td>';      '<td>'.&nameUserString('header').'&nbsp;Section/Group</td>';
    if ($env{'form.showgrading'} eq 'yes' 
       && $submitonly ne 'queued'
       && $submitonly ne 'all') {
       foreach (sort(@$partlist)) {
    my $display_part=&get_display_part((split(/_/))[0],$url,$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++;
   # $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) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);  
  my $statusflg = '';   my %status = ();
  foreach (keys(%status)) {  
     $statusflg = 1 if ($status{$_} ne 'nothing');   if ($submitonly eq 'queued') {
     my ($foo,$partid,$foo1) = split(/\./,$_);      my %queue_status = 
     if ($status{'resource.'.$partid.'.submitted_by'} ne '') {   &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
  $statusflg = '';   $udom,$uname);
  $gradeTable.='<input type="hidden" name="'.      next if (!defined($queue_status{'gradingqueue'}));
     $student.':submitted_by" value="'.      $status{'gradingqueue'} = $queue_status{'gradingqueue'};
     $status{'resource.'.$partid.'.submitted_by'}.'" />';   }
   
    if ($env{'form.showgrading'} eq 'yes' 
       && $submitonly ne 'queued'
       && $submitonly ne 'all') {
       (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
       my $submitted = 0;
       my $graded = 0;
       my $incorrect = 0;
       foreach (keys(%status)) {
    $submitted = 1 if ($status{$_} ne 'nothing');
    $graded = 1 if ($status{$_} =~ /^ungraded/);
    $incorrect = 1 if ($status{$_} =~ /^incorrect/);
   
    my ($foo,$partid,$foo1) = split(/\./,$_);
    if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
       $submitted = 0;
       my ($part)=split(/\./,$partid);
       $gradeTable.='<input type="hidden" name="'.
    $student.':'.$part.':submitted_by" value="'.
    $status{'resource.'.$partid.'.submitted_by'}.'" />';
    }
     }      }
       
       next if (!$submitted && ($submitonly eq 'yes' ||
        $submitonly eq 'incorrect' ||
        $submitonly eq 'graded'));
       next if (!$graded && ($submitonly eq 'graded'));
       next if (!$incorrect && $submitonly eq 'incorrect');
  }   }
  next if ($statusflg eq '' && $submitonly eq 'yes');  
   
  $ctr++;   $ctr++;
  if ( $Apache::grades::viewgrades eq 'F' ) {   my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
     $gradeTable.='<tr bgcolor="#ffffe6">'.  
  '<td align="center"><input type=checkbox name="stuinfo" value="'.   if ( $perm{'vgr'} eq 'F' ) {
  $student.':'.$$fullname{$student}.'"></td>'."\n".      $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1);
  '<td>&nbsp;'.$$fullname{$student}.'&nbsp;</td>'."\n".      $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.
  '<td>&nbsp;'.$uname.'&nbsp;</td>'."\n".                 '<td align="center"><label><input type=checkbox name="stuinfo" value="'.
  '<td align="middle">&nbsp;'.$udom.'&nbsp;</td>'."\n";                 $student.':'.$$fullname{$student}.':::SECTION'.$section.
              ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
     foreach (sort keys(%status)) {         &nameUserString(undef,$$fullname{$student},$uname,$udom).
  next if (/^resource.*?submitted_by$/);         '&nbsp;'.$section.'</td>'."\n";
  $gradeTable.='<td align="middle">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";  
       if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
    foreach (sort keys(%status)) {
       next if (/^resource.*?submitted_by$/);
       $gradeTable.='<td align="center">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";
    }
     }      }
     $gradeTable.='</tr>'."\n";  #    $gradeTable.='<td></td>' if ($ctr%2 ==1);
       $gradeTable.='</tr>'."\n" if ($ctr%2 ==0);
  }   }
     }      }
     $gradeTable.='</table></td></tr></table>'.      if ($ctr%2 ==1) {
    $gradeTable.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>';
       if ($env{'form.showgrading'} eq 'yes' 
    && $submitonly ne 'queued'
    && $submitonly ne 'all') {
    foreach (@$partlist) {
       $gradeTable.='<td>&nbsp;</td>';
    }
       } elsif ($submitonly eq 'queued') {
    $gradeTable.='<td>&nbsp;</td>';
       }
    $gradeTable.='</tr>';
       }
   
       $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.' checked for submissions</font><br />';   'No '.$submissions.' found for this resource for any students. ('.$num_students.
    ' 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/;
Line 482  LISTJAVASCRIPT Line 860  LISTJAVASCRIPT
 }  }
   
 #---- 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 512  sub sub_page_js { Line 935  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 536  sub sub_page_js { Line 959  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 548  sub sub_page_js { Line 971  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 567  sub sub_page_js { Line 989  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 578  sub sub_page_js { Line 999  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 596  sub sub_page_js { Line 1017  sub sub_page_js {
     }      }
   
     function checkSolved(formname,id) {      function checkSolved(formname,id) {
  if (eval("formname.solved"+id+".value") == "correct_by_student") {   if (formname["solved"+id].value == "correct_by_student" && formname.overRideScore.value == 'no') {
     alert("This problem has been graded correct by the computer. The score cannot be changed.");      var reply = confirm("This problem has been graded correct by the computer. Do you want to change the score?");
     return "noupdate";      if (!reply) {return "noupdate";}
       formname.overRideScore.value = 'yes';
  }   }
  return "update";   return "update";
     }      }
   
     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;
     }      }
   
 //=========== Check that a point is assigned for all the parts (essay grading only) ============  //=========== Check that a point is assigned for all the parts  ============
     function checksubmit(formname,val,total,parttot) {      function checksubmit(formname,val,total,parttot) {
  document.SCORE.gradeOpt.value = val;   formname.gradeOpt.value = val;
  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 633  sub sub_page_js { Line 1055  sub sub_page_js {
     }      }
           
  }   }
    if (val == "Grade Student") {
       formname.showgrading.value = "yes";
       if (formname.Status.value == "") {
    formname.Status.value = "Active";
       }
       formname.studentNo.value = total;
    }
  formname.submit();   formname.submit();
     }      }
   
Line 641  sub sub_page_js { Line 1070  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 681  SUBJAVASCRIPT Line 1109  SUBJAVASCRIPT
 sub sub_page_kw_js {  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);
       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 718  sub sub_page_kw_js { Line 1149  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 738  sub sub_page_kw_js { Line 1168  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 761  sub sub_page_kw_js { Line 1193  sub sub_page_kw_js {
     return;      return;
   }    }
   
 //  var pWin = null;    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 769  sub sub_page_kw_js { Line 1216  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;
     pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx=70,screeny=75,width=600,height='+height);      xpos = (xpos < 0) ? '0' : xpos;
       var ypos = (screen.height-height)/2-30;
       ypos = (ypos < 0) ? '0' : ypos;
   
       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 790  sub sub_page_kw_js { Line 1242  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 803  sub sub_page_kw_js { Line 1257  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 857  sub sub_page_kw_js { Line 1311  sub sub_page_kw_js {
     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 890  sub sub_page_kw_js { Line 1345  sub sub_page_kw_js {
     return;      return;
   }    }
   
 //  var hwdWin = null;  
   function highlightCentral() {    function highlightCentral() {
 //    if (window.hwdWin) window.hwdWin.close();  //    if (window.hwdWin) window.hwdWin.close();
     hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx=100,screeny=75');      var xpos = (screen.width-400)/2;
       xpos = (xpos < 0) ? '0' : xpos;
       var ypos = (screen.height-330)/2-30;
       ypos = (ypos < 0) ? '0' : 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>");
   
     hDoc.write("<script language=javascript>");      hDoc.write("<script language=javascript>");
     hDoc.write("function updateChoice(flag) {");      hDoc.write("function updateChoice(flag) {");
     hDoc.write("  opener.document.SCORE.kwclr.value = radioSelection(document.hlCenter.kwdclr);");      hDoc.write("  opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);");
     hDoc.write("  opener.document.SCORE.kwsize.value = radioSelection(document.hlCenter.kwdsize);");      hDoc.write("  opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);");
     hDoc.write("  opener.document.SCORE.kwstyle.value = radioSelection(document.hlCenter.kwdstyle);");      hDoc.write("  opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);");
     hDoc.write("  opener.document.SCORE.refresh.value = \\"on\\";");      hDoc.write("  opener.document.SCORE.refresh.value = \\"on\\";");
     hDoc.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");      hDoc.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");
     hDoc.write("     opener.document.SCORE.submit();");      hDoc.write("     opener.document.SCORE.submit();");
Line 911  sub sub_page_kw_js { Line 1371  sub sub_page_kw_js {
     hDoc.write("  self.close()");      hDoc.write("  self.close()");
     hDoc.write("}");      hDoc.write("}");
   
     hDoc.write("function radioSelection(radioButton) {");  
     hDoc.write("    var selection=null;");  
     hDoc.write("    for (var i=0; i<radioButton.length; i++) {");  
     hDoc.write("        if (radioButton[i].checked) {");  
     hDoc.write("            selection=radioButton[i].value;");  
     hDoc.write("            return selection;");  
     hDoc.write("        }");  
     hDoc.write("    }");  
     hDoc.write("}");  
   
     hDoc.write("<");      hDoc.write("<");
     hDoc.write("/script>");      hDoc.write("/script>");
   
Line 954  sub sub_page_kw_js { Line 1404  sub sub_page_kw_js {
     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 963  SUBJAVASCRIPT Line 1414  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,undef,$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 998  sub gradeBox { Line 1451  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($uname,$udom,$counter,$partid,$record);
     return $result;      return $result;
 }  }
   
   sub handback_box {
       my ($uname,$udom,$counter,$partid,$record) = @_;
       my $result;
       foreach my $respid (undef) {
    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'});      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') {
    $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 1048  sub show_problem { Line 1545  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 $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'});
     ($uname,$udom)        = &finduser($uname) if $udom eq '';      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';      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 '';
   
     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 '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
     my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');  
       if (!&canview($usec)) {
    $request->print('<font color="red">Unable to view requested student.('.
    $uname.'@'.$udom.' in section '.$usec.' in course id '.
    $env{'request.course.id'}.')</font>');
    $request->print(&show_grading_menu_form($symb,$url));
    return;
       }
   
       if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
       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);   &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>Problem: </b>'.$ENV{'form.probTitle'}.'</font>'."\n");   '<font size=+1>&nbsp;<b>Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n");
   
    if ($env{'form.handgrade'} eq 'no') {
       my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.
    $checkIcon.' symbol.'."\n";
       $request->print($checkMark);
    }
   
  # 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';
       }
       $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 '') {   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'};
   
  $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="probTitle"  value="'.$ENV{'form.probTitle'}.'" />'."\n".   '<input type="hidden" name="Status"     value="'.$env{'form.Status'}.'" />'."\n".
    '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\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="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="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="lastSub"    value="'.$ENV{'form.lastSub'}.'" />'."\n".   '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".
  '<input type="hidden" name="section"    value="'.$ENV{'form.section'}.'">'."\n".   '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".
  '<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n".   '<input type="hidden" name="section"    value="'.$env{'form.section'}.'">'."\n".
  '<input type="hidden" name="response"   value="'.$ENV{'form.response'}.'">'."\n".   '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'">'."\n".
  '<input type="hidden" name="handgrade"  value="'.$ENV{'form.handgrade'}.'">'."\n".   '<input type="hidden" name="handgrade"  value="'.$env{'form.handgrade'}.'">'."\n".
  '<input type="hidden" name="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 1148  KEYWORDS Line 1685  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';
    }
    $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode));
     }      }
   
     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 ($partlist,$handgrade) = &response_type($url);      my ($partlist,$handgrade,$responseType) = &response_type($url,$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.'</font>'.  
  '<font color="#999999">&nbsp; &nbsp;Domain: '.$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 this is 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 1203  KEYWORDS Line 1746  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 1229  KEYWORDS Line 1774  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)=   my ($partid,$respid) = split(/_/,$part);
  &get_last_submission (%record);   my $display_part=&get_display_part($partid,$url,$symb);
     my $lastsubonly=''.   if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
  ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.      if (exists($seenparts{$partid})) { next; }
  $$timestamp).'';      $seenparts{$partid}=1;
     if ($$timestamp eq '') {      my $submitby='<b>Part:</b> '.$display_part.
  $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0].'</td></tr>'."\n";   ' <b>Collaborative submission by:</b> '.
     } else {   '<a href="javascript:viewSubmitter(\''.
  for my $part (sort keys(%$handgrade)) {   $env{"form.$uname:$udom:$partid:submitted_by"}.
     foreach (@$string) {   '\')"; TARGET=_self>'.
  my ($partid,$respid) = /^resource\.(\d+)\.(\d+)\.submission/;   $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';
  if ($part eq ($partid.'_'.$respid)) {      $request->print($submitby);
     my ($ressub,$subval) = split(/:/,$_,2);      next;
 # Similarity check   }
                             my $similar='';   my $responsetype = $responseType->{$partid}->{$respid};
                             my ($oname,$odom,$ocrsid,$oessay,$osim)=&most_similar($uname,$udom,$subval);   if (!exists($record{"resource.$partid.$respid.submission"})) {
                             if ($osim) {      $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
  $osim=int($osim*100.0);   $display_part.' <font color="#999999">( ID '.$respid.
  $similar='<hr /><h3><font color="#FF0000">Essay is '.$osim.'% similar to an essay by '.&Apache::loncommon::plainname($oname,$odom).   ' )</font>&nbsp; &nbsp;'.
                                 '</font></h3><blockquote><i>'.   '<font color="red">Nothing submitted - no attempts</font><br /><br />';
                                 &keywords_highlight($oessay).'</i></blockquote><hr />';      next;
                             }   }
     $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.   foreach (@$string) {
  $partid.'</b> <font color="#999999">( ID '.$respid.      my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;
  ' )</font>&nbsp; &nbsp;'.      if ($part ne ($partid.'_'.$respid)) { next; }
                                 ($record{"resource.$partid.$respid.uploadedurl"}?      my ($ressub,$subval) = split(/:/,$_,2);
                                 '<a href="'.      # Similarity check
                                 &Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).      my $similar='';
    '"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> <font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />':'').      if($env{'form.checkPlag'}){
                                 '<b>Answer: </b><blockquote>'.   my ($oname,$odom,$ocrsid,$oessay,$osim)=
  &keywords_highlight($subval).'</blockquote><br />&nbsp;'.$similar.'</td></tr>'."\n"      &most_similar($uname,$udom,$subval);
  if ($ENV{'form.lastSub'} eq 'lastonly' ||    if ($osim) {
     ($ENV{'form.lastSub'} eq 'hdgrade' &&       $osim=int($osim*100.0);
      $$handgrade{$part} =~ /:yes$/));      $similar="<hr /><h3><font color=\"#FF0000\">Essay".
    " is $osim% similar to an essay by ".
    &Apache::loncommon::plainname($oname,$odom).
    '</font></h3><blockquote><i>'.
    &keywords_highlight($oessay).
    '</i></blockquote><hr />';
  }   }
     }      }
       my $order=&get_order($partid,$respid,$symb,$uname,$udom);
       if ($env{'form.lastSub'} eq 'lastonly' || 
    ($env{'form.lastSub'} eq 'hdgrade' && 
    $$handgrade{$part} eq 'yes')) {
    my $display_part=&get_display_part($partid,$url,$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.'" 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>'."\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($url);
    $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'));
     }      }
   
       $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
    .$udom.'" />'."\n");
           
     # return if view submission with no grading option      # return if view submission with no grading option
     if ($ENV{'form.showgrading'} eq '') {      if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {
  $request->print('</td></tr></table></td></tr></table></form>'."\n");   my $toGrade.='<input type="button" value="Grade Student" '.
  $request->print(&show_grading_menu_form($symb,$url))       'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''
     if (($ENV{'form.command'} eq 'submission') ||       .$counter.'\');" TARGET=_self> &nbsp;'."\n" if (&canmodify($usec));
  ($ENV{'form.command'} eq 'processGroup' && $counter == $total));   $toGrade.='</td></tr></table></td></tr></table>'."\n";
    if (($env{'form.command'} eq 'submission') || 
       ($env{'form.command'} eq 'processGroup' && $counter == $total)) {
       $toGrade.='</form>'.&show_grading_menu_form($symb,$url) 
    }
    $request->print($toGrade);
  return;   return;
       } else {
    $request->print('</td></tr></table></td></tr></table>'."\n");
     }      }
   
     # Grading options      # essay grading message center
     $result='<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".      if ($env{'form.handgrade'} eq 'yes') {
  '<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".   my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
  '<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'   my $msgfor = $givenn.' '.$lastname;
  .$udom.'" />'."\n";   if (scalar(@col_fullnames) > 0) {
     my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'});      my $lastone = pop @col_fullnames;
     my $msgfor = $givenn.' '.$lastname;      $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';
     if (scalar(@col_fullnames) > 0) {   }
  my $lastone = pop @col_fullnames;   $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
  $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';   $result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
     }      '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
     $msgfor =~ s/\'/\\'/g; #' stupid emacs   $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
     $result.='<tr><td bgcolor="#ffffff">'."\n".      ',\''.$msgfor.'\')"; TARGET=_self>'.
  '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.      &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> ('.
  ',\''.$msgfor.'\')"; TARGET=_self>'.      &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" />)'.
  'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> &nbsp;'.      '<img src="'.$request->dir_config('lonIconsURL').
  '<img src="'.$request->dir_config('lonIconsURL').      '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".
  '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".      '<br />&nbsp;('.
  '<br />&nbsp;(Message will be sent when you click on Save & Next below.)'."\n"       &mt('Message will be sent when you click on Save & Next below.').")\n";
  if ($ENV{'form.handgrade'} eq 'yes');   $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$/);   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 1341  KEYWORDS Line 1941  KEYWORDS
   
     # print end of form      # print end of form
     if ($counter == $total) {      if ($counter == $total) {
  my $endform='<table border="0"><tr><td>'.   my $endform='<table border="0"><tr><td>'."\n";
     '<input type="hidden" name="gradeOpt" value="" />'."\n";   $endform.='<input type="button" value="Save & Next" '.
  if ($ENV{'form.handgrade'} eq 'yes') {      'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
     $endform.='<input type="button" value="Save & Next" '.      $total.','.scalar(@partlist).');" TARGET=_self> &nbsp;'."\n";
  'onClick="javascript:checksubmit(this.form,\'Save & Next\','.   my $ntstu ='<select name="NTSTU">'.
  $total.','.scalar(@partlist).');" TARGET=_self> &nbsp;'."\n";      '<option>1</option><option>2</option>'.
     my $ntstu ='<select name="NTSTU">'.      '<option>3</option><option>5</option>'.
  '<option>1</option><option>2</option>'.      '<option>7</option><option>10</option></select>'."\n";
  '<option>3</option><option>5</option>'.   my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
  '<option>7</option><option>10</option></select>'."\n";   $ntstu =~ s/<option>$nsel</<option selected="on">$nsel</;
     my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');   $endform.=$ntstu.'student(s) &nbsp;&nbsp;';
     $ntstu =~ s/<option>$nsel</<option selected="on">$nsel</;   $endform.='<input type="button" value="Previous" '.
     $endform.=$ntstu.'student(s) &nbsp;&nbsp;';      'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;'."\n".
  } else {      '<input type="button" value="Next" '.
     $endform.='<input type="hidden" name="NTSTU" value="1" />'."\n";      'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;';
  }   $endform.='(Next and Previous (student) do not save the scores.)'."\n" ;
  $endform.='<input type="button" value="Next" '.  
     'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;'."\n".  
     '<input type="button" value="Previous" '.  
     'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;';  
  $endform.='(Next and Previous do not save the scores.)'."\n"   
     if ($ENV{'form.handgrade'} eq 'yes');  
  $endform.='</td><tr></table></form>';   $endform.='</td><tr></table></form>';
  $endform.=&show_grading_menu_form($symb,$url);   $endform.=&show_grading_menu_form($symb,$url);
  $request->print($endform);   $request->print($endform);
Line 1372  KEYWORDS Line 1966  KEYWORDS
   
 #--- Retrieve the last submission for all the parts  #--- Retrieve the last submission for all the parts
 sub get_last_submission {  sub get_last_submission {
     my (%returnhash)=@_;      my ($returnhash)=@_;
     my (@string,$timestamp);      my (@string,$timestamp);
     if ($returnhash{'version'}) {      if ($$returnhash{'version'}) {
  my %lasthash=();   my %lasthash=();
  my ($version);   my ($version);
  for ($version=1;$version<=$returnhash{'version'};$version++) {   for ($version=1;$version<=$$returnhash{'version'};$version++) {
     foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {      foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) {
  $lasthash{$_}=$returnhash{$version.':'.$_};   $lasthash{$_}=$$returnhash{$version.':'.$_};
    $timestamp = scalar(localtime($returnhash{$version.':timestamp'}));     $timestamp = scalar(localtime($$returnhash{$version.':timestamp'}));
     }      }
  }   }
  foreach ((keys %lasthash)) {   foreach ((keys %lasthash)) {
Line 1392  sub get_last_submission { Line 1986  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;
     }      }
     # This is not really the right place to do this, but I cannot find a  
     # better one at this time.  So here we go - the m in the s:::mg causes  
     # ^ to match the beginning of a new line.  So we replace(???) the beginning  
     # of the line with <br /> to make things formatted a little better.  
     $string =~ s:^:<br />:mg;  
     return $string;      return $string;
 }  }
   
 #--- Called from submission routine  #--- Called from submission routine
 sub processHandGrade {  sub processHandGrade {
     my ($request) = shift;      my ($request) = shift;
     my $url    = $ENV{'form.url'};      my $url    = $env{'form.url'};
     my $symb   = $ENV{'form.symb'};      my $symb   = $env{'form.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,$url,$symb,$uname,$udom,$ctr);
     if ($errorflag eq 'no_score') {      if ($errorflag eq 'no_score') {
  $ctr++;   $ctr++;
  next;   next;
     }      }
     my $includemsg = $ENV{'form.includemsg'.$ctr};      if ($errorflag eq 'not_allowed') {
    $request->print("<font color=\"red\">Not allowed to modify grades for $uname:$udom</font>");
    $ctr++;
    next;
       }
       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,   $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
        $ENV{'form.msgsub'},$message);         $subject.' ['.
     }         &Apache::lonnet::declutter($url).']',$message);
     if ($ENV{'form.collaborator'.$ctr}) {   $request->print('<br />'.&mt('Sending message to [_1]@[_2]',$uname,$udom).': '.
  my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});   $msgstatus);
  foreach (@collaborators) {      }
     &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,      if ($env{'form.collaborator'.$ctr}) {
    $ENV{'form.unamedom'.$ctr});   my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
     if ($message ne '') {   foreach my $collabstr (@collabstrs) {
  $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,      my ($part,@collaborators) = split(/:/,$collabstr);
        $ENV{'form.msgsub'},      foreach my $collaborator (@collaborators) {
        $message);   my ($errorflag,$pts,$wgt) = 
       &saveHandGrade($request,$url,$symb,$collaborator,$udom,$ctr,
      $env{'form.unamedom'.$ctr},$part);
    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 1464  sub processHandGrade { Line 2073  sub processHandGrade {
  }   }
     }      }
   
     # Keywords sorted in alphabatical order      if ($env{'form.handgrade'} eq 'yes') {
     my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};   # Keywords sorted in alphabatical order
     my %keyhash = ();   my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
     $ENV{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;   my %keyhash = ();
     $ENV{'form.keywords'}           =~ s/^\s+|\s+$//;   $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
     my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));   $env{'form.keywords'}           =~ s/^\s+|\s+$//;
     $ENV{'form.keywords'} = join(' ',@keywords);   my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
     $keyhash{$symb.'_keywords'}     = $ENV{'form.keywords'};   $env{'form.keywords'} = join(' ',@keywords);
     $keyhash{$symb.'_subject'}      = $ENV{'form.msgsub'};   $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};
     $keyhash{$loginuser.'_kwclr'}   = $ENV{'form.kwclr'};   $keyhash{$symb.'_subject'}      = $env{'form.msgsub'};
     $keyhash{$loginuser.'_kwsize'}  = $ENV{'form.kwsize'};   $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};
     $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'};   $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};
    $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
     # message center - Order of message gets changed. Blank line is eliminated.  
     # New messages are saved in ENV for the next student.   # message center - Order of message gets changed. Blank line is eliminated.
     # All messages are saved in nohist_handgrade.db   # New messages are saved in env for the next student.
     my ($ctr,$idx) = (1,1);   # All messages are saved in nohist_handgrade.db
     while ($ctr <= $ENV{'form.savemsgN'}) {   my ($ctr,$idx) = (1,1);
  if ($ENV{'form.savemsg'.$ctr} ne '') {   while ($ctr <= $env{'form.savemsgN'}) {
     $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};      if ($env{'form.savemsg'.$ctr} ne '') {
     $idx++;   $keyhash{$symb.'_savemsg'.$idx} = $env{'form.savemsg'.$ctr};
    $idx++;
       }
       $ctr++;
  }   }
  $ctr++;   $ctr = 0;
     }   while ($ctr < $ngrade) {
     $ctr = 0;      if ($env{'form.newmsg'.$ctr} ne '') {
     while ($ctr < $ngrade) {   $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  if ($ENV{'form.newmsg'.$ctr} ne '') {   $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
     $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};   $idx++;
     $ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};      }
     $idx++;      $ctr++;
  }   }
  $ctr++;   $env{'form.savemsgN'} = --$idx;
    $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
    my $putresult = &Apache::lonnet::put
       ('nohist_handgrade',\%keyhash,$cdom,$cnum);
     }      }
     $ENV{'form.savemsgN'} = --$idx;  
     $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'};  
     my $putresult = &Apache::lonnet::put  
  ('nohist_handgrade',\%keyhash,  
  $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'},'0');      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++;
  }   }
  return '';   return '';
     }      }
   
   # Go directly to grade student - from submission or link from chart page
       if ($button eq 'Grade Student') {
    (undef,undef,$env{'form.handgrade'},undef,undef) = &showResourceInfo($url);
    my $processUser = $env{'form.unamedom'.$env{'form.studentNo'}};
    ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
    $env{'form.fullname'} = $$fullname{$processUser};
    &submission($request,0,0);
    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)};
     $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 1547  sub processHandGrade { Line 2171  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($url);
     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($url,$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 1570  sub processHandGrade { Line 2215  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 1580  sub processHandGrade { Line 2225  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,$url);
  $request->print($the_end);   $request->print($the_end);
     }      }
     return '';      return '';
Line 1588  sub processHandGrade { Line 2233  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,$url,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
     my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);      my @v_flag;
       my $usec = &Apache::lonnet::getsection($domain,$stuname,
      $env{'request.course.id'});
       if (!&canmodify($usec)) { return('not_allowed'); }
       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') {  
  $newrecord{'resource.'.$_.'.solved'} = 'excused';      my @parts = split(/:/,$env{'form.partlist'.$newflg});
  if (exists($record{'resource.'.$_.'.awarded'})) {      foreach my $new_part (@parts) {
     $newrecord{'resource.'.$_.'.awarded'} = '';   #collaborator may vary for different parts
    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($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;
     $newrecord{'resource.'.$_.'.awarded'}  = $partial       if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
  if ($record{'resource.'.$_.'.awarded'} ne $partial);   #do not update score for part if not changed.
     my $reckey = 'resource.'.$_.'.solved';   next;
     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');  
     }      }
     $newrecord{'resource.'.$_.'.submitted_by'} = $submitter       if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
  if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));   $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
     $newrecord{'resource.'.$_.'regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";      }
       my $reckey = 'resource.'.$new_part.'.solved';
       if ($partial == 0) {
    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'}";
    }
    my ($partlist,$handgrade,$responseType) = &response_type($url,$symb);
    my $portfolio_root = &Apache::loncommon::propath($domain,
           $stuname).
           '/userfiles/portfolio';
    foreach my $part_resp (sort(keys(%$handgrade))) {
       my ($part_id, $resp_id) = split(/_/,$part_resp);
               if ($env{'form.'.$part_resp.'_returndoc1'} && ($new_part eq $part_id)) {
                   # if multiple files are uploaded names will be 'returndoc2','returndoc3'
                   my $file_counter = 1;
                   while ($env{'form.'.$part_resp.'_returndoc'.$file_counter}) {
                       my $fname=$env{'form.returndoc'.$file_counter.'.filename'};
                       $newrecord{"resource.$new_part.$resp_id.handback"} = $env{'form.returndocorig'.$file_counter};
                       $request->print("<br />".$fname." will be the uploaded file name");
                       $request->print("<font color=\"red\">Will upload document</font>".$env{'form.returndocorig'.$file_counter});
                       $file_counter++;
                   }
               }
           }
   
    # 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 (@v_flag,$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(@v_flag)) {
               &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@v_flag);
           }
  &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 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);
           }
       }
       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, $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 @v_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 $version = 0;
    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);
                   $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(@v_portfiles, $directory.$new_answer);
                       &Apache::lonnet::mark_as_readonly($domain,$stu_name,
                           ['/portfolio'.$directory.$new_answer],
                           [$symb,$env{'request.course.id'},'graded']);
                   }
                   
               }
               $$record{$key} = join(',',@v_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 '',$pts,$wgt;      return($name,$version,$ext);
 }  }
   
 #--------------------------------------------------------------------------------------  #--------------------------------------------------------------------------------------
Line 1642  sub viewgrades_js { Line 2549  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) || point < 0) {      if (isNaN(point) || parseFloat(point) < 0) {
  alert("A number equal or greater than 0 is expected. Entered value = "+point);   alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
  var resetbox = false;   var resetbox = false;
  for (var i=0; i<radioButton.length; i++) {   for (var i=0; i<radioButton.length; i++) {
     if (radioButton[i].checked) {      if (radioButton[i].checked) {
Line 1660  sub viewgrades_js { Line 2567  sub viewgrades_js {
  }   }
  return;   return;
     }      }
     if (point > weight) {      if (parseFloat(point) > parseFloat(weight)) {
  var resp = confirm("You entered a value ("+point+   var resp = confirm("You entered a value ("+parseFloat(point)+
    ") greater than the weight for the part. Accept?");     ") greater than the weight for the part. Accept?");
  if (resp == false) {   if (resp == false) {
     textbox.value = "";      textbox.value = "";
Line 1670  sub viewgrades_js { Line 2577  sub viewgrades_js {
     }      }
     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 (point == i) {   if (parseFloat(point) == i) {
     radioButton[i].checked=true;      radioButton[i].checked=true;
  }   }
     }      }
   
  } 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 1692  sub viewgrades_js { Line 2598  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 1708  sub viewgrades_js { Line 2614  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 1739  sub viewgrades_js { Line 2646  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) || point < 0) {   if (isNaN(point) || parseFloat(point) < 0) {
     alert("A number equal or greater than 0 is expected. Entered value = "+point);      alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
     textbox.value = "";      textbox.value = "";
     return;      return;
  }   }
  if (point > weight) {   if (parseFloat(point) > parseFloat(weight)) {
     var resp = confirm("You entered a value ("+point+      var resp = confirm("You entered a value ("+parseFloat(point)+
        ") greater than the weight of the part. Accept?");         ") greater than the weight of the part. Accept?");
     if (resp == false) {      if (resp == false) {
  textbox.value = "";   textbox.value = "";
Line 1761  sub viewgrades_js { Line 2668  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 1782  sub viewgrades_js { Line 2691  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 1810  sub viewgrades { Line 2717  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,$url) = ($env{'form.symb'},$env{'form.url'}); 
     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>Problem: </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($url,$symb);
Line 1823  sub viewgrades { Line 2736  sub viewgrades {
  '<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="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($url,$symb);
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     $result.='<table border="0">';      $result.='<table border="0">';
Line 1848  sub viewgrades { Line 2763  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 1856  sub viewgrades { Line 2771  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,$url,$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 1875  sub viewgrades { Line 2791  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".
  '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';   '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
     $result.='<input type="button" value="Reset" '.      $result.='<input type="button" value="Reset" '.
  'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self> &nbsp; &nbsp;';   'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self>';
     $result.='<input type="button" value="Submit Changes" '.  
  'onClick="javascript:submit();" TARGET=_self />'."\n";  
   
     #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></td><td><b>Username</b></td><td><b>Domain</b></td>'."\n";   '<td>'.&nameUserString('header')."</td>\n";
     my (@parts) = sort(&getpartlist($url));      my (@parts) = sort(&getpartlist($url,$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');
    $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,$url,$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($url,$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'}.'" with enrollment status "'.$ENV{'form.status'}.'" to modify or grade.</font>';   $result='<font color="red">There are no students in section "'.$env{'form.section'}.
       '" with enrollment status "'.$env{'form.Status'}.'" to modify or grade.</font>';
     }      }
     $result.=&show_grading_menu_form($symb,$url);      $result.=&show_grading_menu_form($symb,$url);
     return $result;      return $result;
Line 1939  sub viewgrades { Line 2861  sub viewgrades {
   
 #--- 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 ($url,$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> '.
  '</td><td>'.$uname.'</td><td align="middle">'.$udom.'</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 1963  sub viewstudentgrade { Line 2904  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.='<td align="middle"><input type="text" name="'.      $result.='<input type="text" name="'.
  'GD_'.$student.'_'.$part.'_'.$type.'" '.   'GD_'.$student.'_'.$part.'_'.$type.'" '.
  'value="'.$score.'" size="4" /></td>'."\n";   'value="'.$score.'" size="4" /></td>'."\n";
  }   }
Line 1989  sub viewstudentgrade { Line 2929  sub viewstudentgrade {
 sub editgrades {  sub editgrades {
     my ($request) = @_;      my ($request) = @_;
   
     my $symb=$ENV{'form.symb'};      my $symb=$env{'form.symb'};
     my $url =$ENV{'form.url'};      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>Problem: </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 2005  sub editgrades { Line 2947  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($url,$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++;
     }      }
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
Line 2030  sub editgrades { Line 2972  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,$url,$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 .= '<td>'.&nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
  $line .= '<tr bgcolor="#ffffde"><td>'.$uname.'&nbsp;</td><td>'.   my $usec=$classlist->{"$uname:$udom"}[5];
     $udom.'&nbsp;</td><td>'.   if (!&canmodify($usec)) {
  $$fullname{$usercolon}.'&nbsp;</td>';      my $numcols=scalar(@partid)*4+2;
       $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";
       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 2104  sub editgrades { Line 3070  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) {
  $result .= '<tr bgcolor="#ffffff"><td align="center" colspan="7">No Changes Occured For the Students Below</td></tr>'.$noupdate;  # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
    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,$url);
     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 2143  sub split_part_type { Line 3145  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 2181  ENDPICK Line 3185  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 2218  ENDPICK Line 3224  ENDPICK
 sub csvuploadmap_header {  sub csvuploadmap_header {
     my ($request,$symb,$url,$datatoken,$distotal)= @_;      my ($request,$symb,$url,$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='<table border="0">';      my ($result) = &showResourceInfo($url,$env{'form.probTitle'});
     $result.='<tr><td colspan=3><font size=+1><b>Problem: </b>'.$ENV{'form.probTitle'}.'</font></td></tr>';      my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
     my ($partlist,$handgrade) = &response_type($url);      my $ignore=&mt('Ignore First Line');
     my ($resptype,$hdgrade)=('','no');  
     for (sort keys(%$handgrade)) {  
  my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});  
  $resptype = $responsetype;  
  $hdgrade = $handgrade if ($handgrade eq 'yes');  
  $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.  
     '<td><b>Type: </b>'.$responsetype.'</td>'.  
     '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';  
     }  
     $result.='</table>';  
     $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>
Line 2247  Total number of records found in file: $ Line 3243  Total number of records found in file: $
 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="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="csvuploadassign" />  <input type="hidden" name="command"    value="csvuploadoptions" />
 <hr />  <hr />
 <script type="text/javascript" language="Javascript">  <script type="text/javascript" language="Javascript">
 $javascript  $javascript
 </script>  </script>
 ENDPICK  ENDPICK
 return '';      return '';
   
 }  }
   
 sub csvupload_fields {  sub csvupload_fields {
     my ($url) = @_;      my ($url,$symb) = @_;
     my (@parts) = &getpartlist($url);      my (@parts) = &getpartlist($url,$symb);
     my @fields=(['username','Student Username'],['domain','Student Domain']);      my @fields=(['ID','Student ID'],
    ['username','Student Username'],
    ['domain','Student Domain']);
     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 2294  sub csvuploadmap_footer { Line 3296  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 2309  sub upcsvScores_form { Line 3308  sub upcsvScores_form {
     }      }
     </script>      </script>
 CSVFORMJS  CSVFORMJS
     $ENV{'form.probTitle'} = &Apache::lonnet::gettitle($symb);      return $result;
   }
   
   sub upcsvScores_form {
       my ($request) = shift;
       my ($symb,$url)=&get_symb_and_url($request);
       if (!$symb) {return '';}
       my $result=&checkforfile_js();
       $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
       my ($table) = &showResourceInfo($url,$env{'form.probTitle'});
       $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 problem - '.$ENV{'form.probTitle'}.      $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" target="LONcatInfo">  <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="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,$url);
   
     return $result;      return $result;
 }  }
   
Line 2342  sub csvuploadmap { Line 3351  sub csvuploadmap {
     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();
       if ($env{'form.noFirstLine'}) { shift(@records); }
     &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);      &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
     my ($i,$keyfields);      my ($i,$keyfields);
     if (@records) {      if (@records) {
  my @fields=&csvupload_fields($url);   my @fields=&csvupload_fields($url,$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 2364  sub csvuploadmap { Line 3374  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);
Line 2374  sub csvuploadmap { Line 3389  sub csvuploadmap {
     return '';      return '';
 }  }
   
 sub csvuploadassign {  sub csvuploadoptions {
     my ($request)= @_;      my ($request)= @_;
     my ($symb,$url)=&get_symb_and_url($request);      my ($symb,$url)=&get_symb_and_url($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,$url));
       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,$url)=&get_symb_and_url($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 @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;
    }
    my $usec=$classlist->{"$username:$domain"}[5];
    if (!&canmodify($usec)) {
       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('<br /><font size="+1"><b>Skipped Students</b></font><br />');   $request->print('<p<font size="+1"><b>Skipped Students</b></font></p>');
  foreach my $student (@skipped) { $request->print("<br />$student"); }   foreach my $student (@skipped) { $request->print("$student<br />\n"); }
       }
       if (@notallowed) {
    $request->print('<p><font size="+1" color="red"><b>Students Not Allowed to Modify</b></font></p>');
    foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
     }      }
       $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb,$url));      $request->print(&show_grading_menu_form($symb,$url));
     return '';      return '';
 }  }
Line 2434  sub csvuploadassign { Line 3557  sub csvuploadassign {
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
 #  #
 #-------------- 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 2448  function checkPickOne(formname) { Line 3571  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();
 }  }
   
 function radioSelection(radioButton) {  
     var selection=null;  
     if (radioButton.length > 1) {  
  for (var i=0; i<radioButton.length; i++) {  
     if (radioButton[i].checked) {  
  return radioButton[i].value;  
     }  
  }  
     } else {  
  if (radioButton.checked) return radioButton.value;  
     }  
     return selection;  
 }  
       
 function pullDownSelection(selectOne) {  
     var selection="";  
     if (selectOne.length > 1) {  
  for (var i=0; i<selectOne.length; i++) {  
     if (selectOne[i].selected) {  
  return selectOne[i].value;  
     }  
  }  
     } else {  
  if (selectOne.selected) return selectOne.value;  
     }  
 }  
 </script>  </script>
 LISTJAVASCRIPT  LISTJAVASCRIPT
       &commonJSfunctions($request);
     my ($symb,$url) = &get_symb_and_url($request);      my ($symb,$url) = &get_symb_and_url($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 2494  LISTJAVASCRIPT Line 3591  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 2514  LISTJAVASCRIPT Line 3613  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: </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="command" value="displayPage" />'."\n".   '<input type="hidden" name="command" value="displayPage" />'."\n".
  '<input type="hidden" name="url"     value="'.$url.'" />'."\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,$url);
     $request->print($studentTable);      $request->print($studentTable);
Line 2566  LISTJAVASCRIPT Line 3671  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',1, 1);  
   
     my $res = $navmap->firstResource(); # temp resource to access constants  
     $navmap->init();  
   
     # End navmap using boilerplate  
   
     my $iterator = $navmap->getIterator(undef, undef, undef, 1);  
     my $depth = 1;  
     $iterator->next(); # ignore first BEGIN_MAP  
     my $curRes = $iterator->next();  
   
     my %symbx = ();      my %symbx = ();
     my @titles = ();      my @titles = ();
     my $minder=0;      my $minder = 0;
     while ($depth > 0) {  
         if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}  
         if ($curRes == $iterator->END_MAP()) { $depth--; }  
   
         if (ref($curRes) && $curRes->is_map()) {      # Gather every sequence that has problems.
     my ($mapUrl, $id, $resUrl) = split(/___/, $curRes->symb()); # check map contains at least one problem      my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps         1,0,1);
       for my $sequence ($navmap->getById('0.0'), @sequences) {
     my $mapiterator = $navmap->getIterator($map->map_start(),   if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
    $map->map_finish());      my $title = $minder.'.'.$sequence->compTitle();
       push @titles, $title; # minder in case two titles are identical
     my $mapdepth = 1;      $symbx{$title} = $sequence->symb();
     my $countProblems = 0;      $minder++;
     $mapiterator->next(); # skip the first BEGIN_MAP   }
     my $mapcurRes = $mapiterator->next(); # for "current resource"  
     my $ctr=0;  
     while ($mapdepth > 0) {  
  if($mapcurRes == $mapiterator->BEGIN_MAP) { $mapdepth++; }  
  if($mapcurRes == $mapiterator->END_MAP) { $mapdepth++; }  
   
  if (ref($mapcurRes) && $mapcurRes->is_problem() && !$mapcurRes->randomout) {  
     $countProblems++;  
  }  
  $mapcurRes = $mapiterator->next();  
     }  
     if ($countProblems > 0) {  
  my $title = $curRes->compTitle();  
  push @titles,$minder.'.'.$title; # minder, just in case two titles are identical  
  $symbx{$minder.'.'.$title} = $curRes->symb();  
  $minder++;  
     }  
        }  
         $curRes = $iterator->next();  
     }      }
   
     $navmap->untieHashes();  
     return \@titles,\%symbx;      return \@titles,\%symbx;
 }  }
   
Line 2627  sub displayPage { Line 3697  sub displayPage {
     my ($request) = shift;      my ($request) = shift;
   
     my ($symb,$url) = &get_symb_and_url($request);      my ($symb,$url) = &get_symb_and_url($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 (undef,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 $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';  
     $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.      #need to make sure we have the correct data for later EXT calls, 
  '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";      #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)) {
    $request->print('<font color="red">Unable to view requested student.('.$env{'form.student'}.')</font>');
    $request->print(&show_grading_menu_form($symb,$url));
    return;
       }
       my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';
       $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom).
    '</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,$url));
    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="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" />';
   
     $studentTable.='&nbsp;<b>Note:</b> A problem graded correct ('.$checkIcon.      $studentTable.='&nbsp;<b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon.
  ') by the computer cannot be changed.'."\n".   ' symbol.'."\n".
  '<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;No&nbsp;</b></td>'.   '<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
  '<td><b>&nbsp;'.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem View').'/Grade</b></td></tr>';   '<td><b>&nbsp;'.($env{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';
   
     my ($depth,$ctr,$question) = (1,0,1);      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 && $ctr < 100) { # ctr, just in case it never gets out of loop      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) {
     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(   my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'});
  $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>
 #    $request->print('match='.$1.'<br>');  #    $companswer =~ s/$1/ /ms;
 #    $companswer =~ s/$1/ /s;  #    $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 {
     $studentTable.='<table border="0" width="100%"><tr><td bgcolor="#777777">'.      my %responseType = ();
  '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.      foreach my $partid (@{$parts}) {
  '<td><b>Date/Time</b></td>'.   my @responseIds =$curRes->responseIds($partid);
  '<td><b>Submission</b></td>'.   my @responseType =$curRes->responseType($partid);
  '<td><b>Status&nbsp;</b></td></tr>';   my %responseIds;
     my ($version);   for (my $i=0;$i<=$#responseIds;$i++) {
     for ($version=1;$version<=$record{'version'};$version++) {      $responseIds{$responseIds[$i]}=$responseType[$i];
  my $timestamp = scalar(localtime($record{$version.':timestamp'}));  
  $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';  
  my @versionKeys = split(/\:/,$record{$version.':keys'});  
  my @displaySub = ();  
  foreach my $partid (@{$parts}) {  
     my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys;  
     next if ($record{"$version:resource.$partid.solved"} eq '');  
 #    next if ($record{"$version:resource.$partid.award"} eq 'APPROX_ANS' &&   
 #     $record{"$version:resource.$partid.solved"} eq '');  
     $displaySub[0].=(exists $record{$version.':'.$matchKey[0]}) ?   
  '<b>Part&nbsp;'.$partid.'&nbsp;'.  
  ($record{"$version:resource.$partid.tries"} eq '' ? 'Trial&nbsp;not&nbsp;counted' :  
  'Trial&nbsp;'.$record{"$version:resource.$partid.tries"}).'</b>&nbsp; '.  
  $record{$version.':'.$matchKey[0]}.'<br />' : '';  
     $displaySub[1].=(exists $record{"$version:resource.$partid.award"}) ?  
  '<b>Part&nbsp;'.$partid.'</b> &nbsp;'.  
  $record{"$version:resource.$partid.award"}.'/'.  
  $record{"$version:resource.$partid.solved"}.'<br />' : '';  
     $displaySub[2].=(exists $record{"$version:resource.$partid.regrader"}) ?  
  $record{"$version:resource.$partid.regrader"}.' (<b>Part:</b> '.$partid.')' : '';  
  }   }
  $displaySub[2].=(exists $record{"$version:resource.regrader"}) ?   $responseType{$partid} = \%responseIds;
     $record{"$version:resource.regrader"} : '';  
  $studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1].  
     ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).'&nbsp;</td></tr>';  
     }      }
     $studentTable.='</table></td></tr></table>';      $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');
     
     }      }
       if (&canmodify($usec)) {
     foreach my $partid (@{$parts}) {   foreach my $partid (@{$parts}) {
  $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);      $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
  $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>';
   
        }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
  $ctr++;  
     }      }
   
     $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,$url);
Line 2771  sub displayPage { Line 3834  sub displayPage {
     return '';      return '';
 }  }
   
   sub displaySubByDates {
       my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
       my $isCODE=0;
       if (exists($record->{'resource.CODE'})) { $isCODE=1; }
       my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
    '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
    '<td><b>Date/Time</b></td>'.
    ($isCODE?'<td><b>CODE</b></td>':'').
    '<td><b>Submission</b></td>'.
    '<td><b>Status&nbsp;</b></td></tr>';
       my ($version);
       my %mark;
       my %orders;
       $mark{'correct_by_student'} = $checkIcon;
       if (!exists($$record{'1:timestamp'})) {
    return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />';
       }
       for ($version=1;$version<=$$record{'version'};$version++) {
    my $timestamp = scalar(localtime($$record{$version.':timestamp'}));
    $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 @displaySub = ();
    foreach my $partid (@{$parts}) {
       my @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
   #    next if ($$record{"$version:resource.$partid.solved"} eq '');
       my $display_part=&get_display_part($partid,undef,$symb);
       foreach my $matchKey (@matchKey) {
    if (exists($$record{$version.':'.$matchKey}) &&
       $$record{$version.':'.$matchKey} ne '') {
       my ($responseId)=($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/);
       $displaySub[0].='<b>Part:</b>&nbsp;'.$display_part.'&nbsp;';
       $displaySub[0].='<font color="#999999">(ID&nbsp;'.
    $responseId.')</font>&nbsp;<b>';
       if ($$record{"$version:resource.$partid.tries"} eq '') {
    $displaySub[0].='Trial&nbsp;not&nbsp;counted';
       } else {
    $displaySub[0].='Trial&nbsp;'.
       $$record{"$version:resource.$partid.tries"};
       }
       my $responseType=$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:").'<br />';
    }
       }
       if (exists $$record{"$version:resource.$partid.award"}) {
    $displaySub[1].='<b>Part:</b>&nbsp;'.$display_part.' &nbsp;'.
       lc($$record{"$version:resource.$partid.award"}).' '.
       $mark{$$record{"$version:resource.$partid.solved"}}.
       '<br />';
       }
       if (exists $$record{"$version:resource.$partid.regrader"}) {
    $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>';
       return $studentTable;
   }
   
 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 (undef,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 $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';      if (!&canmodify($usec)) {
     $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.   $request->print('<font color="red">Unable to modify requested student.('.$env{'form.student'}.'</font>');
  '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";   $request->print(&show_grading_menu_form($env{'form.symb'},$env{'form.url'}));
    return;
       }
       my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';
       $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).
    '</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,$url)=&get_symb_and_url($request);
    $request->print(&show_grading_menu_form($symb,$url));
    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,$ctr,$question,$changeflag)= (1,0,1,0);      my ($depth,$question,$prob,$changeflag)= (1,1,1,0);
     while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop      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) {
     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,undef,
  $displayPts[0].='&nbsp;<b>Part</b> '.$partid.' = '.     $curRes->symb());
    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();
  $ctr++;  
     }      }
   
     $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'},$env{'form.url'});
     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 2895  sub defaultFormData { Line 4068  sub defaultFormData {
     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="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 2916  sub getSequenceDropDown { Line 4089  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 2933  sub scantron_uploads { Line 4118  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 2943  sub scantron_scantab { Line 4129  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,$url)=&get_symb_and_url($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,$url);
     my $grading_menu_button=&show_grading_menu_form($symb,$url);      my $grading_menu_button=&show_grading_menu_form($symb,$url);
     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>
     </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_and_url($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 3013  sub get_scantron_config { Line 4319  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 3028  sub username_to_idmap { Line 4340  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 3050  sub scantron_parse_scanline { Line 4450  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 3067  sub scantron_parse_scanline { Line 4507  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) {                          # randomout is dysfunctional at best for this purpose
       if (ref($curres) && $curres->is_problem()) { #&& !$curres->randomout) {
  return 1;   return 1;
     }      }
     return 0;      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;
    }
       }
       &Apache::lonnet::logthis('remembering '.join(':',%to_remember));
       &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,$url)=&get_symb_and_url($r);
       if (!$symb) {return '';}
       my $default_form_data=&defaultFormData($symb,$url);
       $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,$url)."</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'}" />
   SCANTRONFORM
       return $result;
   }
   
   sub scantron_validate_file {
       my ($r) = @_;
       my ($symb,$url)=&get_symb_and_url($r);
       if (!$symb) {return '';}
       my $default_form_data=&defaultFormData($symb,$url);
       
       # 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($r);
       my $result=&scantron_form_start($max_bubble).$default_form_data;
       $r->print($result);
       
       my @validate_phases=( '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) {
    $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,$url).
         "</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_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 ( defined($$scan_record{'scantron.PaperID'}) ) {
    $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'});
    $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 {
       my ($r)=@_;
       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::lonnet::delenv('form.counter');
       foreach my $resource (@resources) {
    my $result=&Apache::lonnet::ssi($resource->src().'?symb='.&Apache::lonnet::escape($resource->symb()));
       }
       &Apache::lonnet::delenv('scantron\.');
       my $envfile=$env{'user.environment'};
       $envfile=~/\/([^\/]+)\.id$/;
       $envfile=$1;
       &Apache::lonnet::transfer_profile_to_env($r->dir_config('lonIDsDir'),
        $envfile);
       $env{'form.scantron_maxbubble'}=$env{'form.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,$url)=&get_symb_and_url($r);
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $default_form_data=&defaultFormData($symb,$url);      my $default_form_data=&defaultFormData($symb,$url);
   
     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 3111  SCANTRONFORM Line 5327  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();
  chomp($line);      my $i=-1;
  my $scan_record=&scantron_parse_scanline($line,\%scantron_config);      my ($uname,$udom,$started);
  my ($uname,$udom);      while ($i<$scanlines->{'count'}) {
  if ($uname=&scantron_find_student($scan_record,\%idmap)) {    ($uname,$udom)=('','');
     &scantron_add_delay(\@delayqueue,$line,    $i++;
  'Unable to find a student that matches');    my $line=&scantron_get_line($scanlines,$scan_data,$i);
  }    if ($line=~/^[\s\cz]*$/) { next; }
  $r->print('<pre>doing studnet'.$uname.'</pre>');   if ($started) {
  ($uname,$udom)=split(/:/,$uname);      &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
  &Apache::lonnet::delenv('form.counter');       'last student');
  &Apache::lonnet::appenv(%$scan_record);   }
 #    &Apache::lonhomework::showhash(%ENV);   $started=1;
     $Apache::lonxml::debug=1;    my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
  &Apache::lonxml::debug("line is $line");    $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);
     &Apache::lonnet::delenv('form.counter');
     &Apache::lonnet::appenv(%$scan_record);
   
     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; }
    }
    $completedstudents{$uname}={'line'=>$line};
    if (&Apache::loncommon::connection_aborted($r)) { last; }
       } continue {
  &Apache::lonnet::delenv('form.counter');   &Apache::lonnet::delenv('form.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,$url));
       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_and_url($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,$url)=&get_symb_and_url($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,$url));
    } 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())) {
    &Apache::lonnet::logthis("$requested_file  $filename");
    if ($requested_file eq $filename) { return 1; }
       }
       return 0;
   }
   
   sub scantron_download_scantron_data {
       my ($r)=@_;
       my $default_form_data=&defaultFormData(&get_symb_and_url($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_and_url($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_and_url($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,$url)=@_;
     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="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 3216  sub show_grading_menu_form { Line 5560  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 3234  sub gradingmenu { Line 5578  sub gradingmenu {
   
     $request->print(<<GRADINGMENUJS);      $request->print(<<GRADINGMENUJS);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
     function checkChoice(formname) {      function checkChoice(formname,val,cmdx) {
  var cmd = formname.command;   if (val <= 2) {
  formname.saveState.value = "saveCmd="+radioSelection(cmd)+":saveSec="+pullDownSelection(formname.section)+      var cmd = radioSelection(formname.radioChoice);
     ":saveSub="+radioSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.status);      var cmdsave = cmd;
  if (cmd[0].checked || cmd[1].checked || cmd[2].checked || cmd[3].checked || cmd[4].checked) formname.submit();   } else {
  if (cmd[5].checked) {      cmd = cmdx;
       cmdsave = 'submission';
    }
    formname.command.value = cmd;
    formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+
       ":saveSub="+pullDownSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status);
    if (val < 5) formname.submit();
    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 3256  sub gradingmenu { Line 5608  sub gradingmenu {
     formname.receipt.focus();      formname.receipt.focus();
     return false;      return false;
  }   }
  formname.command[5].checked = true;  
  return true;   return true;
     }      }
   
     function radioSelection(radioButton) {  
  var selection=null;  
  if (radioButton.length > 1) {  
     for (var i=0; i<radioButton.length; i++) {  
  if (radioButton[i].checked) {  
     return radioButton[i].value;  
  }  
     }  
  } else {  
     if (radioButton.checked) return radioButton.value;  
  }  
  return selection;  
     }  
   
     function pullDownSelection(selectOne) {  
  var selection="";  
  if (selectOne.length > 1) {  
     for (var i=0; i<selectOne.length; i++) {  
  if (selectOne[i].selected) {  
     return selectOne[i].value;  
  }  
     }  
  } else {  
     if (selectOne.selected) return selectOne.value;  
  }  
     }  
   
 </script>  </script>
 GRADINGMENUJS  GRADINGMENUJS
       &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>';
  '<table border="0">'.      my ($table,undef,$hdgrade) = &showResourceInfo($url,$probTitle);
  '<tr><td colspan=3><font size=+1><b>Problem: </b>'.$probTitle.'</font></td></tr>'."\n";      $result.=$table;
     my ($partlist,$handgrade) = &response_type($url);  
     my ($resptype,$hdgrade)=('','no');  
     for (sort keys(%$handgrade)) {  
  my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});  
  $resptype = $responsetype;  
  $hdgrade = $handgrade if ($handgrade eq 'yes');  
  $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.  
     '<td><b>Type: </b>'.$responsetype.'</td>'.  
     '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';  
     }  
     $result.='</table>'."\n";  
   
     my (undef,$sections) = &getclasslist('all','0');      my (undef,$sections) = &getclasslist('all','0');
     my $savedState = &savedState();      my $savedState = &savedState();
     my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'pickStudentPage' : $$savedState{'saveCmd'});      my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'});
     my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'});      my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'});
     my $saveSub = ($$savedState{'saveSub'} eq '' ? 'yes' : $$savedState{'saveSub'});      my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'});
     my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});      my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
  '<input type="hidden" name="symb"        value="'.$symb.'" />'."\n".   '<input type="hidden" name="symb"        value="'.$symb.'" />'."\n".
  '<input type="hidden" name="url"         value="'.$url.'" />'."\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="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>'."\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><td>'."\n";   '<tr bgcolor="#ffffe6" valign="top"><td>'."\n";
   
     $result.='<table width=100% border=0>'.      $result.='<table width="100%" border=0>';
  '<tr bgcolor="#ffffe6" valign="top"><td colspan="2">'.      $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n".
  '<input type="radio" name="command" value="pickStudentPage" '.   '&nbsp;'.&mt('Select Section').': <select name="section">'."\n";
  ($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.      if (ref($sections)) {
  'Handgrade/View Submission for a student by page/sequence</td></tr>'."\n".   foreach (sort (@$sections)) {
       $result.='<option value="'.$_.'" '.
    ($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n";
    }
       }
       $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</option></select> &nbsp; ';
   
       $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
   
  '<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.      $result.='</td></tr>';
  '<input type="radio" name="command" value="viewgrades" '.  
       $result.='<tr bgcolor="#ffffe6"valign="top"><td><label>'.
    '<input type="radio" name="radioChoice" value="submission" '.
    ($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').
    '</label> <select name="submitonly">'.
    '<option value="yes" '.
    ($saveSub eq 'yes' ? 'selected="on"' : '').'>'.&mt('with submissions').'</option>'.
    '<option value="queued" '.
    ($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>'.
    '<label><input type="radio" name="radioChoice" value="viewgrades" '.
  ($saveCmd eq 'viewgrades' ? 'checked' : '').'> '.   ($saveCmd eq 'viewgrades' ? 'checked' : '').'> '.
  'Grade by section or class</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>'.
    '<label><input type="radio" name="radioChoice" value="pickStudentPage" '.
    ($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.
    'The <b>complete</b> set/page/sequence: For one student</label></td></tr>'."\n";
   
       $result.='<tr bgcolor="#ffffe6"><td><br />'.
    '<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="Next->" />'.
    '</td></tr></table>'."\n";
   
  '<tr bgcolor="#ffffe6"valign="top"><td><input type="radio" name="command" value="submission" '.      $result.='</td><td valign="top">';
  ($saveCmd eq 'submission' ? 'checked' : '').'> '.  
  ($hdgrade eq 'yes' ? 'View/Grade essay response of' : 'View').      $result.='<table width="100%" border=0>';
  ' an individual student </td>'."\n".      $result.='<tr bgcolor="#ffffe6"><td>'.
  '<td>-->&nbsp;For students who has: '.   '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.
  '<input type="radio" name="submitonly" value="yes" '.   ' '.&mt('scores from file').' </td></tr>'."\n";
  ($saveSub eq 'yes' ? 'checked' : '').' /> submitted'.  
  '<input type="radio" name="submitonly" value="all" '.  
  ($saveSub eq 'all' ? 'checked' : '').' /> everybody</td></tr>'."\n".  
   
  '<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.  
  '<input type="radio" name="command" value="csvform" '.  
  ($saveCmd eq 'csvform' ? 'checked' : '').'> '.  
  'Upload 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="radio" name="command" value="scantron_selectphase" '.   '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.
  ($saveCmd eq 'scantron_selectphase' ? 'checked="on"' : '').' /> '.   '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";
         '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="radio" name="command" value="verify" onChecked="javascript:this.form.receipt.focus()" '.      '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="'.&mt('Verify').'" />'.
     ($saveCmd eq 'verify' ? 'checked' : '').'> '.      ' '.&mt('receipt').': '.
     'Verify a submission receipt issued by this server</td>'.      &Apache::lonnet::recprefix($env{'request.course.id'}).
     '<td>-->&nbsp;Receipt no: '.unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).  
     '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'.      '-<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.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2"><br />'."\n".      $result.='</form></td></tr></table>'."\n".
  '&nbsp;Select section: <select name="section">'."\n";   '</td></tr></table>'."\n".
     if (ref($sections)) {   '</td></tr></table>'."\n";
  foreach (sort (@$sections)) {$result.='<option value="'.$_.'" '.      return $result;
  ($saveSec eq $_ ? 'selected="on"' : '').'>'.$_.'</option>'."\n";}  }
     }  
     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> &nbsp; ';  
   
     $result.='Student Status:</b><select name="status">'.  sub reset_perm {
  '<option value="Active" '.($saveStatus eq 'Active' ? 'selected' : '').'>Active</option>'.      undef(%perm);
  '<option value="Expired" '.($saveStatus eq 'Expired' ? 'selected' : '').'>Expired</option>'.  }
  '<option value="Any" '.($saveStatus eq 'Any' ? 'selected' : '').'>Any</option>'.  
  '</select>';  
   
     $result.=' &nbsp; <font color="red">(Applies to the first three options only.)</font>'."\n";  sub init_perm {
       &reset_perm();
       foreach my $test_perm ('vgr','mgr','opa') {
   
     if (ref($sections)) {   my $scope = $env{'request.course.id'};
  $result.='&nbsp;(Section "no" implies the students were not assigned a section.)<br />'    if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {
     if (grep /no/,@$sections);  
     }  
     $result.='</td></tr>';  
   
     $result.='<tr bgcolor="#ffffe6"><td colspan="2"><br />'.      $scope .= '/'.$env{'request.course.sec'};
  '<input type="button" onClick="javascript:checkChoice(this.form);" value="View/Grade" />'."\n".      if ( $perm{$test_perm}=
  '</form></td></tr></table>'."\n".   &Apache::lonnet::allowed($test_perm,$scope)) {
  '</td></tr></table>'."\n".   $perm{$test_perm.'_section'}=$env{'request.course.sec'};
  '</td></tr></table>'."\n";      } else {
     return $result;   delete($perm{$test_perm});
       }
    }
       }
 }  }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
       
     if ($ENV{'browser.mathml'}) {      &reset_perm();
  $request->content_type('text/xml');      if ($env{'browser.mathml'}) {
    &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 $url=$env{'form.url'};
     my $symb=$ENV{'form.symb'};      my $symb=$env{'form.symb'};
     my $command=$ENV{'form.command'};      my @commands=&Apache::loncommon::get_env_multiple('form.command');
       my $command=$commands[0];
       if ($#commands > 0) {
    &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
       }
     if (!$url) {      if (!$url) {
  my ($temp1,$temp2);   my ($temp1,$temp2);
  ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);   ($temp1,$temp2,$env{'form.url'})=&Apache::lonnet::decode_symb($symb);
  $url = $ENV{'form.url'};   $url = $env{'form.url'};
     }      }
     &send_header($request);      &send_header($request);
     if ($url eq '' && $symb eq '') {      if ($url eq '' && $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(   $request->print(&Apache::lonnet::ssi_body('/res/'.$url,
  &Apache::lonnet::ssi('/res/'.$url,    ('grade_username' => $tuname,
      ('grade_username' => $tuname,     'grade_domain' => $tudom,
       'grade_domain' => $tudom,     'grade_courseid' => $tcrsid,
       'grade_courseid' => $tcrsid,     'grade_symb' => $tsymb)));
       'grade_symb' => $tsymb)));  
     } else {      } else {
  $request->print('<h3>Not authorized: '.$token.'</h3>');   $request->print('<h3>Not authorized: '.$token.'</h3>');
     }                 }
  } else {   } else {
     $request->print('<h3>Not a valid DocID: '.$token.'</h3>');      $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
  }   }
Line 3445  sub handler { Line 5788  sub handler {
     }      }
  }   }
     } else {      } else {
  $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});   &init_perm();
  if ($command eq 'submission') {   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') {   } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
     &pickStudentPage($request);      &pickStudentPage($request);
  } elsif ($command eq 'displayPage') {   } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
     &displayPage($request);      &displayPage($request);
  } elsif ($command eq 'gradeByPage') {   } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
     &updateGradeByPage($request);      &updateGradeByPage($request);
  } elsif ($command eq 'processGroup') {   } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
     &processGroup($request);      &processGroup($request);
  } elsif ($command eq 'gradingmenu') {   } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
     $request->print(&gradingmenu($request));      $request->print(&gradingmenu($request));
  } elsif ($command eq 'viewgrades') {   } elsif ($command eq 'viewgrades' && $perm{'vgr'}) {
     $request->print(&viewgrades($request));      $request->print(&viewgrades($request));
  } elsif ($command eq 'handgrade') {   } elsif ($command eq 'handgrade' && $perm{'mgr'}) {
     $request->print(&processHandGrade($request));      $request->print(&processHandGrade($request));
  } elsif ($command eq 'editgrades') {   } elsif ($command eq 'editgrades' && $perm{'mgr'}) {
     $request->print(&editgrades($request));      $request->print(&editgrades($request));
  } elsif ($command eq 'verify') {   } elsif ($command eq 'verify' && $perm{'vgr'}) {
     $request->print(&verifyreceipt($request));      $request->print(&verifyreceipt($request));
  } elsif ($command eq 'csvform') {   } elsif ($command eq 'csvform' && $perm{'mgr'}) {
     $request->print(&upcsvScores_form($request));      $request->print(&upcsvScores_form($request));
  } elsif ($command eq 'csvupload') {   } elsif ($command eq 'csvupload' && $perm{'mgr'}) {
     $request->print(&csvupload($request));      $request->print(&csvupload($request));
  } elsif ($command eq 'viewclasslist') {   } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
     $request->print(&viewclasslist($request));  
  } elsif ($command eq 'csvuploadmap') {  
     $request->print(&csvuploadmap($request));      $request->print(&csvuploadmap($request));
  } elsif ($command eq 'csvuploadassign') {   } 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 'scantron_selectphase') {   } elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
       $request->print(&csvuploadassign($request));
    } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
     $request->print(&scantron_selectphase($request));      $request->print(&scantron_selectphase($request));
  } elsif ($command eq 'scantron_process') {    } 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'}) {
     $request->print(&scantron_process_students($request));      $request->print(&scantron_process_students($request));
  } else {    } elsif ($command eq 'scantronupload' && 
     $request->print("Unknown action: $command:");    (&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) {
       $request->print("Access Denied ($command)");
  }   }
     }      }
     &send_footer($request);      &send_footer($request);
Line 3506  sub send_header { Line 5864  sub send_header {
 #remotewindow.close();  #remotewindow.close();
 #</script>");   #</script>"); 
     $request->print(&Apache::loncommon::bodytag('Grading'));      $request->print(&Apache::loncommon::bodytag('Grading'));
       $request->rflush();
 }  }
   
 sub send_footer {  sub send_footer {
     my ($request)= @_;      my ($request)= @_;
     $request->print('</body>');      $request->print('</body></html>');
     $request->print(&Apache::lontexconvert::footer());  
 }  }
   
 1;  1;

Removed from v.1.98  
changed lines
  Added in v.1.322


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